本文写自于博主编写平衡智能车程序的时候,由于中断使用的较多,如定时器中断,串口中断等等。为了避免MCU运行程序时卡死,我就将串口中断改为串口IDLE空闲接收中断。可以实现不定长数据的接收;以此可以修改PID参数,对调车的朋友很友好。
实验效果如下:
接下来对Cubemx进行相关配置
使用stm32F103系列
1.配置RCC
- 设置高速外部时钟HSE 选择外部时钟源
2.配置SYS
- 设置成SW-Debug模式
3.配置串口
最后在开启中断即可。
4.配置中断
- 可按自己的需求调整中断的优先级,0-15,优先级依次降低。
5.配置时钟树 (我一般默认72M)
5.设置项目名称,生成工程即可。
接下来编写业务代码
1. 打开usart.c文件
//最usart.c的顶部/* USER CODE BEGIN 0 */添加下列代码
/* USER CODE BEGIN 0 */
#include <stdio.h>
volatile uint8_t rx_len = 0; //接收一帧数据的长度
volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
char rx_buffer[200]={0}; //接收数据缓存数组
/* USER CODE END 0 */
/* USER CODE BEGIN USART1_Init 2 */
//下方为自己添加的代码
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
/* USER CODE END USART1_Init 2 */
//最usart.c的最底部添加
/* USER CODE BEGIN 1 */
/** 函数功能: 重定向c库函数printf到DEBUG_USARTx */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/* USER CODE END 1 */
2. 然后打开usart.h文件
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
/* USER CODE BEGIN Private defines */
#define BUFFER_SIZE 200
extern volatile uint8_t rx_len ; //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern char rx_buffer[200]; //接收数据缓存数组
/* USER CODE END Private defines */
3. 打开stm32f1xx_it.c文件
//添加usart.h头文件
#include "usart.h"
//找到void USART1_IRQHandler(void)函数
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
if((tmp_flag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
//temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//temp = huart1.Instance->DR; //读取数据寄存器中的数据
//这两句和上面那句等效
HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
//temp = hdma_usart1_rx.Instance->NDTR;// 读取NDTR寄存器,获取DMA中未传输的数据个数,
rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
recv_end_flag = 1; // 接受完成标志位置1
}
/* USER CODE END USART1_IRQn 0 */
4. 最后在main.c函数添加以下头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
5. 按自己需求编写需要修改的参数变量和代码,先附上我的;
float kp1,ki1,kd1; //PID的参数
char chage[][5] = {"kp1=", "kd1=", "kd1=", "a", "b", "c"};//用于判定
void BSP_USART_idleDMA(void)
{
if(recv_end_flag ==1) //判断标志位
{
//修改kp
if(!memcmp(chage[0],rx_buffer,4)) //比较前四个字节
{
kp1 = atof(rx_buffer+4); //如果一样则修改kp1的参数,往下的同理
printf("/***** kp = %f *****/",kp1);
}
//修改ki
if(!memcmp(chage[1],rx_buffer,4))
{
ki1 = atof(rx_buffer+4);
printf("/***** ki = %f *****/",ki1);
}
//修改kd
if(!memcmp(chage[2],rx_buffer,4))
{
kd1 = atof(rx_buffer+4);
printf("/***** kd = %f *****/",kd1);
}
rx_len=0;
recv_end_flag=0; //清除标志位
memset(rx_buffer,0x00,sizeof(rx_buffer)); //清空数组
}
HAL_UART_Receive_DMA(&huart1,(uint8_t *)rx_buffer,BUFFER_SIZE); //再次开启DMA接收
}
最后在while里调用即可
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
BSP_USART_idleDMA();
}
参考博客:STM32 HAL CubeMX 串口IDLE接收空闲中断+DMA_Z小旋的博客-CSDN博客_cubemx idle