[MM32软件] shell调试教程之如何在MM32 MCU上使用shell来辅助开发

[复制链接]
 楼主| 黑心单片机 发表于 2022-4-29 10:47 | 显示全部楼层 |阅读模式

对于做linux开发的研发人员来说,大家都喜欢通过输入指令符来执行一些命令操作,如果在MCU编程过程中有一个类似linux的shell命令工具可以通过串口调试助手输入命令然后运行一些调试函数,将会为编程提供极大的帮助。

对于MCU来说,每次修改程序调试就需要重新下载,有时候仅仅是调试修改几个参数,这样反复修改程序编译下载就显得很繁琐,浪费时间,而且在调试电机电源等高压电源类应用时,如果出现操作错误,有可能会造成炸机毁坏电脑等危险,如果有一个类似linux的命令行操作,这样就可以省出很多时间,但是由于MCU存储资源和运算速度等限制,所以想实现一个同样功能的操作,就需要做一些处理优化。


 楼主| 黑心单片机 发表于 2022-4-29 10:49 | 显示全部楼层
目前有很多类似的shell代码,本次介绍如何在MM32 MCU上使用shell来辅助开发,由于篇幅过长,所以将分三个章节讲述实现方式,本章节将教大家基础的软件配置,后面章节将带领大家学习如何配置Jlink RTT和shell的程序配置。硬件资源如下:本次实验将在基于MM32L073的MiniBoard上进行测试验证,实现shell的通信端口可以使用任意通信方式,如UARTUSB、SPI、IIC、485等方式,本次使用MM32L073PF的UART1作为shell输入输出通道,

如图2。PA15作为状态指示 LED1的控制引脚,如图2。
97370626b51efc37c9.png
图1 串口硬件原理图
68852626b5201a636a.png
图2  LED硬件原理图
 楼主| 黑心单片机 发表于 2022-4-29 10:51 | 显示全部楼层

软件资源如下:结合上述使用到的硬件资源,下面我们着重介绍软件实现流程以及相关配置代码,主要涉及如何移植shell的输入输出以及如何执行命令。

以下为主函数初始化配置及相关全局变量定义内容,代码如下:
  1. void LED_Init(void)
  2. {
  3. GPIO_InitTypeDef  GPIO_InitStructure;
  4. RCC_AHBPeriphclockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //开启GPIOA时钟
  5. GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_15;
  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  8. GPIO_Init(GPIOA, &GPIO_InitStructure);
  9. LED1_OFF();
  10. }
  11. void uart_nvic_init(u32 bound)
  12. {
  13. //>>>GPIO端口设置>>>
  14. GPIO_InitTypeDef GPIO_InitStructure;
  15. UART_InitTypeDef UART_InitStructure;
  16. NVIC_InitTypeDef NVIC_InitStructure;
  17. //>>>使能外设时钟>>>
  18. RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE); //使能UART1
  19. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);  //开启GPIOA时钟
  20. //>>>UART1 NVIC 配置>>>
  21. NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
  22. NVIC_InitStructure.NVIC_IRQChannelPriority = 3;  //子优先级3
  23. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
  24. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
  25. //>>>UART的GPIO复用功能配置>>>
  26. GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
  27. GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
  28. //>>>UART 初始化设置>>>
  29. UART_InitStructure.UART_BaudRate = bound;//串口波特率
  30. UART_InitStructure.UART_WordLength = UART_WordLength_8b;//字长为8位数据格式
  31. UART_InitStructure.UART_StopBits = UART_StopBits_1;//一个停止位
  32. UART_InitStructure.UART_Parity = UART_Parity_No;//无奇偶校验位
  33. UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;//无硬件数据流控制
  34. UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; //收发模式
  35. UART_Init(UART1, &UART_InitStructure); //初始化串口1
  36. UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);//开启串口接受中断
  37. UART_Cmd(UART1, ENABLE);                    //使能串口1
  38. //>>>UART1_TX   GPIOA.9>>> UART1_RX    GPIOA.10>>>
  39. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  40. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  41. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  42. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
  43. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  44. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOAtiNG;//浮空输入
  45. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
  46. }
  47. //>>>UART发送一个字节 >>>
  48. void Uart_PutChar(char chByte)
  49. {
  50. while ((UART1->CSR & UART_IT_TXIEN) == 0);
  51. UART1->TDR = (chByte & (uint16_t)0x00FF);
  52. }
  53. //>>>UART 发送字符串>>>
  54. void Uart_PutBuff(uint8_t *pchBuff, uint32_t wLen)
  55. {
  56. while (wLen--) {
  57. Uart_PutChar(*pchBuff);
  58. pchBuff++;
  59. }
  60. }
  61. //>>>main主函数 >>>
  62. int main(void)
  63. {
  64. delay_init();
  65. LED_Init();
  66. uart_nvic_init(115200);  //串口初始化为115200
  67. //uart_shell.read = shellRead;
  68. uart_shell.write = Uart_PutChar;
  69. shellInit(&uart_shell);//shell初始化
  70. while (1)
  71. {
  72. }
  73. }



 楼主| 黑心单片机 发表于 2022-4-29 10:54 | 显示全部楼层
