ARM裸板开发——简单编写实现“shell”功能

谁说胖子不能爱 提交于 2020-01-21 12:44:56


简单编写实现一个裸板环境下使用的“shell”功能程序,可以控制LED、beep等。

main主程序设计

#include "uart.h"
#include "strcmp.h"
#include "led.h"

//保存从上位机接收的数据信息
static char buf[32];

void main(void)
{
    //1.初始化UART
    uart_init();
    //2.初始化LED
    led_init();

    //3.根据用户需求完成业务
    while(1) {
        uart_puts("\n Shell#");
        uart_gets(buf, 32);
        if(!my_strcmp(buf, "led on"))
            led_on();
        else if(!my_strcmp(buf, "led off"))
            led_off();
        else
            uart_puts("\n Your command is invalid\n");
    }
}

主要函数:strcmp实现

实现strcmp字符串比较函数,用于对比用户命令的输入。

  • strcmp.h
#ifndef __STRCMP_H
#define __STRCMP_H

/*功能:比较字符串函数声明
 *返回值:
    str1=str2:返回0
    str1>str2:返回大于0
    str1<str2:返回小于0
  参数:
    str1:要比较的字符串
    str2:要比较的字符串
 * */
extern int my_strcmp(const char *str1,
                     const char *str2);
#endif

  • strcmp.c
