stm32f407,cubemx配置,USMART组件移植
Posted 了不起的亮亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了stm32f407,cubemx配置,USMART组件移植相关的知识,希望对你有一定的参考价值。
一、usmart介绍
1、usmart简介
本USMART调试组件是正点原子开发提供的,主要功能是通过串口调试单片机里的函数,并执行。对于代码调试很有帮助。
我们普通的代码调试是通过:改写函数,修改参数,编译下载,查看结果;如果不满意的话继续这个流程。这样流程很麻烦而且单片机也是有寿命的,如此往复有损寿命。
使用USMART调试组件,只需要在串口调试助手里面输入函数以及参数,然后通过串口直接发送给单片机,就执行了一次参数调整,不满意的话可以在串口调试助手里面修改参数然后继续向单片机发送就可以了,直到满意为止。
例如我们调试lcd屏幕上显示的内容、位置、以及显示数字大小时,USMART组件的使用就可以让我们通过串口调试助手向单片机不断发送lcd屏幕的显示指令,我们可以不断调整文字的位置大小等等,直到符合预期,最后在代码中添加符合预期的代码参数就好了。而不用在代码中一次次的修改参数,编译,最后下载到单片机中查看结果。
2、usmart组件包含的文件以及介绍
usmart组件总共包含六个文件:如下图所示
(1)readme.txt是说明文件。
(2)usmart.c负责与外部交互;
usmart.h是头文件,其中有用户配置的宏定义,可以用来配置usmart的功能以及总参数 的长度(直接和SRAM占用挂钩)、是否使能定时器扫描、是否使用读写函数等。
(3)usmart_str.c负责命令和参数解析;usamrt_str.h是其头文件。
(4)usmart_config.c文件是用户用来添加一些需要用usmart管理的函数。
3、usamrt实现的流程
usmart组件的使用包含以下几个步骤:
(1)在usmart_config.c中添加我们需要调用的函数(在usmart_nametab数组中添加),只有添加了函数我们才能用usmart组件在串口调试助手中输入,调用这个函数的功能。
(2)初始化串口,串口初始完成才能通过对应串口向单片机发送调试函数命令。
(3)初始化usmart,通过usmart_init函数实现
(4)轮询usmart_scan函数,处理串口数据。
二、cubemx工程配置
cubemx工程配置,根据正点原子提供的usmart调试组件,对于usmart移植来讲,需要使用到串口1和定时器4,因此在cubemx中需要对串口1和定时器4进行初始化。
串口1是串口调试助手和单片机交互用的,需要配置串口的波特率,使能串口1中断等。如下图所示:
定时器4是用来实现定时扫描功能的,使能了定时器中断,并在中断中调用usmart_scan函数,以实现串口数据处理。
其中定时器4的初始化和中断服务函数放在了usmart.c,为了方便移植,我们直接用usmart.c中定义的定时器相关函数。因此,在这里我们需要随意定义和初始化一个定时器(在这里为定时器1),保证生成的“uvprojx”工程加载文件 "stm32f4xx_hal_tim.h",这样就可以保证在usmart.c文件中定义的“”TIM_HandleTypeDef”结构体时候不会报错,也即我们可以用库函数自己定义定时器4的的相关内容。
三、usmart相关文件的移植
cubemx初始化完成串口1和定时器1之后,我们就可以开始移植usmart文件了:
usmart文件夹直接复制黏贴到项目文件夹内,
然后在keil工程中,添加usmart文件夹中c文件,并在target中添加头文件路径。
由以上介绍可知,usmart中有六个文件,其中readme.txt为说明文件,剩余五个文件中,需要我们更改的是usmart.c、usmart.h、usmart_config.c。
(一)usmart.c文件修改
正点原子usmart.c文件包含三个头文件,其中usart.h为串口1头文件,sys.h为时钟初始化头文件,也包含了一些常用的数据类型短关键字(例如u32、u16、u8等)。
串口1和时钟初始化我们已经在cubemx中定义完成。其中生成的时钟初始化的代码放在了main.c中的SystemClock_Config()函数:
sys.h包含的一些常用的数据类型短关键字(例如u32、u16、u8等),我们可以将其放在其他的头文件中,例如延时函数头文件delay.h中,注意的是,如果需要使用这些数据类型短关键字,也需要包含delay.h。
所以,我们将数据类型短关键字的定义放到delay.h中,然后在usmart.c中就可以直接删除#include”sys.h“。
此外由于usmart.c中把定时器4的相关内容也包括进来了,需要添加:
#include "stm32f4xx_hal_tim.h"
才能使得定时器4的相关定义内容不报错。
此外在usmart.c文件中对定时器4的初始化需要根据情况修改:如中断优先级等,在这里我随便设置的,自己可以根据设置的优先级分组适当调整。
HAL_NVIC_SetPriority(TIM4_IRQn,0,0); //设置中断优先级,抢占优先级0,子优先级0
还有对于定时器中断的时间,定时器4的时钟频率来源于APB1,这里的sysclk为APB1的时钟频率,根据自己cubemx配置的时钟频率,来填写usmart_dev.init(sysclk)中的sysclk。
//sysclk:系统时钟(Mhz) void usmart_init(u8 sysclk) #if USMART_ENTIMX_SCAN==1 Timer4_Init(64535,(u32)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步. #endif usmart_dev.sptype=1; //十六进制显示参数
由于定时器是向上计数,最大值为65535,分频因子为sysclk*100-1,即为10khz,所需要达到100ms中断一次,需要计数1000次,65535-1000=64535为定时器自动重装值。
(二)cubemx生成的usart.c文件修改
cubemx生成的串口1初始化文件为usart.c,头文件为usart.h,在usmart.c中可以直接包含这个头文件。但是需要做出一些修改。
修改内容包括:添加支持printf()函数的代码、重写中断服务函数 USART1_IRQHandler(void) 、添加串口1中断回调函数 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)。
1、首先为了支持printf()函数,需要在usart.c中添加如下代码:
#pragma import(__use_no_semihosting) struct __FILE int handle; ; FILE __stdout; void _sys_exit(int x) x = x; int fputc(int ch, FILE *f) HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY ); return ch; // while((USART1->SR&0X40)==0);// // USART1->DR = (uint8_t) ch; // return ch;
2、重写中断服务函数USART1_IRQHandler(void)
我们使用正点原子提供的中断服务函数来接收数据,需要将cubemx生成的usart.c做一定的修改。
(1)首先usart.c中添加变量:
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. u16 USART_RX_STA=0; //接收状态标记:bit15,接收完成标志;bit14:;接收到0x0d;bit13~0,接收到的有效字节数目 u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
usart.h中添加定义:
#define USART_REC_LEN 200 //定义最大接收字节数 200 #define RXBUFFERSIZE 1 //缓存大小 extern u16 USART_RX_STA; //接收状态标记 extern u8 aRxBuffer[RXBUFFERSIZE];//HAL库USART接收Buffer extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
cubemx生成的usart.c文件中已经定义了串口1的句柄为huart1:
UART_HandleTypeDef huart1;
而正点原子定义的句柄为UART1_Handler:
UART_HandleTypeDef UART1_Handler; //UART句柄
因此在cubemx生成的usart.c文件中,需要把UART1_Handler的定义删除,并把使用到UART1_Handler的地方替换为huart1
(2)其次添加中断服务函数
//串口1中断服务程序(其中使用到串口句柄的地方,已经更改为huart1) void USART1_IRQHandler(void) u32 timeout=0; HAL_UART_IRQHandler(&huart1); //调用HAL库中断处理公用函数 timeout=0; while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就绪 timeout++;////超时处理 if(timeout>HAL_MAX_DELAY) break; timeout=0; while(HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1 timeout++; //超时处理 if(timeout>HAL_MAX_DELAY) break;
特别注意的是:cubemx配置完串口1并使能中断之后,也会生成中断服务函数,在stm32f4xx_it.c文件中:
void USART1_IRQHandler(void)
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
因为我们已经重写了中断服务函数,需要将这些代码全部注释掉。
(3)添加串口回调函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
//串口1回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) if(huart->Instance==USART1)//如果是串口1 if((USART_RX_STA&0x8000)==0)//接收未完成 if(USART_RX_STA&0x4000)//接收到了0x0d if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始 else USART_RX_STA|=0x8000; //接收完成了 else //还没收到0X0D if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000; else USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
(4)最后,需要开启串口1接收中断
在串口1的初始化完成之后开启接收中断 :HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE);
void MX_USART1_UART_Init(void) /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 9600; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler(); /* USER CODE BEGIN USART1_Init 2 */ HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 /* USER CODE END USART1_Init 2 */
(三)usmart_config.c文件修改
在usamrt_config.c文件中添加要调试的函数,首先要包含该函数的头文件,其次按照如下所给代码的格式来添加对应的函数就好了:
(void*)LCD_Display_Dir,"void LCD_Display_Dir(u8 dir)", (void*)LCD_ShowxNum,"void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)",
(四)main.c文件修改
在main.c中添加头文件usmart.h,然后添加usmart组件初始化函数:
usmart_dev.init(84);
四、所有配置完成之后的文件
usmart.c
#include "usmart.h" #include "usart.h" #include "delay.h" #include "stm32f4xx_hal_tim.h" TIM_HandleTypeDef TIM4_Handler; //定时器句柄 //系统命令 u8 *sys_cmd_tab[]= "?", "help", "list", "id", "hex", "dec", "runtime", ; //处理系统指令 //0,成功处理;其他,错误代码; u8 usmart_sys_cmd_exe(u8 *str) u8 i; u8 sfname[MAX_FNAME_LEN];//存放本地函数名 u8 pnum; u8 rval; u32 res; res=usmart_get_cmdname(str,sfname,&i,MAX_FNAME_LEN);//得到指令及指令长度 if(res)return USMART_FUNCERR;//错误的指令 str+=i; for(i=0;i<sizeof(sys_cmd_tab)/4;i++)//支持的系统指令 if(usmart_strcmp(sfname,sys_cmd_tab[i])==0)break; switch(i) case 0: case 1://帮助指令 printf("\\r\\n"); #if USMART_USE_HELP printf("------------------------USMART V3.3------------------------ \\r\\n"); printf(" USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 \\r\\n"); printf("它,你可以通过串口助手调用程序里面的任何函数,并执行.因此,你可\\r\\n"); printf("以随意更改函数的输入参数(支持数字(10/16进制,支持负数)、字符串\\r\\n"), printf("、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持\\r\\n"), printf("函数返回值显示.支持参数显示进制设置功能,支持进制转换功能.\\r\\n"); printf("技术支持:www.openedv.com\\r\\n"); printf("USMART有7个系统命令(必须小写):\\r\\n"); printf("?: 获取帮助信息\\r\\n"); printf("help: 获取帮助信息\\r\\n"); printf("list: 可用的函数列表\\r\\n\\n"); printf("id: 可用函数的ID列表\\r\\n\\n"); printf("hex: 参数16进制显示,后跟空格+数字即执行进制转换\\r\\n\\n"); printf("dec: 参数10进制显示,后跟空格+数字即执行进制转换\\r\\n\\n"); printf("runtime:1,开启函数运行计时;0,关闭函数运行计时;\\r\\n\\n"); printf("请按照程序编写格式输入函数名及参数并以回车键结束.\\r\\n"); printf("--------------------------ALIENTEK------------------------- \\r\\n"); #else printf("指令失效\\r\\n"); #endif break; case 2://查询指令 printf("\\r\\n"); printf("-------------------------函数清单--------------------------- \\r\\n"); for(i=0;i<usmart_dev.fnum;i++)printf("%s\\r\\n",usmart_dev.funs[i].name); printf("\\r\\n"); break; case 3://查询ID printf("\\r\\n"); printf("-------------------------函数 ID --------------------------- \\r\\n"); for(i=0;i<usmart_dev.fnum;i++) usmart_get_fname((u8*)usmart_dev.funs[i].name,sfname,&pnum,&rval);//得到本地函数名 printf("%s id is:\\r\\n0X%08X\\r\\n",sfname,usmart_dev.funs[i].func); //显示ID printf("\\r\\n"); break; case 4://hex指令 printf("\\r\\n"); usmart_get_aparm(str,sfname,&i); if(i==0)//参数正常 i=usmart_str2num(sfname,&res); //记录该参数 if(i==0) //进制转换功能 printf("HEX:0X%X\\r\\n",res); //转为16进制 else if(i!=4)return USMART_PARMERR;//参数错误. else //参数显示设定功能 printf("16进制参数显示!\\r\\n"); usmart_dev.sptype=SP_TYPE_HEX; else return USMART_PARMERR; //参数错误. printf("\\r\\n"); break; case 5://dec指令 printf("\\r\\n"); usmart_get_aparm(str,sfname,&i); if(i==0)//参数正常 i=usmart_str2num(sfname,&res); //记录该参数 if(i==0) //进制转换功能 printf("DEC:%lu\\r\\n",res); //转为10进制 else if(i!=4)return USMART_PARMERR;//参数错误. else //参数显示设定功能 printf("10进制参数显示!\\r\\n"); usmart_dev.sptype=SP_TYPE_DEC; else return USMART_PARMERR; //参数错误. printf("\\r\\n"); break; case 6://runtime指令,设置是否显示函数执行时间 printf("\\r\\n"); usmart_get_aparm(str,sfname,&i); if(i==0)//参数正常 i=usmart_str2num(sfname,&res); //记录该参数 if(i==0) //读取指定地址数据功能 if(USMART_ENTIMX_SCAN==0)printf("\\r\\nError! \\r\\nTo EN RunTime function,Please set USMART_ENTIMX_SCAN = 1 first!\\r\\n");//报错 else usmart_dev.runtimeflag=res; if(usmart_dev.runtimeflag)printf("Run Time Calculation ON\\r\\n"); else printf("Run Time Calculation OFF\\r\\n"); else return USMART_PARMERR; //未带参数,或者参数错误 else return USMART_PARMERR; //参数错误. printf("\\r\\n"); break; default://非法指令 return USMART_FUNCERR; return 0; //////////////////////////////////////////////////////////////////////////////////////// //移植注意:本例是以stm32为例,如果要移植到其他mcu,请做相应修改. //usmart_reset_runtime,清除函数运行时间,连同定时器的计数寄存器以及标志位一起清零.并设置重装载值为最大,以最大限度的延长计时时间. //usmart_get_runtime,获取函数运行时间,通过读取CNT值获取,由于usmart是通过中断调用的函数,所以定时器中断不再有效,此时最大限度 //只能统计2次CNT的值,也就是清零后+溢出一次,当溢出超过2次,没法处理,所以最大延时,控制在:2*计数器CNT*0.1ms.对STM32来说,是:13.1s左右 //其他的:TIM4_IRQHandler和Timer4_Init,需要根据MCU特点自行修改.确保计数器计数频率为:10Khz即可.另外,定时器不要开启自动重装载功能!! #if USMART_ENTIMX_SCAN==1 //复位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; //返回计数值 //下面这两个函数,非USMART函数,放到这里,仅仅方便移植. //定时器4中断服务程序 void TIM4_IRQHandler(void) if(__HAL_TIM_GET_IT_SOURCE(&TIM4_Handler,TIM_IT_UPDATE)==SET)//溢出中断 usmart_dev.scan(); //执行usmart扫描 __HAL_TIM_SET_COUNTER(&TIM4_Handler,0);; //清空定时器的CNT __HAL_TIM_SET_AUTORELOAD(&TIM4_Handler,100);//恢复原来的设置 __HAL_TIM_CLEAR_IT(&TIM4_Handler, TIM_IT_UPDATE);//清除中断标志位 //使能定时器4,使能中断. void Timer4_Init(u16 arr,u16 psc) //定时器4 __HAL_RCC_TIM4_CLK_ENABLE(); HAL_NVIC_SetPriority(TIM4_IRQn,0,0); //设置中断优先级,抢占优先级0,子优先级0 HAL_NVIC_EnableIRQ(TIM4_IRQn); //开启ITM4中断 TIM4_Handler.Instance=TIM4; //通用定时器4 TIM4_Handler.Init.Prescaler=psc; //分频 TIM4_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器 TIM4_Handler.Init.Period=arr; //自动装载值 TIM4_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&TIM4_Handler); HAL_TIM_Base_Start_IT(&TIM4_Handler); //使能定时器4和定时器4中断 #endif //////////////////////////////////////////////////////////////////////////////////////// //初始化串口控制器 //sysclk:系统时钟(Mhz) void usmart_init(u8 sysclk) #if USMART_ENTIMX_SCAN==1 Timer4_Init(64535,(u32)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步. #endif usmart_dev.sptype=1; //十六进制显示参数 //从str中获取函数名,id,及参数信息 //*str:字符串指针. //返回值:0,识别成功;其他,错误代码. u8 usmart_cmd_rec(u8*str) u8 sta,i,rval;//状态 u8 rpnum,spnum; u8 rfname[MAX_FNAME_LEN];//暂存空间,用于存放接收到的函数名 u8 sfname[MAX_FNAME_LEN];//存放本地函数名 sta=usmart_get_fname(str,rfname,&rpnum,&rval);//得到接收到的数据的函数名及参数个数 if(sta)return sta;//错误 for(i=0;i<usmart_dev.fnum;i++) sta=usmart_get_fname((u8*)usmart_dev.funs[i].name,sfname,&spnum,&rval);//得到本地函数名及参数个数 if(sta)return sta;//本地解析有误 if(usmart_strcmp(sfname,rfname)==0)//相等 if(spnum>rpnum)return USMART_PARMERR;//参数错误(输入参数比源函数参数少) usmart_dev.id=i;//记录函数ID. break;//跳出. if(i==usmart_dev.fnum)return USMART_NOFUNCFIND; //未找到匹配的函数 sta=usmart_get_fparam(str,&i); //得到函数参数个数 if(sta)return sta; //返回错误 usmart_dev.pnum=i; //参数个数记录 return USMART_OK; //usamrt执行函数 //该函数用于最终执行从串口收到的有效函数. //最多支持10个参数的函数,更多的参数支持也很容易实现.不过用的很少.一般5个左右的参数的函数已经很少见了. //该函数会在串口打印执行情况.以:"函数名(参数1,参数2...参数N)=返回值".的形式打印. //当所执行的函数没有返回值的时候,所打印的返回值是一个无意义的数据. void usmart_exe(void) u8 id,i; u32 res; u32 temp[MAX_PARM];//参数转换,使之支持了字符串 u8 sfname[MAX_FNAME_LEN];//存放本地函数名 u8 pnum,rval; id=usmart_dev.id; if(id>=usmart_dev.fnum)return;//不执行. usmart_get_fname((u8*)usmart_dev.funs[id].name,sfname,&pnum,&rval);//得到本地函数名,及参数个数 printf("\\r\\n%s(",sfname);//输出正要执行的函数名 for(i=0;i<pnum;i++)//输出参数 if(usmart_dev.parmtype&(1<<i))//参数是字符串 printf("%c",\'"\'); printf("%s",usmart_dev.parm+usmart_get_parmpos(i)); printf("%c",\'"\'); temp[i]=(u32)&(usmart_dev.parm[usmart_get_parmpos(i)]); else //参数是数字 temp[i]=*(u32*)(usmart_dev.parm+usmart_get_parmpos(i)); if(usmart_dev.sptype==SP_TYPE_DEC)printf("%ld",temp[i]);//10进制参数显示 else printf("0X%X",temp[i]);//16进制参数显示 if(i!=pnum-1)printf(","); printf(")"); #if USMART_ENTIMX_SCAN==1 usmart_reset_runtime(); //计时器清零,开始计时 #endif switch(usmart_dev.pnum) case 0://无参数(void类型) res=(*(u32(*)())usmart_dev.funs[id].func)(); break; case 1://有1个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0]); break; case 2://有2个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1]); break; case 3://有3个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2]); break; case 4://有4个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3]); break; case 5://有5个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4]); break; case 6://有6个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\\ temp[5]); break; case 7://有7个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\\ temp[5],temp[6]); break; case 8://有8个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\\ temp[5],temp[6],temp[7]); break; case 9://有9个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\\ temp[5],temp[6],temp[7],temp[8]); break; case 10://有10个参数 res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\\ temp[5],temp[6],temp[7],temp[8],temp[9]); break; #if USMART_ENTIMX_SCAN==1 usmart_get_runtime();//获取函数执行时间 #endif if(rval==1)//需要返回值. if(usmart_dev.sptype==SP_TYPE_DEC)printf("=%lu;\\r\\n",res);//输出执行结果(10进制参数显示) else printf("=0X%X;\\r\\n",res);//输出执行结果(16进制参数显示) else printf(";\\r\\n"); //不需要返回值,直接输出结束 if(usmart_dev.runtimeflag) //需要显示函数执行时间 printf("Function Run Time:%d.%1dms\\r\\n",usmart_dev.runtime/10,usmart_dev.runtime%10);//打印函数执行时间 //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]=\'\\0\'; //在末尾加入结束符. sta=usmart_dev.cmd_rec(USART_RX_BUF);//得到函数各个信息 if(sta==0)usmart_dev.exe(); //执行函数 else len=usmart_sys_cmd_exe(USART_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; USART_RX_STA=0;//状态寄存器清空 #if USMART_USE_WRFUNS==1 //如果使能了读写操作 //读取指定地址的值 u32 read_addr(u32 addr) return *(u32*)addr;// //在指定地址写入指定的值 void write_addr(u32 addr,u32 val) *(u32*)addr=val; #endif
usart.c(我使用到了三个串口,大家只需看串口1定义的内容即可)
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file usart.c * @brief This file provides code for the configuration * of the USART instances. ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ #include <stdio.h> #include <stdarg.h> #include <string.h> /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "usart.h" /* USER CODE BEGIN 0 */ /**********************************begin************************************************************/ #pragma import(__use_no_semihosting) struct __FILE int handle; ; FILE __stdout; void _sys_exit(int x) x = x; int fputc(int ch, FILE *f) HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY ); return ch; // while((USART1->SR&0X40)==0);// // USART1->DR = (uint8_t) ch; // return ch; /**********************************end************************************************************/ /**********************************serial port transmit function begin****************************************************/ /** */ void UsartPrintf(UART_HandleTypeDef USARTx, char *fmt,...) unsigned char UsartPrintfBuf[256]; va_list ap; unsigned char *pStr = UsartPrintfBuf; va_start(ap, fmt); vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); // va_end(ap); while(*pStr != NULL) HAL_UART_Transmit (&USARTx ,(uint8_t *)pStr++,1,HAL_MAX_DELAY ); /**********************************serial port transmit function end****************************************************/ //串口1中断服务程序 //注意,读取USARTx->SR能避免莫名其妙的错误 u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. //接收状态标记:bit15,接收完成标志;bit14:;接收到0x0d;bit13~0,接收到的有效字节数目 u16 USART_RX_STA=0; //接收状态标记 u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲 /* USER CODE END 0 */ UART_HandleTypeDef huart1; UART_HandleTypeDef huart2; UART_HandleTypeDef huart3; /* USART1 init function */ void MX_USART1_UART_Init(void) /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 9600; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler(); /* USER CODE BEGIN USART1_Init 2 */ HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE); //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 /* USER CODE END USART1_Init 2 */ /* USART2 init function */ void MX_USART2_UART_Init(void) /* USER CODE BEGIN USART2_Init 0 */ /* USER CODE END USART2_Init 0 */ /* USER CODE BEGIN USART2_Init 1 */ /* USER CODE END USART2_Init 1 */ huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) Error_Handler(); /* USER CODE BEGIN USART2_Init 2 */ /* USER CODE END USART2_Init 2 */ /* USART3 init function */ void MX_USART3_UART_Init(void) /* USER CODE BEGIN USART3_Init 0 */ /* USER CODE END USART3_Init 0 */ /* USER CODE BEGIN USART3_Init 1 */ /* USER CODE END USART3_Init 1 */ huart3.Instance = USART3; huart3.Init.BaudRate = 9600; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart3) != HAL_OK) Error_Handler(); /* USER CODE BEGIN USART3_Init 2 */ /* USER CODE END USART3_Init 2 */ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) GPIO_InitTypeDef GPIO_InitStruct = 0; if(uartHandle->Instance==USART1) /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* USART1 clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ else if(uartHandle->Instance==USART2) /* USER CODE BEGIN USART2_MspInit 0 */ /* USER CODE END USART2_MspInit 0 */ /* USART2 clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART2 interrupt Init */ HAL_NVIC_SetPriority(USART2_IRQn, 2, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); /* USER CODE BEGIN USART2_MspInit 1 */ /* USER CODE END USART2_MspInit 1 */ else if(uartHandle->Instance==USART3) /* USER CODE BEGIN USART3_MspInit 0 */ /* USER CODE END USART3_MspInit 0 */ /* USART3 clock enable */ __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**USART3 GPIO Configuration PB10 ------> USART3_TX PB11 ------> USART3_RX */ GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USART3 interrupt Init */ HAL_NVIC_SetPriority(USART3_IRQn, 3, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); /* USER CODE BEGIN USART3_MspInit 1 */ /* USER CODE END USART3_MspInit 1 */ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) if(uartHandle->Instance==USART1) /* USER CODE BEGIN USART1_MspDeInit 0 */ /* USER CODE END USART1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART1_CLK_DISABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); /* USART1 interrupt Deinit */ HAL_NVIC_DisableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ else if(uartHandle->Instance==USART2) /* USER CODE BEGIN USART2_MspDeInit 0 */ /* USER CODE END USART2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART2_CLK_DISABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3); /* USART2 interrupt Deinit */ HAL_NVIC_DisableIRQ(USART2_IRQn); /* USER CODE BEGIN USART2_MspDeInit 1 */ /* USER CODE END USART2_MspDeInit 1 */ else if(uartHandle->Instance==USART3) /* USER CODE BEGIN USART3_MspDeInit 0 */ /* USER CODE END USART3_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART3_CLK_DISABLE(); /**USART3 GPIO Configuration PB10 ------> USART3_TX PB11 ------> USART3_RX */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11); /* USART3 interrupt Deinit */ HAL_NVIC_DisableIRQ(USART3_IRQn); /* USER CODE BEGIN USART3_MspDeInit 1 */ /* USER CODE END USART3_MspDeInit 1 */ /* USER CODE BEGIN 1 */ //串口1中断服务程序 void USART1_IRQHandler(void) u32 timeout=0; HAL_UART_IRQHandler(&huart1); //调用HAL库中断处理公用函数 timeout=0; while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就绪 timeout++;////超时处理 if(timeout>HAL_MAX_DELAY) break; timeout=0; while(HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1 timeout++; //超时处理 if(timeout>HAL_MAX_DELAY) break; //串口1回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) if(huart->Instance==USART1)//如果是串口1stm32 usmart使用
我直接用正点原子给的,步骤如下
先添加三个.c进工程,添加两个头文件的编译路径
#include "usart.h"
#include "usmart.h"main函数里添加如下
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(9600); //串口初始化为9600
usmart_dev.init(72); //初始化USMART
delay_init(); //延时函数初始化
打开usmart_config.h 如下操作即可
然后就能烧录打开串口开始使用了,输入help(勾选发送新行)即可知道使用说明,想执行某个函数就输入函数和参数,就会被执行,设置一下就可以看到执行时间,
注意目前版本函数的参数只能是单纯的数据,它可以是指针也可以是整数或其他,但不能是结构体,或者填宏定义,因为其不能被正确识别,所以想GPIO_ResetBits之类的函数就会出现参数错误
usmart能让我们不重新烧录而执行函数,对调参数操作有很大的帮助
usmart还支持显示函数运行时间,是用TIM4计时的,所以用了计时就不能在代码里用TIM4
以上是关于stm32f407,cubemx配置,USMART组件移植的主要内容,如果未能解决你的问题,请参考以下文章
STM32F407开发板用户手册第21章 STM32F407的NVIC中断分组和配置(重要)