完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 正点原子运营官 于 2020-4-21 12:01 编辑 1)实验平台:ALIENTEK NANO STM32F411 V1开发板 2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子 第十八章 USMART 调试组件实验 本章,我们将向大家介绍一个十分重要的辅助调试工具:USMART 调试组件。该组件由ALIENTEK 开发提供,功能类似 linux 的 shell(RTT 的 finsh 也属于此类)。USMART 最主要的功能就是通过串口调用单片机里面的函数,并执行,对我们调试代码是很有帮助的。本章分为如下几个部分: 18.1 USMART 调试组件简介 18.2 硬件设计 18.3 软件设计 18.4 下载验证 18.1 USMART 调试组件简介 USMART 是由 ALIENTEK 开发的一个灵巧的串口调试互交组件,通过它你可以通过串口助手调用程序里面的任何函数,并执行。因此,你可以随意更改函数的输入参数(支持数字(10/16进制)、字符串、函数入口地址等作为参数),单个函数最多支持 10 个输入参数,并支持函数返回值显示,目前最新版本为 V3.2。 USMART 的特点如下: 1, 可以调用绝大部分用户直接编写的函数。 2, 资源占用极少(最少情况:FLASH:4K;SRAM:72B)。 3, 支持参数类型多(数字(包含 10/16 进制)、字符串、函数指针等)。 4, 支持函数返回值显示。 5, 支持参数及返回值格式设置。 6, 支持函数执行时间计算(V3.1 版本新特性)。 7, 使用方便。 有了 USMART,你可以轻易的修改函数参数、查看函数运行结果,从而快速解决问题。比如你调试一个摄像头模块,需要修改其中的几个参数来得到最佳的效果,普通的做法:写函数 ->修改参数->下载->看结果->不满意->修改参数->下载->看结果->不满意….不停的循环,直到满意为止。这样做很麻烦不说,单片机也是有寿命的啊,老这样不停的刷,很折寿的。而利用USMART,则只需要在串口调试助手里面输入函数及参数,然后直接串口发送给单片机,就执行了一次参数调整,不满意的话,你在串口调试助手修改参数在发送就可以了,直到你满意为止。这样,修改参数十分方便,不需要编译、不需要下载、不会让单片机折寿。 USMART 支持的参数类型基本满足任何调试了,支持的类型有:10 或者 16 进制数字、字符串指针(如果该参数是用作参数返回的话,可能会有问题!)、函数指针等。因此绝大部分函数,可以直接被 USMART 调用,对于不能直接调用的,你只需要重写一个函数,把影响调用的参数去掉即可,这个重写后的函数,即可以被 USMART 调用了。 USMART 的实现流程简单概括就是:第一步,添加需要调用的函数(在 usmart_config.c 里面的 usmart_nametab 数组里面添加);第二步,初始化串口;第三步,初始化 USMART(通过usmart_init 函数实现);第四步,轮询 usmart_scan 函数,处理串口数据。 经过以上简单介绍,我们对 USMART 有了个大概了解,接下来我们来简单介绍下 USMART 组件的移植。 USMART 组件总共包含 6 文件如图 18.1.1 所示: 图 18.1.1 USMART 组件代码 其中 redeme.txt 是一个说明文件,不参与编译。其他五个文件,usmart.c 负责与外部互交等。 usmat_str.c 主要负责命令和参数解析。usmart_config.c 主要由用户添加需要由 usmart 管理的函 数。 usmart.h 和 usmart_str.h 是两个头文件,其中 usmart.h 里面含有几个用户配置宏定义,可以用来配置 usmart 的功能及总参数长度(直接和 SRAM 占用挂钩)、是否使能定时器扫描、是否使用读写函数等。 USMART 的移植,只需要实现 5 个函数。其中 4 个函数都在 usmart.c 里面,另外一个是串 口接收函数,必须有由用户自己实现,用于接收串口发送过来的数据。 第一个函数,串口接收函数。该函数,我们是通过 SYSTEM 文件夹默认的串口接收来实现的,该函数在 5.3.1 节有介绍过,我们这里就不列出来了。SYSTEM 文件夹里面的串口接收函 数,最大可以一次接收 200 字节,用于从串口接收函数名和参数等。大家如果在其他平台移植,请参考 SYSTEM 文件夹串口接收的实现方式进行移植。 第二个是 void usmart_init(void)函数,该函数的实现代码如下: //初始化串口控制器 //sysclk:系统时钟(Mhz) 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; //十六进制显示参数 } 该函数有一个参数 sysclk,就是用于定时器初始化。这里需要说明一下,为了让我们的库函数和寄存器实现函数一致,我们这里不直接通过 SystemCoreClock 来获取系统时钟,直接通过在外面设置的方式(当然你也可以去掉 sysclk 这个参数),这样函数体里面的 Timer4_Init 函 数 就 可 修 改 为 Timer4_Init(1000,(u32) SystemCoreClock/100-1) 。 另 外 USMART_ENTIMX_SCAN 是在 usmart.h 里面定义的一个是否使能定时器中断扫描的宏定义。 如果为 1,就通过定时器初始化函数 Timer4_Init 初始化定时器 4 中断,每 100ms 中断一次,并 在中断服务程序 TIM4_IRQHandler 里面调用 usmart_scan 函数进行扫描,这里我们就不列出代 码,因为之前的实验对这方面讲解较多。如果为 0,那么需要用户需要自行间隔一定时间(100ms 左右为宜)调用一次 usmart_scan 函数,以实现串口数据处理。注意:如果要使用函数执行时 间统计功能(runtime 1),则必须设置 USMART_ENTIMX_SCAN 为 1。另外,为了让统计 时间精确到 0.1ms,定时器的计数时钟频率必须设置为 10Khz,否则时间就不是 0.1ms 了。 第三和第四个函数仅用于服务 USMART 的函数执行时间统计功能(串口指令:runtime 1), 分别是:usmart_reset_runtime 和 usmart_get_runtime,这两个函数代码如下: //复位 runtime //需要根据所移植到的 MCU 的定时器参数进行修改 void usmart_reset_runtime(void) { __HAL_TIM_CLEAR_FLAG(&TIM4_Handler,TIM_FLAG_UPDATE);//清除中断标志位 __HAL_TIM_SET_AUTORELOAD(&TIM4_Handler,0XFFFF); //将重装载值设置到最大 __HAL_TIM_SET_COUNTER(&TIM4_Handler,0); //清空定时器的 CNT usmart_dev.runtime=0; } //获得 runtime 时间 //返回值:执行时间,单位:0.1ms,最大延时时间为定时器 CNT 值的 2 倍*0.1ms //需要根据所移植到的 MCU 的定时器参数进行修改 u32 usmart_get_runtime(void) { if(__HAL_TIM_GET_FLAG(&TIM4_Handler,TIM_FLAG_UPDATE)==SET) //在运行期间,产生了定时器溢出 { usmart_dev.runtime+=0XFFFF; } usmart_dev.runtime+=__HAL_TIM_GET_COUNTER(&TIM4_Handler); return usmart_dev.runtime; //返回计数值 } 这里我们还是利用定时器 4 来做执行时间计算,usmart_reset_runtime 函数在每次 USMART 调用函数之前执行,清除计数器,然后在函数执行完之后,调用 usmart_get_runtime 获取整个 函数的运行时间。由于 usmart 调用的函数,都是在中断里面执行的,所以我们不太方便再用定 时器的中断功能来实现定时器溢出统计,因此,USMART 的函数执行时间统计功能,最多可以 统计定时器溢出 1 次的时间,对 STM32 来说,定时器是 16 位的,最大计数是 65535,而由于 我们定时器设置的是 0.1ms 一个计时周期(10Khz),所以最长计时时间是:65535*2*0.1ms=13.1 秒。也就是说,如果函数执行时间超过 13.1 秒,那么计时将不准确。 最后一个是 usmart_scan 函数,该函数用于执行 usmart 扫描,该函数需要得到两个参量, 第一个是从串口接收到的数组(USART_RX_BUF),第二个是串口接收状态(USART_RX_STA)。 接收状态包括接收到的数组大小,以及接收是否完成。该函数代码如下: //usmart 扫描函数 //通过调用该函数,实现 usmart 的各个控制.该函数需要每隔一定时间被调用一次 //以及时执行从串口发过来的各个函数. //本函数可以在中断里面调用,从而实现自动管理. //如果非 ALIENTEK 用户,则 USART_RX_STA 和 USART_RX_BUF[]需要用户自己实现 void usmart_scan(void) { u8 sta,len; if(USART_RX_STA&0x8000)//串口接收完成? { len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度 USART_RX_BUF[len]=' |