【NUCLEO-F446RE试用体验】+8.DMA串口接收不定长数据
-
-
lygo
- LV4工程师
-
| 2017-09-14 15:12:13
- 浏览量 924
- 回复:3
前面用了串口通信的轮询方式,那么这篇文章就要用DMA来接收数据了。如何接收不定长数据呢?那么这篇帖子就会为大家讲解。其实就是利用STM32单片机
中的IDLE中断,所以利用这个中断,可以接收不定长字节的数据
。IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。一帧数据结束后,就会产生IDLE中断。这个中断真是太有用了。省去了好多判断的麻烦。
详情请见下篇帖子:(不定长数据接收的原理http://blog.csdn.net/hyk0601/article/details/51698489 那么什么是DMA呢?(其实上面帖子也提到过)
即就是直接内存存取技术(DMA)方式。
所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。那么就进入配置吧: (1)
DMA的配置: 其余的配置和上面那篇帖子差不多,唯一区别就是要使能DMA,及配置它,如下图:
这里我们只用了DMA的接收所以使能接收就可以了。 (2)
、编写用户代码,如下:(1)、主函数main.c代码如下:void SystemClock_Config(void);
void Error_Handler(void);
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;
#define RX_BUFFER_SIZE 32 //用于接收到一帧数据的最大长度
uint8_t Rx_Buffer; //保存接收到的一帧数据
uint32_t RxLength; //实际接收到的一帧数据的长度
uint8_t RxFlag; //接收到一帧数据标记
/*******************************************************************************
串口空闲中断回调函数,在stm32f4xx_it.c 中的 void USART3_IRQHandler(void) 中调用
*******************************************************************************/
void UsartIdle_Callback(void)
{
uint32_t tmp1, tmp2;
tmp1 = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
if((tmp1 != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
tmp2 = hdma_usart2_rx.Instance->NDTR; //读取还没有被传送的个数
RxLength = RX_BUFFER_SIZE - tmp2; //DMA缓存大小减去没有被传送的个数,就等于已经被传送的个数,也就是接收到的个数。
RxFlag = 1; //接收完成标志置位
}
}
/*******************************************************************************
解析接收到的一帧数据,根据实际应用修改
*******************************************************************************/
void Rx_Handle(void)
{
char *pString;
if(RxFlag)
{
RxFlag = 0;
pString = "valid input\r\n";
switch(RxLength)
{
case 1:break;//
case 4:
if(strncmp((char *)Rx_Buffer, "lygo", 4) == 0)//判断是不是"lygo",是的话执行事情
{
pString = "success\r\n";
}break;
case 5:
if(strncmp((char *)Rx_Buffer, "STM32", 5) == 0)//是STM32的话打印型号出来
{
pString = "STM32NUCLEO F446RET6\r\n";
}break;
case 6:
if(strncmp((char *)Rx_Buffer, "LED-ON", 6) == 0)//是"LED ON"的话点亮LED5
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
pString = "LED ON\r\n";
}break;
case 7:
if(strncmp((char *)Rx_Buffer, "LED-OFF", 7) == 0)//是"LED OFF"关闭LED5
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
pString = "LED OFF\r\n";
}break;
default :break;
}
HAL_UART_Transmit(&huart2,(uint8_t *) pString, strlen(pString), 1000); //轮询方式发送
HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE); //重新使能DMA接收
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE); //启动串口接收
printf("start....\r\n");
while (1)
{
Rx_Handle();
}
}
(2)、在it.c添加以下函数:
详情请见这这两篇帖子 : http://bbs.21ic.com/icview-1472850-1-1.html?_dsign=53a0296ahttp://www.stmcu.org/module/forum/thread-602761-1-1.html (3)、
效果演示: 好的本次实验完成!!!谢谢大家!!!最后上传实验文档如下:
前面用了串口通信的轮询方式,那么这篇文章就要用DMA来接收数据了。如何接收不定长数据呢?那么这篇帖子就会为大家讲解。其实就是利用STM32单片机
中的IDLE中断,所以利用这个中断,可以接收不定长字节的数据
。IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。一帧数据结束后,就会产生IDLE中断。这个中断真是太有用了。省去了好多判断的麻烦。
详情请见下篇帖子:(不定长数据接收的原理http://blog.csdn.net/hyk0601/article/details/51698489 那么什么是DMA呢?(其实上面帖子也提到过)
即就是直接内存存取技术(DMA)方式。
所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。那么就进入配置吧: (1)
DMA的配置: 其余的配置和上面那篇帖子差不多,唯一区别就是要使能DMA,及配置它,如下图:
这里我们只用了DMA的接收所以使能接收就可以了。 (2)
、编写用户代码,如下:(1)、主函数main.c代码如下:void SystemClock_Config(void);
void Error_Handler(void);
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;
#define RX_BUFFER_SIZE 32 //用于接收到一帧数据的最大长度
uint8_t Rx_Buffer; //保存接收到的一帧数据
uint32_t RxLength; //实际接收到的一帧数据的长度
uint8_t RxFlag; //接收到一帧数据标记
/*******************************************************************************
串口空闲中断回调函数,在stm32f4xx_it.c 中的 void USART3_IRQHandler(void) 中调用
*******************************************************************************/
void UsartIdle_Callback(void)
{
uint32_t tmp1, tmp2;
tmp1 = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
if((tmp1 != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
tmp2 = hdma_usart2_rx.Instance->NDTR; //读取还没有被传送的个数
RxLength = RX_BUFFER_SIZE - tmp2; //DMA缓存大小减去没有被传送的个数,就等于已经被传送的个数,也就是接收到的个数。
RxFlag = 1; //接收完成标志置位
}
}
/*******************************************************************************
解析接收到的一帧数据,根据实际应用修改
*******************************************************************************/
void Rx_Handle(void)
{
char *pString;
if(RxFlag)
{
RxFlag = 0;
pString = "valid input\r\n";
switch(RxLength)
{
case 1:break;//
case 4:
if(strncmp((char *)Rx_Buffer, "lygo", 4) == 0)//判断是不是"lygo",是的话执行事情
{
pString = "success\r\n";
}break;
case 5:
if(strncmp((char *)Rx_Buffer, "STM32", 5) == 0)//是STM32的话打印型号出来
{
pString = "STM32NUCLEO F446RET6\r\n";
}break;
case 6:
if(strncmp((char *)Rx_Buffer, "LED-ON", 6) == 0)//是"LED ON"的话点亮LED5
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
pString = "LED ON\r\n";
}break;
case 7:
if(strncmp((char *)Rx_Buffer, "LED-OFF", 7) == 0)//是"LED OFF"关闭LED5
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
pString = "LED OFF\r\n";
}break;
default :break;
}
HAL_UART_Transmit(&huart2,(uint8_t *) pString, strlen(pString), 1000); //轮询方式发送
HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE); //重新使能DMA接收
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE); //启动串口接收
printf("start....\r\n");
while (1)
{
Rx_Handle();
}
}
(2)、在it.c添加以下函数:
详情请见这这两篇帖子 : http://bbs.21ic.com/icview-1472850-1-1.html?_dsign=53a0296ahttp://www.stmcu.org/module/forum/thread-602761-1-1.html (3)、
效果演示: 好的本次实验完成!!!谢谢大家!!!最后上传实验文档如下: