目录
0.前言
1.功能要求
电路原理图
2.STM32Cubemx初始化
3.程序实现
main.c相关初始化
中断处理函数编写
4.工程文件
这套教程针对于蓝桥杯嵌入式比赛的底层模块调试,针对于比赛进行每个模块的调试,低层模块调试好,对于赛题的实现就会方便很多,虽然有一些应试教育的意味,不过准备这个比赛对于程序整体实现思维还是有着很大帮助,我也从中受益颇多!
调试串口进行数据收发。数据发送要在一定时间内间隔发送,串口接收要实现不定长接收,这里我们使用DMA空闲中断来接收数据。
开启USART1,将PA10设置为USART1_RX,将PA9设置为USART1_TX
USART1详细设置
开启USART1中断
使能DMA
任意长度字符接收主要是靠IDLE空闲中断实现的,当DMA接收产生空闲时,即接收已经完成,就会使能空闲中断标志位IDLE,然后停止DMA ,处理已经接收到的数据,清空中断标志位,当下次数据来时,继续接收,达到接收任意长度的目的。(程序是没问题的,这是我的理解,有不对的请纠正)。该程序也有一个问题,没有解决,当这次接收的字符长长度小于上一次接收的字符串时。接收字符串的数组RX后几位仍然保留上一次接收的数据,没法清除,这里我也没有好的方法解决,还请大佬指点。
函数功能均在注释中
main.c中创建USART相关变量
/*main.c*/
uint16_t TX_tt=0;//串口发送计时标志位
uint16_t TX_Ref=0;//串口发送标志位
uint16_t RX_Deal=0;//串口接收处理标志位
uint8_t RX[50];//串口接收数组
uint8_t TX[50]="Hello world!\r\n";//串口发送数组
Task_Time()函数扩充,定时发送串口数据(移步②,查看该函数)
/*main.c*/
void Task_Time(void)
{
if(++Key_tt==10) {Key_tt=0;Key_Ref=1;}//按键扫描计时
if(++Lcd_tt==200) {Lcd_tt=0;Lcd_Ref=1;}//LCD刷新计时
if(++ADC_tt==300) {ADC_tt=0;ADC_Ref=1;}//ADC读取计时
if(++MCP_tt==200) {MCP_tt=0;MCP_Ref=1;}//MCP写入计时
if(++DAC_tt==200) {DAC_tt=0;DAC_Ref=1;}//DAC输出计时
if(++RTC_tt==1000) {RTC_tt=0;RTC_Ref=1;}//RTC获取计时
if(++TX_tt==1000) {TX_tt=0;TX_Ref=1;} //串口发送计时
}
主循环中添加判断程序
/*main.c*/
while(1)
{
if(TX_Ref==1)
{
TX_Ref=0;//清除标志位
HAL_UART_Transmit_DMA(&huart1, TX, strlen((char*)TX));//串口DMA发送数据
}
}
在主循环前添加初始化函数
/*main.c*/
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart1, RX, 50);//开启DMA接收
在stm32g4xx_it.c中的void USART1_IRQHandler(void)函数中编写以下逻辑,用来判断是否执行IDLE空闲中断
/*stm32g4xx_it.c*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/*用户函数*/
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)!=RESET)//判断空闲中断标志位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清空空闲中断标志位
HAL_UART_DMAStop(&huart1);//停止DMA接收
HAL_UART_IdleCpltCallback(&huart1);//执行IDLE空闲中断
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
在usart.c中编写HAL_UART_IdleCpltCallback()回调函数,并在usart.h中声明
/*usart.h*/
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);
在usart.c中声明外部变量并创建变量RecCount
/*usart.c*/
extern uint8_t RX[50];//串口接收数组
extern uint16_t RX_Deal;//串口接收处理标志位
uint16_t RecCount=0;
编写HAL_UART_IdleCpltCallback()函数,该函数没有在定义中找到,但是我测试了一下,只有这个函数名是能正常使用的,所以需要记一下,可以使用stm32g4xx_usart.c中已知的回调函数HAL_UART_RxCpltCallback()修改
/*usart.c*/
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
RecCount=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//对接收数据计数
RecCount=0;
HAL_UART_Receive_DMA(&huart1, RX, 50);//开启DMA接收
RX_Deal=1;
}
相关函数定义位置stm43g4xx_hal_usart.c、stm43g4xx_hal_usart.h和stm43g4xx_hal_dma.c中
/*stm32g4xx_usart.c*/
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_DMAStop(UART_HandleTypeDef *huart);
/*stm32g4xx_usart.h*/
__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__);//使能空闲中断
__HAL_UART_CLEAR_IDLEFLAG(__HANDLE__);//清除中断标志位
__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__);//获取中断标志位的状态
/*stm32g4xx_dma.h*/
__HAL_DMA_GET_COUNTER(__HANDLE__);
在Task.c中声明外部变量,就可以接收和发送信息啦
/*Task.c*/
extern uint8_t RX[50];//串口接收数组
extern uint16_t RX_Deal;//串口接收处理标志位
extern uint8_t TX[50];//串口发送数组
该工程文件为全10讲所有内容,可以作为参考,不过还是希望读者能够自己手敲一遍,加深记忆
链接:https://pan.baidu.com/s/1LcNszc7_oHWvXcucCezlxg?pwd=hw07
提取码:hw07
自己做的笔记,也希望能对你有帮助!