该文档介绍stm32 uart1通过DMA方式发送和接收数据,代码示例基于ucos-ii操作系统。
- DMA发送数据
- 启动DMA并发送完成后,产生DMA发送完成中断,在DMA中断服务函数中执行以下操作:
- 清DMA发送完成标志
- 关闭串口发送DMA通道
- 给前台(应用)程序设置一个软件标志位,说明数据发送完成。
- 在数据发送缓冲区内放好要发送的数据(此数据缓冲区的首地址必须要在DMA初始化时写入到DMA配置中去)
- 将数据缓冲区内要发送的数据字节数传给DMA通道(串口发送和接收不是同一个通道)
- 开启DMA,一旦开启,则DMA开始发送数据,
- 等待数据发送完成标志!
- 判断数据发送完成:
- DMA接收数据
串口接收DMA在初始化时就处于开启状态,一直等待数据的到来,串口中断IDLE在串口一直没有数据时,是不会产生的,产生的条件是:当清除IDLE标志位后,必须有接收到第一个数据后才触发,一旦接收的数据断流,即产生IDLE中断。
这里判断接收数据完成是通过串口的空闲方式实现,即当串口的数据流停止后,就会产生IDLE中断,在中断里做
- 关闭串口接收的DMA通道。一是防止又有数据接收到,产生干扰;二是便于DMA重新配置赋值。
- 清除DMA中断标志位
- 从DMA寄存器中获取接收到的数据字节数(对于上层应用很有必要)
- 重新设置DMA下次需要接收到的数据字节数(必须大于预期的接收值长度,否则计数减到0时又会复位,覆盖接收缓冲区中的数据,导致数据丢失!)
- 开启DMA通道,等待下一次的数据接收
- 可以设置信号量,通知应用程序数据接收完成,传递接收的数据长度,便于应用程序对数据的处理。
有待思考的
!!! 此处的接收缓冲区时来自哪里? uart1的数据缓冲区,还是DMA的接收数据缓冲区,最大支持多少个字节???
!!! DMA发送数据,网上也有看到将配置封装在发送函数中,如
uart1_dma_send_data(uint8_t* buf, uint32_t len),这个好处是,可以变化mem地址(buf数组,以及长度可以适当配置变化),不错!
!!! DMA接收函数,应用DMA中断判断接收完成只能依赖于DMA配置中字节长度,支持DMA传输完成,传输过半,传输错误,但对于不固定长度接收数据,this is a question.
注意:
- DMA外设和DMA通道有对应关系,需要参考stm32手册
- DMA传输数据时,需要明确传输的字节数目
- DMA使能情况下,不能配置传输字节数到DMA寄存器,所以也不会产生DMA数据传输(因为传输字节数目为0)
#include "includes.h"/* DMA方式介绍: DMA使用流程: 1. 配置 外设端: - 串口引脚GPIO配置 - 串口功能参数配置(数据位格式,波特率等),此处还需要**配置对应的DMA请求允许**。 - 如果有中断,还需要配置中断优先级 DMA端: - DMA功能配置 - 初始化DMA通道DMA_Init(DMA1_Channel4,&DMA_InitStructure); 。 DMA源(Memory)/目的(外设)地址 。 DMA的传输方向 。 DMA的buffer size 。 DMA外设(DISABLE)/Memory(ENABLE)地址自增使能配置 。 DMA传输字节格式(支持byte,half-word,word) 。 DMA传输方式(Normal和Circle) 。 DMA传输优先级(共有四种,) 。 DMA m2m使能/失能配置(此处不是用于m2m所以配置为DISABLE) - 清除中断标志位 - 此处应DISABLE DMA通道,否则配置完成即会产生DMA数据传输(非期望数据) - 使能DMA发送完成中断 - DMA中断服务函数 发送数据完成后,即关闭DMA通道,发送信号量到应用程序外设部分配置 2. 使能(使用) 应用程序需要发送数据时: - 将要发送的数据准备好,并且要知道发送多少单位数据(发送字节数) - 配置DMA要发送的字节数,使能DMA即可.*///#define DMA_USART1_DR_Base (USART1_BASE + 0x4) //0x40013804#define DMA_USART1_DR_Base 0x40013804//1. 串口1端口配置void hal_debug_gpio_config(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //DBTX PA9 uart1_tx GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStructure); //DBRX PA10 uart1_rx GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&GPIO_InitStructure);}//2. 串口功能配置void hal_debug_func_config(void){ USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1,&USART_InitStructure); //config uart DMA request USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//enable usart1 dma send request USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //enable usart1 dma recieve request USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //enable usart1 idle interrupt USART_Cmd(USART1,ENABLE);}//void hal_debug_nvic_config(void){ NVIC_InitTypeDef NVIC_InitStructure; //uart1 interrupt NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //dma interrupt NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}//3. 串口初始化void hal_debug_init(void){ hal_debug_gpio_config(); hal_debug_func_config(); hal_debug_nvic_config();}//////////////////////DMA config///////////////////////////////////1. uart dma configsvoid uart_dma_init(void){ DMA_InitTypeDef DMA_InitStructure; //Tx DMA CONFIG RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //enable DMA1 clock DMA_Cmd(DMA1_Channel4,DISABLE); //close DMA Channel DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = DMA_USART1_DR_Base; //(uint32_t)(&USART1->DR) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Tx_Buf; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = USART1_TX_BSIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4,&DMA_InitStructure); DMA_ClearFlag(DMA1_FLAG_GL4); // clear all DMA flags //DMA_Cmd(DMA1_Channel4,ENABLE); // close DMA Channel DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); //open DMA send inttrupt //Rx DMA CONFIG DMA_Cmd(DMA1_Channel5, DISABLE); // DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Rx_Buf; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = USART1_RX_BSIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_ClearFlag(DMA1_FLAG_GL5); DMA_Cmd(DMA1_Channel5, ENABLE); }///////////////////////uart dma send//////////////////////void DMA1_Channel4_IRQHandler(void){ if(DMA_GetITStatus(DMA1_FLAG_TC4)==SET) { DMA_ClearFlag(DMA1_FLAG_GL4); DMA_Cmd(DMA1_Channel4, DISABLE); OSMboxPost(mbLumModule_Tx, (void*)1); }}void uart_dma_send_enable(uint16_t size){ DMA1_Channel4->CNDTR = (uint16_t)size; DMA_Cmd(DMA1_Channel4, ENABLE); }void uart1_dma_send_data(void){ uint8_t err; uint16_t i; uint16_t USART1_Tx_Index=0; for(i=0;i<8;i++){ USART1_Tx_Buf[USART1_Tx_Index++]=i; } uart_dma_send_enable(USART1_Tx_Index); OSMboxPend(mbLumModule_Tx, 5000, &err);}///////////////////////uart dma send/////////////////////////////////////////////uart dma recv//////////////////////void uart1_dma_recv_data(void){ uint16_t index = 0; DMA_Cmd(DMA1_Channel5, DISABLE); DMA_ClearFlag(DMA1_FLAG_GL5); //index = USART1_RX_BSIZE - DMA_GetCurrDataCounter(DMA1_Channel5); DMA1_Channel5->CNDTR = USART1_RX_BSIZE; DMA_Cmd(DMA1_Channel5, ENABLE); //OSMboxPost(mbLumModule_Rx,USART1_Rx_Buf);}void USART1_IRQHandler(void){ if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET) { uart1_dma_recv_data(); USART_ClearITPendingBit(USART1,USART_IT_IDLE); }}///////////////////////uart dma recv//////////////////////123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
2. app_test.c
该文件为app_test线程,用于1s发送一次串口数据,其中发送串口数据中应用了信号量,如果没有等待到信号量,最大等待时间为5s(加上此处的1s周期则为6s)。
#include "includes.h"OS_STK gTest[APP_TEST_STK_SIZE];uint8_t USART1_Tx_Buf[USART1_TX_BSIZE] = {0};OS_EVENT *gSemEvent1 = NULL;OS_EVENT *mbLumModule_Tx = NULL;void app_test(void* p_arg){ (void)p_arg; //create new event gSemEvent1 = OSSemCreate(2); mbLumModule_Tx = OSMboxCreate((void*)0); while(1){ //OSSemPost(gSemEvent1); OSTimeDly(1000); //printf("hello world!rn"); uart1_dma_send_data(); }}123456789101112131415161718192021
该函数用于创建ucos任务,一个main函数,一个创建任务的任务函数。
其中app_test任务就是这边uart DMA发送数据的任务。
#include "includes.h"/* 1. 主函数 用于启动ucos-ii操作系统,启动第一个启动任务 2. 启动任务启动其他的任务*/static OS_STK gTaskStartStk[APP_TASK_START_STK_SIZE]; //定义栈 static void App_TaskStart(void *p_arg);static void app_task_create (void);//1. mainint main(void){ INT8U os_err; OSInit(); os_err = OSTaskCreateExt((void (*)(void *)) App_TaskStart, /* Create the start task. */ (void * ) 0, (OS_STK * )&gTaskStartStk[APP_TASK_START_STK_SIZE - 1], (INT8U ) APP_TASK_START_PRIO, (INT16U ) APP_TASK_START_PRIO, (OS_STK * )&gTaskStartStk[0], (INT32U ) APP_TASK_START_STK_SIZE, (void * )0, (INT16U )(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));#if (OS_TASK_NAME_SIZE >= 11) OSTaskNameSet(APP_TASK_START_PRIO, (INT8U *)"Start Task", &os_err);#endif OSStart(); } //2. start taskstatic void App_TaskStart(void *p_arg){ (void)p_arg; /*hal init*/ hal_dirvers_init(); //printf("uCos-II V2.86 FM.rn");#if (OS_TASK_STAT_EN > 0) OSStatInit(); /* Determine CPU capacity.*/#endif //printf("Create App Task.rn"); /*creat other app*/ app_task_create(); while(1){ OSTimeDly(1000); hal_led_toggle(1); //led_toggle } }/*Create APP Task*/static void app_task_create (void){ INT8U os_err; //test task os_err = OSTaskCreate(app_test, (void *)0, &gTest[APP_TEST_STK_SIZE-1], APP_TEST_PRIO); #if (OS_TASK_NAME_SIZE >= 20) OSTaskNameSet(APP_TEST_PRIO, (INT8U *)"test task", &os_err); #endif //led toggle task/*****************************************************************************************/ os_err = OSTaskCreate((void (*)(void *))app_led_toggle, (void*)0, (OS_STK*)&gTaskLedToggle[APP_TASK_LED_STK_SIZE-1], (INT8U)APP_TASK_LED_PRIO ); #if (OS_TASK_NAME_SIZE >= 20) OSTaskNameSet(APP_TASK_LED_PRIO, (INT8U *)"led_toggle", &os_err); #endif/*****************************************************************************************/ #if 0 //task 1/*****************************************************************************************/ os_err = OSTaskCreate(app_task1, (void*)0, &gTask1[APP_TASK1_STK_SIZE-1], APP_TASK1_PRIO); /*****************************************************************************************/ //task 2/*****************************************************************************************/ os_err = OSTaskCreate(app_task2, (void*)0, &gTask2[APP_TASK2_STK_SIZE-1], APP_TASK2_PRIO); /*****************************************************************************************/ //task 3/*****************************************************************************************/ os_err = OSTaskCreate(app_task3, (void*)0, &gTask3[APP_TASK3_STK_SIZE-1], APP_TASK3_PRIO);/*****************************************************************************************/ #endif}