移植的步骤先定义一个shell对象,即:SHELL_TypeDef uart_shell;
然后实例化对象的操作接口,对于本次我们采用中断接收,所以不用调用读取接口函数,所以接收接口如下修改:
//uart_shell.read = shellRead; 注释掉读取接口,采用中断处理代码如下:
  1. void UART1_IRQHandler(void)                 //串口1中断服务程序
  2. {
  3. u8 Res;
  4. IF (UART_GeTITStatus(UART1, UART_IT_RXIEN)  != RESET){
  5. UART_ClearITPendingBit(UART1, UART_IT_RXIEN);
  6. Res = UART_ReceiveData(UART1);//读取接收到的数据
  7. shellHandler(&uart_shell, Res);//shell处理函数
  8. }
  9. }
然后实例化发送接口,代码如下:uart_shell.write = Uart_PutChar;
最后实例化对象,代码如下:shellInit(&uart_shell);
完成shell对象的全部实例化,那么我们如何加入我们需要的命令函数呢?很简单,有多种方式,本次我们介绍最简单的一个,即SHELL_EXPORT_CMD();其它参考源码,本次我们加入测试代码如下:
  1. void led1_on(void)
  2. {
  3. LED1_ON();
  4. }
  5. SHELL_EXPORT_CMD(led1_on, led1_on, led1_on);//三个变量:命令,功能,描述
  6. void led1_off(void)
  7. {
  8. LED1_OFF();
  9. }
  10. SHELL_EXPORT_CMD(led1_off, led1_off, led1_off);//同上
  11. void led1_toggle(void)
  12. {
  13. LED1_TOGGLE();
  14. }
  15. SHELL_EXPORT_CMD(led1_toggle, led1_toggle, led1_toggle);
  16. void reboot(void)
  17. {
  18. NVIC_SystemReset();
  19. }
  20. SHELL_EXPORT_CMD(reboot, reboot, reboot);


tpgf 发表于 2022-5-4 15:12 | 显示全部楼层
不擅长写shell文件啊
qcliu 发表于 2022-5-4 15:20 | 显示全部楼层
做什么的时候需要shell编制呢
drer 发表于 2022-5-4 15:35 | 显示全部楼层
新手适用这个吗
coshi 发表于 2022-5-4 15:54 | 显示全部楼层
一般如何进行实例化呢
kxsi 发表于 2022-5-4 16:10 | 显示全部楼层
都可以辅助什么功能呢
wiba 发表于 2022-5-4 16:20 | 显示全部楼层
能提高运行效率吗
您需要登录后才可以回帖 登录 | 注册

本版积分规则

72

主题

514

帖子

0

粉丝
快速回复 返回顶部 返回列表