1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
串口DMA发送:
发送数据的流程: 前台程序中有数据要发送,则需要做如下几件事 1. 在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。 2. 将数据缓冲区内要发送的数据字节数赋值给发送DMA通道,(串口发送DMA和串口接收DAM不是同一个DMA通道) 3. 开启DMA,一旦开启,则DMA开始发送数据,说明一下:在KEIL调试好的时候,DMA和调试是不同步的,即不管Keil 是什么状态,DMA总是发送数据。 4. 等待发送完成标志位,即下面的终端服务函数中的第3点设置的标志位。或者根据自己的实际情况来定,是否要一直等待这个标志位,也可以通过状态机的方式来循环查询也可以。或者其他方式。 判断数据发送完成: 启动DMA并发送完后,产生DMA发送完成中断,在中断函数中做如下几件事: 1. 清 DMA 发送完成中断标志位 2. 关闭串口发送 DMA 通道 3. 给前台程序设置一个软件标志位,说明数据已经发送完毕 串口DMA接收: 接收数据的流程: 串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。 判断数据数据接收完成: 这里判断接收完成是通过串口空闲中断的方式实现,即当串口数据流停止后,就会产生 IDLE 中断。这个中断里面做如下几件事: 1. 关闭串口接收DMA通道,2点原因:1.防止后面又有数据接收到,产生干扰。2.便于DMA的重新配置赋值,下面第4点。 2. 清除DMA 所有标志位 3. 从DMA寄存器中获取接收到的数据字节数 4. 重新设置DMA下次要接收的数据字节数,注意,这里是给DMA寄存器重新设置接收的计数值,这个数量只能大于或者等于可能接收的字节数,否则当DMA接收计数器递减到0的时候,又会重载这个计数值,重新循环递减计数,所以接收缓冲区的数据则会被覆盖丢失。 5. 开启 DMA 通道,等待下一次的数据接收, 注意,对 DMA 的相关寄存器配置写入,如第 4 条的写入计数值,必须要在关闭 DMA 的条件进行,否则操作无效。 说明一下,STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。 /**********************************************************************************************纯属搬运***********************************************************************************************/ 上面文字性的分析已经写的很好了,我就不累赘了。原作者给了个例子,我移植到了 战舰上,就帖个工程吧。 再附送一个F207上的例子(同理F4),这个跟F1系列的差异还是蛮大的。 /******************************************************************************* * 函数名 : dma_uart2_init * 描述 : 初始化DMA串口2 * DMA1 * DMA_Channel_4 通道4 * DMA2_Stream4 数据流6 * 参数 : * 返回值 : 无 *******************************************************************************/ void dma_uart2_init(void) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* DMA clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // 开启DMA2时钟 //=DMA_Configuration==============================================================================// // DMA_Cmd(DMA2_Stream7, DISABLE); // 关DMA通道 DMA_DeInit(DMA1_Stream6); // 恢复缺省值 while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){}//等待DMA可配置 DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道4 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DR); // 设置串口发送数据寄存器 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)DMA_UART2_SendBuf; // 设置发送缓冲区首地址 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // 设置外设位目标,内存缓冲区 -> 外设寄存器 DMA_InitStructure.DMA_BufferSize = 0; // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不做增加调整,调整不调整是DMA自动实现的 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存缓冲区地址增加调整 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据宽度8位,1个字节 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 单次传输模式,不循环 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 优先级设置 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//DMA_FIFOMode_Disable DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA1_Stream6, &DMA_InitStructure); // 写入配置 /* Enable the DMA Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; // 发送DMA通道的中断配置 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; // 优先级设置 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE);// 开启DMA通道传输完成中断 USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);// 开启串口DMA发送 } /******************************************************************************* * 函数名 : uart2_init * 描述 : 初始化串口2 * 参数 : bound:波特率 * 返回值 : 无 *******************************************************************************/ void uart2_init(u32 bound)//初始化串口2 { //GPIO端口设置 NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOb时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//USART2 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);//重映射,TX GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);//重映射,RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //翻转速度 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //输入输出设置,输入/输出/复用/模拟 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //输出模式,开漏/推挽 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //输入模式,浮空/上拉/下拉 GPIO_Init(GPIOA, &GPIO_InitStructure); //USART 初始化设置 USART_InitStructure.USART_BaudRate = bound;//一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 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(USART2, &USART_InitStructure);//初始化串口 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级3,子优先级不能为0??? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器 USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 USART_Cmd(USART2, ENABLE);//使能串口 dma_uart2_init();//初始化串口DMA发送 } /******************************************************************************* * 函数名 : USART2_IRQHandler * 描述 : 串口2中断服务函数,接收采集板数据 * 参数 : 无 * 返回值 : 无 *******************************************************************************/ void USART2_IRQHandler(void)//串口2中断服务程序 { u8 c; if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)//接收中断 { USART_ClearITPendingBit(USART2, USART_IT_RXNE); LED2 = !LED2; c = USART_ReceiveData(USART2);//读取接收寄存器,读数据会清除中断 write_loop_2_buf(c); } } //DMA 发送应用源码 void DMA1_Stream6_IRQHandler(void) { static portBASE_TYPE xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6)) { // printf("DMA1中断rn"); DMA_ClearFlag(DMA1_Stream6, DMA_IT_TCIF6);// 清除标志 DMA_Cmd(DMA1_Stream6, DISABLE); // 关闭DMA通道 //发送信号量 xSemaphoreGiveFromISR(xSemaphoreHandle_DMA_USART2_SendFlag, &xHigherPriorityTaskWoken);//发送完成标志 if(xHigherPriorityTaskWoken == pdTRUE) //给出信号量使任务解除阻塞,如果解除阻塞的任务的优先级高于当前任务的优先级——强制进行一次任务切换 { portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } } } void DMA_USART2_SendData(u8 *buf, u16 size)//DMA串口2发送数据 { memcpy(DMA_UART2_SendBuf, buf, size);//拷贝到DMA缓冲区 DMA1_Stream6->NDTR = (u16)size; // 设置要发送的字节数目 // printf("发送字节数=%drn",size); DMA_Cmd(DMA1_Stream6, ENABLE); //开始DMA发送 xSemaphoreTake(xSemaphoreHandle_DMA_USART2_SendFlag, portMAX_DELAY);//使用信号量等待发送完成,无超时 串口DMA接收对于全双工模式挺适合,半双工模式实用性就一般了,不如中断加FIFIO操作性好。DMA+FIFO没有研究明白是否可行。 对于485有一个不能忽视的问题: 当DMA完成中断触发的时候,大约有2个字节还没有发送,一般延时2ms就差不多了。为了保险和适应不同波特率,我写成了查询+阻塞(OS延时)。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1767 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1069 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
727浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
567浏览 3评论
592浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 20:16 , Processed in 0.756002 second(s), Total 76, Slave 60 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号