基于STM32战舰开发板的USMART调试组件的使用

USMART调试组件是什么?

USMART是正点原子团队为其STM32开发平台开发的一种类似linux的shell的调试工具。具体工作过程是通过串口发送命令给单片机,然后单片机收到命令之后调用单片机里面对应的相关函数,并执行,同时支持返回结果。

USMART是干什么的?

USMART的功能就是改变了函数参数修改的方式,我们以前总是“修改函数参数->下载到开发板中执行”,但是使用USMART之后,我们不用在那样做了,而是“用串口进行函数参数的修改->开发板执行相应操作”。

在我看来,USMART就是一个函数调试助手,可以从串口发送参数也可以从串口接收函数返回值。

USMART的作用机理

这里我们要明白一下,函数的存储方式:

其中,函数体是一个数据处理的工具本身并不占用空间,只是在调用函数处理数据的时候在进行压栈出栈的一些逻辑操作。

USMART修改这些函数的参数值正是通过“访问函数参数的地址并且修改这些参数“实现的。

USMART调试功能的实现——定时器与串口的配合使用

注意我们这里的串口扫描函数的内容以及串口扫描函数的执行位置:

//usmart扫描函数
//通过调用该函数,实现usmart的各个控制.该函数需要每隔一定时间被调用一次
//以及时执行从串口发过来的各个函数.
//本函数可以在中断里面调用,从而实现自动管理.
//如果非ALIENTEK用户,则USMART_RX_STA和USMART_RX_BUF[]需要用户自己实现
void usmart_scan(void)
{  u8 sta,len;    if(USMART_RX_STA&0x8000)//串口接收完成?  {                        len=USMART_RX_STA&0x3fff;    //得到此次接收到的数据长度  USMART_RX_BUF[len]='\0'; //在末尾加入结束符.   sta=usmart_dev.cmd_rec(USMART_RX_BUF);//得到函数各个信息  if(sta==0)usmart_dev.exe(); //执行函数   else   {    len=usmart_sys_cmd_exe(USMART_RX_BUF);  if(len!=USMART_FUNCERR)sta=len;  if(sta)  {  switch(sta)  {  case USMART_FUNCERR:  printf("函数错误!\r\n");              break;    case USMART_PARMERR:  printf("参数错误!\r\n");              break;                case USMART_PARMOVER:  printf("参数太多!\r\n");              break;        case USMART_NOFUNCFIND:  printf("未找到匹配的函数!\r\n");              break;        }  }  }  USMART_RX_STA=0;//状态寄存器清空          }
}  

扫描函数的功能其实包含两部分:

① 扫描串口接收的数据;

② 根据扫描结果,执行相应的功能。

//下面这两个函数,非USMART函数,放到这里,仅仅方便移植.
//定时器4中断服务程序
void TIM4_IRQHandler(void)
{                                     if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)//溢出中断  {  usmart_dev.scan();  //执行usmart扫描      TIM_SetCounter(TIM4,0);     //清空定时器的CNT  TIM_SetAutoreload(TIM4,100);//恢复原来的设置                                                             }                    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //清除中断标志位
}  

在TIMER4的中断服务函数中我们可以看到,当TIM4溢出,即达到1ms时,我们扫描串口并根据接收的命令执行相应的动作。

USMART调试组件的执行流程

USMART使用过程中的疑难