#include “strcmp.h”
/*str1 = "hello", str2 = "hfllo"*/
int my_strcmp(const char *str1, 
                const char *str2)
{
    while(*str1) {
        if(*str1 != *str2)
            return *str1 - *str2;
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

LED初始化及控制功能实现

  • led.h
#ifndef __LED_H
#define __LED_H

/*声明寄存器的基地址信息*/
#define GPIOCOUT (*(volatile unsigned long *)0xC001C000)
#define GPIOCOUTENB (*(volatile unsigned long *)0xC001C004)
#define GPIOCALTFN0 (*(volatile unsigned long *)0xC001C020)

/*声明操作函数*/
extern void led_init(void);
extern void led_on(void);
extern void led_off(void);
#endif

  • led.c
#include "led.h"

//初始化函数定义
void led_init(void)
{
    //1.配置引脚功能为GPIO功能
    GPIOCALTFN0 &= ~(3 << 24);
    GPIOCALTFN0 |= (1 << 24);

    //2.配置引脚为输出功能
    GPIOCOUTENB |= (1 << 12);
}

//开灯函数定义
void led_on(void)
{
    //1.配置输出寄存器为0
    GPIOCOUT &= ~(1 << 12);
}

//关灯函数定义
void led_off(void)
{
    //1.配置输出寄存器为1
    GPIOCOUT |= (1 << 12);
}

UART初始化及控制功能实现

  • uart.h
#ifndef __UART_H
#define __UART_H

/*UART相关寄存器定义*/
#define ULCON0 (*(volatile unsigned long *)0xC00A1000)
#define UCON0 (*(volatile unsigned long *)0xC00A1004)
#define UTRSTAT0 (*(volatile unsigned long *)0xC00A1010)
#define UTXH0 (*(volatile unsigned long *)0xC00A1020)
#define URXH0 (*(volatile unsigned long *)0xC00A1024)
#define UBRDIV0 (*(volatile unsigned long *)0xC00A1028)
#define UFRACVAL0 (*(volatile unsigned long *)0xC00A102C)
#define GPIODALTFN0 (*(volatile unsigned long *)0xC001D020)
#define GPIODALTFN1 (*(volatile unsigned long *)0xC001D024)
#define UARTCLKENB (*(volatile unsigned long *)0xC00A9000)
#define UARTCLKGENOL (*(volatile unsigned long *)0xC00A9004)

/*UART操作函数声明*/
//初始化函数
extern void uart_init(void);
//发送字符函数
extern void uart_putc(char c);
//发送字符串函数
extern void uart_puts(char *str);
//接收字符函数
extern char uart_getc(void);
//接收字符串函数
extern void uart_gets(char buf[], int len);
#endif

  • uart.c
#include "uart.h"

//初始化函数的定义
void uart_init(void)
{
    //1.配置RX和TX对应的引脚功能分别为UARTRXD0和UARTTXD0功能
    //GPIOD14配置UARTRXD0
    //GPIODALTFN0[29:28]=01
    GPIODALTFN0 &= ~(3 << 28);
    GPIODALTFN0 |= (1 << 28);
    //GPIOD18配置UARTTXD0
    //GPIODALTFN1[5:4]=01
    GPIODALTFN1 &= ~(3 << 4);
    GPIODALTFN1 |= (1 << 4);

    //2.配置UART的时钟为50MHZ
    //此时钟给波特率产生器使用
    //PLL[0]=800MHZ
    //n=800MHZ/50MHZ=16
    //n=CLKDIV0+1=>CLKDIV0=15
    //CLKDIV0=UARTCLKGENOL[12:5]
    UARTCLKGENOL &= ~(0xFF << 5);
    UARTCLKGENOL |= (0xF << 5);

    //3.配置UART的波特率为115200
    //数据位为8
    //不采用奇偶校验
    //停止为为1位
    ULCON0 = 3;
    UCON0 = 5;
    UBRDIV0 = 26; 
    UFRACVAL0=2;

    //4.打开UART的时钟
    UARTCLKENB |= (1 << 2);
}

//发送字符函数定义
//以软件形式将数据放到发送缓冲区
//发送移位器自动将数据一位一位的
//发送到TX数据线,速度就是115200
void uart_putc(char c)
{
    //1.由于CPU把数据放到缓存区的速度要远远快于发送移位器把数据放到TX数据线上的速度,CPU再发送下一个字符的时候应该判断一下发送缓冲区是否为空,如果为空,可以发,否则继续死等待
    while(!(UTRSTAT0 & 0x2));

    //2.将数据放到发送缓冲区
    UTXH0 = c;
    
    //3.发送回车字符
    if(c == '\n')
        uart_putc('\r');
}

//发送字符串函数定义
//str = "helloworld\n"
void uart_puts(char *str)
{
    while(*str) {
        uart_putc(*str);
        str++;
    }
}

//获取字符函数定义
char uart_getc(void)
{
    //1.由于CPU读取UART接收缓冲区数据的速度
    //要远远快于接收移位器从RX数据线获取数据的数据,所以CPU在读取数据的时候,首先要判断
    //接收移位器的速度由波特率产生器决定
    //接收缓冲区是否为空
    //如果为空:CPU在原地死等
    //如果不为空:CPU就可以读取数据
    while(!(UTRSTAT0 & 0x1));

    //2.从接收缓冲区寄存器读取数据
    return (URXH0 & 0xFF);
}

//获取字符串函数定义
//调用:char buf[32];uart_gets(buf, 32)
void uart_gets(char buf[], int len)
{
    int i;
    for (i = 0; i < len - 1; i++) {
        //保存上位机发送的字符
        buf[i] = uart_getc();
        //上位机回显
        uart_putc(buf[i]);
        //判断输入期间是否有回车操作
        //如果有,跳出循环
        if(buf[i] == '\r')
            break;
    }
    buf[i] = 0; //最后添加结束符
}

使用Makefile编译

  • Makefile
#定义变量
NAME=shell
BIN=$(NAME).bin
ELF=$(NAME).elf 
OBJ=main.o uart.o led.o strcmp.o

CROSS_COMPILE=arm-cortex_a9-linux-gnueabi-
CC=$(CROSS_COMPILE)gcc
LD=$(CROSS_COMPILE)ld
OBJCOPY=$(CROSS_COMPILE)objcopy
CP=cp
RM=rm
INSTALLPATH=/tftpboot
  
  #链接选项
LDFLAGS=-nostdlib -nostartfiles -Ttext=0x48000000 -emain
  #编译选项
CFLAGS=-nostdlib

#定义编译规则
shell.bin:shell.elf
	arm-cortex_a9-linux-gnueabi-objcopy -O binary shell.elf shell.bin

$(BIN):$(ELF)
	$(OBJCOPY) -O binary $(ELF) $(BIN)
	$(CP) $(BIN) $(INSTALLPATH)
#
#   		#shell.elf:main.o uart.o led.o strcmp.o
#     arm...ld -nostartfiles -nostdlib -... -o shell.elf main.o ...
$(ELF):$(OBJ)
	$(LD) $(LDFLAGS) -o $(ELF) $(OBJ)
#各种.o:各种.c
#  各种编译
%.o:%.c
	$(CC) $(CFLAGS) -c -o $@ $<
#  	 
#  	 #伪目标
#当执行make clean时,仅仅执行clean伪目标对应的命令
clean:
		$(RM) $(BIN) $(ELF) $(OBJ)   

执行结果

在板子上uboot界面执行:

tftp 48000000 shell.bin
go 48000000
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!