开发环境
STM32CUBMX
- 正点原子STM32F407ZGT6探索者开发板
- MDK-ARM 5.31
第一种方式:直接接收
- 配置外部时钟源
2.配置时钟树
3.配置串口一
生产代码后进入工程
重定向printf到串口1(建议在usart.c里重定向)
重定向代码块
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
printf("hello wroldrn");
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_UART_Receive(&huart1,receive_buff,10,0xff);// 接收10个字符
if(receive_buff[0] != 0)
{
printf("recive buff is %s rn",receive_buff);
memset(receive_buff,0,20); // 清除接收内容
}
}
/* USER CODE END 3 */
}
实验现象
第二种方式:中断方式
时钟准备和串口初始化如上一种方式所示,实例从设置中断开始
设置中断
生成代码
定义相关变量
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0}; //接收缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t cAlmStr[] = "数据溢出(大于256)rn";
在usart.h 中编写回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
Uart1_RxFlag = 1;
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
main函数内内容
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Uart1_RxFlag != 0)
{
printf("recive buff is %srn",Uart1_RxBuff);
Uart1_RxFlag = 0;
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,256);
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
实验现象
第三种方法:定时器中断判断串口是否传输完成
此方法可以看做是看作中断接收的一种拓展用法,不同于上一种方法介绍的需要特定的格式的数据帧来判定,此方法结合了定时器,以5ms为界限,如果5ms后没有接收新的数据则认为一包数据已经发送完成。下面是配置的具体方法
1.配置时钟和串口中断的同上,然后配置定时器中断
定时器基础配置
打开定时器中断
我们设置的5ms检测串口是否发送完成
我们使用的定时器的主频是84MHZ
那么定时器频率可用下列方式计算
F = 84 M H Z ( P r e s c a l e r + 1 ) × ( C o u n t e r P e r i o d + 1 ) ; F = frac{84MHZ}{(Prescaler+1)times (CounterPeriod+1)}; F=(Prescaler+1)×(CounterPeriod+1)84MHZ;
配置完成后生成代码
程序部分
定义相关变量
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0}; //接收缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t cAlmStr[] = "数据溢出(大于256)rn";
编写串口中断服务函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart -> Instance == USART1)
{
if(Uart1_Rx_Cnt == 0)
{
__HAL_TIM_CLEAR_FLAG(&htim6,TIM_FLAG_UPDATE);
HAL_TIM_Base_Start_IT(&htim6);
Uart1_RxBuff[Uart1_Rx_Cnt] = aRxBuffer;
Uart1_Rx_Cnt ++;
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt ++] = aRxBuffer;
}
if(Uart1_Rx_Cnt >= 255)
{
Uart1_Rx_Cnt = 0;
Uart1_RxFlag = 0;
memset(Uart1_RxBuff,0x00,256);
}
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
}
}
编写定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim ->Instance == TIM6)
{
HAL_TIM_Base_Stop(&htim6);
__HAL_TIM_SetCounter(&htim6,0);
Uart1_RxFlag = 1;
}
}
编写主程序
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Uart1_RxFlag == 1)
{
printf("rec buff is %srn",Uart1_RxBuff);
memset(Uart1_RxBuff,0x00,256);
Uart1_Rx_Cnt = 0;
Uart1_RxFlag = 0;
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
实验现象
第四种方法:串口空闲中断加DMA
使用DMA可以减少CPU负荷,当接收大批量数据的时候,可以防止频繁进入中断。这样有助于提高效率,下面我们介绍STM32带有的串口空闲中断来配合DMA接收数据的例子。
配置部分
1 串口配置
基本部分
配置DMA
中断配置
生产代码
定义宏定义
#define USART1_DMA_REC_SIZE 600
#define USART1_REC_SIZE 1200
编写结构体
typedef struct
{
uint8_t UsartRecFlag; // 标志位
uint16_t UsartRecLen; // 接收数据长度
uint16_t UsartDMARecLEN; // DMA 接收长度
uint8_t Usart1DMARecBuffer[USART1_DMA_REC_SIZE]; // DMA 接收数组
uint8_t Usart1RecBuffer[USART1_REC_SIZE]; // 接收组
}teUsart1type;
编写打开中断函数
// 打开相关中断
void EnableUsart_It(void)
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
}
改写中断服务函数(在stm32f4xx_it.c中)
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint16_t temp = 0;
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1); // 关闭DMA
temp = huart1.Instance -> SR; // 清除SR状态寄存器
temp = huart1.Instance -> DR; // 清除DR数据寄存器,用来清除中断
temp = hdma_usart1_rx.Instance -> NDTR; // 获取未传输的数据个数
//temp = hdma_usart2_rx.Instance -> NDTR; // F4
Usart1type.UsartDMARecLEN = USART1_DMA_REC_SIZE - temp;
HAL_UART_RxCpltCallback(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
/* USER CODE END USART1_IRQn 1 */
}
编写中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart -> Instance == USART1)
{
if(Usart1type.UsartRecLen > 0)
{
memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartRecLen],Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
}
else
{
memcpy(&Usart1type.Usart1RecBuffer,Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
}
memset(Usart1type.Usart1DMARecBuffer,0x00,600);
Usart1type.UsartRecFlag = 1;
}
}
编写主程序
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
EnableUsart_It();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Usart1type.UsartRecFlag == 1)
{
printf("rec buff is %srn",Usart1type.Usart1RecBuffer);
memset(Usart1type.Usart1RecBuffer,0x00,USART1_REC_SIZE);
Usart1type.UsartRecLen = 0;
Usart1type.UsartRecFlag = 0;
}
HAL_Delay(20);
}
/* USER CODE END 3 */
}
实验现象
END
开发环境
STM32CUBMX
- 正点原子STM32F407ZGT6探索者开发板
- MDK-ARM 5.31
第一种方式:直接接收
- 配置外部时钟源
2.配置时钟树
3.配置串口一
生产代码后进入工程
重定向printf到串口1(建议在usart.c里重定向)
重定向代码块
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
printf("hello wroldrn");
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_UART_Receive(&huart1,receive_buff,10,0xff);// 接收10个字符
if(receive_buff[0] != 0)
{
printf("recive buff is %s rn",receive_buff);
memset(receive_buff,0,20); // 清除接收内容
}
}
/* USER CODE END 3 */
}
实验现象
第二种方式:中断方式
时钟准备和串口初始化如上一种方式所示,实例从设置中断开始
设置中断
生成代码
定义相关变量
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0}; //接收缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t cAlmStr[] = "数据溢出(大于256)rn";
在usart.h 中编写回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
Uart1_RxFlag = 1;
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
main函数内内容
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Uart1_RxFlag != 0)
{
printf("recive buff is %srn",Uart1_RxBuff);
Uart1_RxFlag = 0;
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,256);
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
实验现象
第三种方法:定时器中断判断串口是否传输完成
此方法可以看做是看作中断接收的一种拓展用法,不同于上一种方法介绍的需要特定的格式的数据帧来判定,此方法结合了定时器,以5ms为界限,如果5ms后没有接收新的数据则认为一包数据已经发送完成。下面是配置的具体方法
1.配置时钟和串口中断的同上,然后配置定时器中断
定时器基础配置
打开定时器中断
我们设置的5ms检测串口是否发送完成
我们使用的定时器的主频是84MHZ
那么定时器频率可用下列方式计算
F = 84 M H Z ( P r e s c a l e r + 1 ) × ( C o u n t e r P e r i o d + 1 ) ; F = frac{84MHZ}{(Prescaler+1)times (CounterPeriod+1)}; F=(Prescaler+1)×(CounterPeriod+1)84MHZ;
配置完成后生成代码
程序部分
定义相关变量
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0}; //接收缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t cAlmStr[] = "数据溢出(大于256)rn";
编写串口中断服务函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart -> Instance == USART1)
{
if(Uart1_Rx_Cnt == 0)
{
__HAL_TIM_CLEAR_FLAG(&htim6,TIM_FLAG_UPDATE);
HAL_TIM_Base_Start_IT(&htim6);
Uart1_RxBuff[Uart1_Rx_Cnt] = aRxBuffer;
Uart1_Rx_Cnt ++;
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt ++] = aRxBuffer;
}
if(Uart1_Rx_Cnt >= 255)
{
Uart1_Rx_Cnt = 0;
Uart1_RxFlag = 0;
memset(Uart1_RxBuff,0x00,256);
}
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
}
}
编写定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim ->Instance == TIM6)
{
HAL_TIM_Base_Stop(&htim6);
__HAL_TIM_SetCounter(&htim6,0);
Uart1_RxFlag = 1;
}
}
编写主程序
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Uart1_RxFlag == 1)
{
printf("rec buff is %srn",Uart1_RxBuff);
memset(Uart1_RxBuff,0x00,256);
Uart1_Rx_Cnt = 0;
Uart1_RxFlag = 0;
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
实验现象
第四种方法:串口空闲中断加DMA
使用DMA可以减少CPU负荷,当接收大批量数据的时候,可以防止频繁进入中断。这样有助于提高效率,下面我们介绍STM32带有的串口空闲中断来配合DMA接收数据的例子。
配置部分
1 串口配置
基本部分
配置DMA
中断配置
生产代码
定义宏定义
#define USART1_DMA_REC_SIZE 600
#define USART1_REC_SIZE 1200
编写结构体
typedef struct
{
uint8_t UsartRecFlag; // 标志位
uint16_t UsartRecLen; // 接收数据长度
uint16_t UsartDMARecLEN; // DMA 接收长度
uint8_t Usart1DMARecBuffer[USART1_DMA_REC_SIZE]; // DMA 接收数组
uint8_t Usart1RecBuffer[USART1_REC_SIZE]; // 接收组
}teUsart1type;
编写打开中断函数
// 打开相关中断
void EnableUsart_It(void)
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
}
改写中断服务函数(在stm32f4xx_it.c中)
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint16_t temp = 0;
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1); // 关闭DMA
temp = huart1.Instance -> SR; // 清除SR状态寄存器
temp = huart1.Instance -> DR; // 清除DR数据寄存器,用来清除中断
temp = hdma_usart1_rx.Instance -> NDTR; // 获取未传输的数据个数
//temp = hdma_usart2_rx.Instance -> NDTR; // F4
Usart1type.UsartDMARecLEN = USART1_DMA_REC_SIZE - temp;
HAL_UART_RxCpltCallback(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
/* USER CODE END USART1_IRQn 1 */
}
编写中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart -> Instance == USART1)
{
if(Usart1type.UsartRecLen > 0)
{
memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartRecLen],Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
}
else
{
memcpy(&Usart1type.Usart1RecBuffer,Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
}
memset(Usart1type.Usart1DMARecBuffer,0x00,600);
Usart1type.UsartRecFlag = 1;
}
}
编写主程序
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
EnableUsart_It();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(Usart1type.UsartRecFlag == 1)
{
printf("rec buff is %srn",Usart1type.Usart1RecBuffer);
memset(Usart1type.Usart1RecBuffer,0x00,USART1_REC_SIZE);
Usart1type.UsartRecLen = 0;
Usart1type.UsartRecFlag = 0;
}
HAL_Delay(20);
}
/* USER CODE END 3 */
}
实验现象
END
举报