有返回值的函数在执行后,会通过串口返回函数返回值吗?

  1. //usamrt执行函数
  2. //该函数用于最终执行从串口收到的有效函数.
  3. //最多支持10个参数的函数,更多的参数支持也很容易实现.不过用的很少.一般5个左右的参数的函数已经很少见了.
  4. //该函数会在串口打印执行情况.以:"函数名(参数1,参数2...参数N)=返回值".的形式打印.
  5. //当所执行的函数没有返回值的时候,所打印的返回值是一个无意义的数据.
  6. void usmart_exe(void)
  7. {
  8. u8 id,i;
  9. u32 res;
  10. u32 temp[MAX_PARM];//参数转换,使之支持了字符串
  11. u8 sfname[MAX_FNAME_LEN];//存放本地函数名
  12. u8 pnum,rval;
  13. id=usmart_dev.id;
  14. if(id>=usmart_dev.fnum)return;//不执行.
  15. usmart_get_fname((u8*)usmart_dev.funs[id].name,sfname,&pnum,&rval);//得到本地函数名,及参数个数
  16. printf("\r\n%s(",sfname);//输出正要执行的函数名
  17. for(i=0;i<pnum;i++)//输出参数
  18. {
  19. if(usmart_dev.parmtype&(1<<i))//参数是字符串
  20. {
  21. printf("%c",'"');
  22. printf("%s",usmart_dev.parm+usmart_get_parmpos(i));
  23. printf("%c",'"');
  24. temp[i]=(u32)&(usmart_dev.parm[usmart_get_parmpos(i)]);
  25. }else                         //参数是数字
  26. {
  27. temp[i]=*(u32*)(usmart_dev.parm+usmart_get_parmpos(i));
  28. if(usmart_dev.sptype==SP_TYPE_DEC)printf("%lu",temp[i]);//10进制参数显示
  29. else printf("0X%X",temp[i]);//16进制参数显示
  30. }
  31. if(i!=pnum-1)printf(",");
  32. }
  33. printf(")");
  34. usmart_reset_runtime(); //计时器清零,开始计时
  35. switch(usmart_dev.pnum)
  36. {
  37. case 0://无参数(void类型)
  38. res=(*(u32(*)())usmart_dev.funs[id].func)();
  39. break;
  40. case 1://有1个参数
  41. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0]);
  42. break;
  43. case 2://有2个参数
  44. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1]);
  45. break;
  46. case 3://有3个参数
  47. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2]);
  48. break;
  49. case 4://有4个参数
  50. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3]);
  51. break;
  52. case 5://有5个参数
  53. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4]);
  54. break;
  55. case 6://有6个参数
  56. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
  57. temp[5]);
  58. break;
  59. case 7://有7个参数
  60. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
  61. temp[5],temp[6]);
  62. break;
  63. case 8://有8个参数
  64. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
  65. temp[5],temp[6],temp[7]);
  66. break;
  67. case 9://有9个参数
  68. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
  69. temp[5],temp[6],temp[7],temp[8]);
  70. break;
  71. case 10://有10个参数
  72. res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
  73. temp[5],temp[6],temp[7],temp[8],temp[9]);
  74. break;
  75. }
  76. usmart_get_runtime();//获取函数执行时间
  77.     if(rval==1)//需要返回值.  
  78.     {  
  79.         if(usmart_dev.sptype==SP_TYPE_DEC)printf("=%lu;\r\n",res);//输出执行结果(10进制参数显示)  
  80.         else printf("=0X%X;\r\n",res);//输出执行结果(16进制参数显示)         
  81.     }else printf(";\r\n");      //不需要返回值,直接输出结束  
  82.     if(usmart_dev.runtimeflag)  //需要显示函数执行时间  
  83.     {   
  84.         printf("Function Run Time:%d.%1dms\r\n",usmart_dev.runtime/10,usmart_dev.runtime%10);//打印函数执行时间   
  85.     } 
  86. }

我们看到串口执行函数的最后几行,清晰的表明“如果函数需要返回返回值,就通过串口进行打印”。

我们调试函数的结果时,出现的结果会一闪而过?

这很正常,由于我们的串口扫描执行函数是在定时器中断服务函数中执行的,因此当我们执行完中断服务函数后,要回到原来执行的主程序的位置继续执行。这样才会有“出现的结果一闪而过”的现象,为了更清晰的看到函数执行的结果(例如:显示屏显示的图像),我们可以使用delay延迟函数。

注意:你看到的一闪而过的结果是因为,中断执行完成后,主程序中的程序运行的结果覆盖了你在中断服务函数中运行的结果。

USMART功能占用了STM32的引脚资源

USMART配置流程

步骤一

主函数中调用usmart_dev.init函数初始化usmart

步骤二

在usmart_config.h中注册函数

初始化函数的调用格式:

