前言
衔接上文,我打算在灵动微 MM32F5277这块MCU的基础上实现boot分区的功能,在此之前需要移植一个串口的第三方shell交互组件 - nr_micro_shell。
shell组件介绍及移植
简单介绍一下 nr_micro_shell这个组件:
git链接为:nr_micro_shell: shell for MCU. 单片机命令行交互。
以下为该组件的目录结构:
我们在工程源码路径创建一个component文件夹,并将下载来的nr_micro_shell组件完整的复制进去,如下图:
回到MDK软件中,包含该文件夹的路径至编译器中,且在Manage工具中添加
ansi.c\ansi_port.c\nr_micro_shell.c\nr_micro_shell_commands.c文件到工程中:
到了这一步,我们移植nr_micro_shell组件的第一步就完成了,下一步就是实现底层串口功能与组件的接口对接。
串口功能实现
实现底层串口功能与组件的接口对接之前,我们得先初始化板载的串口功能,具体流程为:使能对应的外设时钟、配置对应的串口管脚及功能、配置串口功能、中断。
我们这次使用到的是串口2,引脚为A2 - UART2_TX\A3 - UART2_RX,根据手册UART2在A2/A3引脚的复用为AF7,所以我们的配置流程为:
void Uart2_init(void)
{
GPIO_Init_Type gpio_init;
UART_Init_Type uart_init;
/* UART2. */
RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART2, true);
RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_UART2);
/* PA2 - UART2_TX. */
gpio_init.Pins = GPIO_PIN_2;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_7);
/* PA3 - UART2_RX. */
gpio_init.Pins = GPIO_PIN_3;
gpio_init.PinMode = GPIO_PinMode_In_Floating;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_7);
uart_init.ClockFreqHz = UART2;
uart_init.BaudRate = 115200;
uart_init.WordLength = UART_WordLength_8b;
uart_init.StopBits = UART_StopBits_1;
uart_init.Parity = UART_Parity_None;
uart_init.XferMode = UART_XferMode_RxTx;
uart_init.HwFlowControl = UART_HwFlowControl_None;
uart_init.XferSignal = UART_XferSignal_Normal;
uart_init.EnableSwapTxRxXferSignal = false;
UART_Init(UART2, &uart_init);
/* Enable RX interrupt. */
UART_EnableInterrupts(UART2, UART_INT_RX_DONE, true);
NVIC_SetPriority(UART2_IRQn, 0x10);
NVIC_EnableIRQ(UART2_IRQn);
/* Enable UART. */
UART_Enable(UART2, true);
}
重定义fputc/fgetc
灵动微的例程里边已经帮我们重定义好了,只要把对应的UART端口映射对就行,具体代码如下:
int fputc(int c, FILE *f)
{
(void)(f);
UART_PutData(UART2, (uint8_t)(c));
while (0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(UART2)))
;
return c;
}
int fgetc(FILE *f)
{
(void)(f);
while (0u == (UART_STATUS_RX_DONE & UART_GetStatus(UART2)))
;
return UART_GetData(UART2);
}
对接nr_micro_shell组件
打开nr_micro_shell_config.h,进行以下多项配置:
1.添加NR_MICRO_SHELL_SIMULATOR宏定义,或是直接删除44、45、46、47行代码;
2.修改宏定义NR_SHELL_END_OF_LINE的值为1,这个值配置的是shell终端的结尾为空格结尾有效;
3.添加宏定义 NR_SHELL_USING_EXPORT_CMD,这个宏定义可以方便我们注册自己的命令;
4.由于我们是在裸机上使用该组件,所以shell_printf()这个函数不需要特殊处理,直接定义为printf函数使用即可;
根据nr_micro_shell作者的说明,该shell组件在裸机中,可以通过轮询或者是串口中断的方式去配置使用,我们在上文串口初始化中,初始化了串口2的接收中断,所以我们选择使用串口中断的方式,在MM32F5270_it.c中实现如下代码:
/* UART2_IRQHandler ISR entry. */
void UART2_IRQHandler(void)
{
uint8_t c;
if ((0u != (UART_INT_RX_DONE & UART_GetEnabledInterrupts(UART2))) && (0u != (UART_INT_RX_DONE & UART_GetInterruptStatus(UART2))))
{
c = UART_GetData(UART2); /* read data to clear rx interrupt bits. */
shell(c);
}
}
当串口2有数据输入时,会触发串口2的接收中断,此时我们接收串口的数据并传入到shell组件的处理函数shell(c)中,到这一步,nr_micro_shell组件算是移植完毕,我们把串口接到电脑,打开SecureCRT试试成果!
nr_micro_shell组件部分bug修复
打开ansi_port.c文件,找到void nr_ansi_common_char_slover(ansi_st *ansi,char x)函数,修改图中红框部分;
该组件仍有部分bug和问题,例如不支持ESC键;使用方向键选择历史命令会覆盖掉 ":" 等问题,但是瑕不掩瑜!这个组件的体量大小及实现效果都是非常适合MCU这种资源紧缺的设备。
SecureCRT检验移植成果
看到如下打印,证明移植成功!
输入 ls cmd会打印当前注册的命令;
输入TAB键也会打印当前注册的命令;
输入 test + 值,会打印对应位置的输入值;
TAB键可以补全命令;
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Kiouker/article/details/139415333
|