uart_init(115200);      //串口初始化为115200
usmart_dev.init(SystemCoreClock/1000000);   //初始化USMART   

正点原子封装的USMART是在其封装的USART串口通信源码基础上建立的,因此我们必须先初始化USART然后再调用usmart_dev.init(时钟频率)初始化定时器时钟频率,其实usmart_dev.init是usmart_init()函数的函数指针,我们可以参考usmart_init()代码:

void usmart_init(u8 sysclk)
{
#if USMART_ENTIMX_SCAN==1  Timer4_Init(1000,(u32)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步.
#endif  usmart_dev.sptype=1;    //十六进制显示参数
}  

注册函数的格式:

(void*)LCD_ReadPoint,"u16 LCD_ReadPoint(u16 x,u16 y)"
// (void*)函数名,”函数声明”,

这样的声明形式也正应验了我们之前所提及的USMART作用的实质:

通过串口接收数据

访问“函数首地址+传入函数对应参数地址偏移量”

执行该函数

USMART代码示例

Led.c

#include "stm32f10x.h"
#include "led.h"  void led_set(u8 state)
{  GPIO_InitTypeDef GPIO_InitStructure;  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_Init(GPIOB,&GPIO_InitStructure);  PBout(5) = state;
}  

Led.h

#ifndef _LED_H
#define _LED_H  #include "sys.h"  void led_set(u8 state);  #endif  

Usmart_config.c

// 将这个程序替换掉原来的usmart_config.c程序即可
#include "usmart.h"
#include "usmart_str.h"   用户配置区///
//这下面要包含所用到的函数所申明的头文件(用户自己添加)
#include "delay.h"
#include "sys.h"
#include "led.h"  extern void led_set(u8 state);  //函数名列表初始化(用户自己添加)
//用户直接在这里输入要执行的函数名及其查找串
struct _m_usmart_nametab usmart_nametab[]=
{
#if USMART_USE_WRFUNS==1    //如果使能了读写操作  (void*)read_addr,"u32 read_addr(u32 addr)",  (void*)write_addr,"void write_addr(u32 addr,u32 val)",
#endif  (void*)delay_ms,"void delay_ms(u16 nms)",  (void*)delay_us,"void delay_us(u32 nus)",  (void*)led_set,"void led_set(u8 state)",
};
///END///
/
//函数控制管理器初始化
//得到各个受控函数的名字
//得到函数总数量
struct _m_usmart_dev usmart_dev=
{  usmart_nametab,  usmart_init,  usmart_cmd_rec,  usmart_exe,  usmart_scan,  sizeof(usmart_nametab)/sizeof(struct _m_usmart_nametab),//函数数量  0,      //参数数量  0,      //函数ID  1,      //参数显示类型,0,10进制;1,16进制  0,      //参数类型.bitx:,0,数字;1,字符串       0,      //每个参数的长度暂存表,需要MAX_PARM个0初始化  0,      //函数的参数,需要PARM_LEN个0初始化
}; 

Main.c

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "usmart.h"
#include "usmart_str.h"
#include "usart.h"  int main()
{  led_set(1);  uart_init(115200); // 初始化USART1  usmart_dev.init(SystemCoreClock); // _m_usmart_dev结构体的对象是usmart_dev,在usmart.h中声明  while(1);
}  

运行结果展示

通过串口向单片机发送“led_set(0)”指令:

有如下原理图可知,当我们PB5输出低电平后,LED小灯被点亮:

基于STM32战舰开发板的USMART调试组件的使用相关推荐

  1. 基于易灵思开发板RiscV的调试流程

    基于易灵思开发板RiscV的调试流程 一.相关环境准备 1.软件Install 2.硬件Device 二.硬件环境搭建 1.开发板连接 2.下载代码到开发板 三.软件环境搭建 1.启动Eclipse软 ...

  2. 基于stm32物联网开发板(1)

    基于stm32物联网开发板(1)   本开发板采用了STM32F103RET6作为核心CPU,72MHZ工作频率,512KB flash,64KB Sram.本开发平台外设模块有ESP8266 WIF ...

  3. 基于stm32物联网开发板(2)--LCD屏幕

    基于stm32物联网开发板(2)–LCD屏幕 LCD应用展示: LCD屏幕应用 1.概述 屏幕尺寸为1.3寸,分辨率240*240,颜色格式RGB565,驱动IC:ST7789VW: 超大可视角度:大 ...

  4. 基于stm32物联网开发板(3)--SYN6288语音模块

    基于stm32物联网开发板(3)–SYN6288语音模块 1.SYN6288语音模块展示示例 SYN6288语音模块 2.概述   SYN6288-A语音合成模块是一款性价比更高,效果更自然的一款中高 ...

  5. 【STM32F103ZE】TOF250(TTL)基于STM32系列开发板的运用

    目录 @[TOC](目录) 一.前言 二.硬件准备 二.软件准备 三.硬件接线图 四.例程源码 五.烧录说明 5.1 烧录接线示意图 5.2 烧录动态图 六.结果输出 一.前言 此片文章主要介绍如果通 ...

  6. OpenHarmony基于BearPi-HM Micro开发板,App常用组件使用 -- 上篇

    一.前言 BearPi-HM Micro开发板,目前只能使用JS开发北向App应用,我们核心应该关注的是系统源码C/C++ 因为BearPi-HM Micro开发板是有可视化交互界面的,所以我们需要知 ...

  7. dev c++怎么调试_「正点原子NANO STM32开发板资料连载」第十八章 USMART 调试组件...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十八章 U ...

  8. *基于RT-Thread的战舰开发板连接Onenent云平台(学习笔记)**

    基于RT-Thread的战舰开发板连接Onenent云平台(学习笔记) 摘要:本文主要是我在使用正点原子开发板在rt_thread框架下连接onenet云平台的学习笔记.此文主要介绍配置步骤和开发过程 ...

  9. 【正点原子STM32连载】 第二十六章 USMART调试组件实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

最新文章

  1. Oracle 9i学习日志(9)--数据字典与动态性能视图及练习
  2. java 浏览器 安全设置_IE浏览器安全设置脚本
  3. php 等5秒钟继续,android – 如何每5秒钟继续请求一个页面而不是杀死电池?
  4. python中if __name__ == __main__的解释
  5. java 获取六个月账期,应收帐龄分析里面账期分析能不能改为0-30天?
  6. 【Floyed】【匈牙利算法】导弹(jzoj 1610)
  7. C++:23 再议const的用法(下)
  8. django model对象修改_Django之Model的Meta选项详解
  9. 重磅!深度学习的顶级会议ICLR 2020 数据图文详解
  10. 杨森翔的书法:立马【五绝】
  11. hadoop hive集群_Hive的优化和压缩
  12. springmvc05 传值
  13. rdd分组聚合算子xxByKey,xxBy
  14. 视觉SLAM十四讲_2_三维空间刚体运动
  15. 你还发现了CSDN那些变化
  16. 深度置信网络(Deep belief network)matlab初解
  17. Linux:进程间的相互作用(模拟两个进程,一个存钱,另一个取钱),进程共享内存,进程的互斥,进程加锁,c++和c实现
  18. html5 blockquote,HTML blockquote 标签 | w3cschool菜鸟教程
  19. 正则表达式 常用实例 22/10/12
  20. 安利的短片 安利的真面目 zt

热门文章

  1. 王思聪「随手」装了台服务器,跑分全球第 4,网友:壕无人性!
  2. Sfm方法过程及原理
  3. 关于ESI研究前沿的思考和使用方法研究
  4. Castor xsd生成java,Can Castor handle class generation from multiple XSDs importing from a base XSD?...
  5. fdbus examples 分析-- fdb_test_server.cpp
  6. linux centos 7 docker下载mysql5.7
  7. stm32cubeide烧写程序_STM32CubeIDE使用入门的几个常见问题
  8. 分布式系统下的纠删码技术(一) -- Erasure Code (EC)
  9. 【设计一个抽象的形状类Shape,方法:求周长和求面积】
  10. 多线程调试(gdb命令行和使用集成开发qtcreator查看线程状态)