历史上的今天
返回首页

历史上的今天

今天是:2024年09月30日(星期一)

正在发生

2019年09月30日 | USART 串口 DMA 发送和接收

2019-09-30 来源:eefocus

串口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中断。


USART 和 DMA 硬件初始化配置


/*--- LumModule Usart Config ---*/


#define LUMMOD_UART                      USART3


#define LUMMOD_UART_GPIO                 GPIOC


#define LUMMOD_UART_CLK                  RCC_APB1Periph_USART3


#define LUMMOD_UART_GPIO_CLK        RCC_APB2Periph_GPIOC


#define LUMMOD_UART_RxPin               GPIO_Pin_11


#define LUMMOD_UART_TxPin               GPIO_Pin_10


#define LUMMOD_UART_IRQn                USART3_IRQn


#define LUMMOD_UART_DR_Base                  (USART3_BASE + 0x4)  //0x40013804


 


#define LUMMOD_UART_Tx_DMA_Channel      DMA1_Channel2


#define LUMMOD_UART_Tx_DMA_FLAG         DMA1_FLAG_GL2//DMA1_FLAG_TC2 | DMA1_FLAG_TE2 


#define LUMMOD_UART_Tx_DMA_IRQ          DMA1_Channel2_IRQn


 


#define LUMMOD_UART_Rx_DMA_Channel      DMA1_Channel3


#define LUMMOD_UART_Rx_DMA_FLAG         DMA1_FLAG_GL3//DMA1_FLAG_TC3 | DMA1_FLAG_TE3 


#define LUMMOD_UART_Rx_DMA_IRQ      DMA1_Channel3_IRQn


 


 


void Uart_Init(void)


{


    NVIC_InitTypeDef NVIC_InitStructure;


    GPIO_InitTypeDef GPIO_InitStructure;


    USART_InitTypeDef USART_InitStructure;


 


    /* System Clocks Configuration */


//= System Clocks Configuration ====================================================================//


   


    /* Enable GPIO clock */


    RCC_APB2PeriphClockCmd(LUMMOD_UART_GPIO_CLK ,  ENABLE ); // 开启串口所在IO端口的时钟


    /* Enable USART Clock */


    RCC_APB1PeriphClockCmd(LUMMOD_UART_CLK, ENABLE); // 开始串口时钟


   


   


//=NVIC_Configuration==============================================================================//


 


    /* Configure the NVIC Preemption Priority Bits */


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);


 


    /* Enable the DMA Interrupt */


    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_Tx_DMA_IRQ;   // 发送DMA通道的中断配置


    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     // 优先级设置


    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;


    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


    NVIC_Init(&NVIC_InitStructure);


 


    /* Enable the USART Interrupt */


    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_IRQn;     // 串口中断配置


    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;


    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;


    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


    NVIC_Init(&NVIC_InitStructure);


   


//=GPIO_Configuration==============================================================================//


 


    GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);  // 我这里没有用默认IO口,所以进行了重新映射,这个可以根据自己的硬件情况配置选择


   


    /* Configure USART3 Rx as input floating */


    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 串口接收IO口的设置


    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_RxPin;


    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);


 


    /* Configure USART3 Tx as alternate function push-pull */


    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // 串口发送IO口的设置


    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 这里设置成复用形式的推挽输出   


    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_TxPin;


    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);


 


    DMA_Uart_Init();   // 串口 DMA 配置


 


    /* USART Format configuration ------------------------------------------------------*/


 


    USART_InitStructure.USART_WordLength = USART_WordLength_8b;    // 串口格式配置


    USART_InitStructure.USART_StopBits = USART_StopBits_1;


    USART_InitStructure.USART_Parity = USART_Parity_No;


    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;


    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;


 


    /* Configure USART3 */


    USART_InitStructure.USART_BaudRate = 115200;  //  波特率设置


    USART_Init(LUMMOD_UART, &USART_InitStructure);


 


    /* Enable USART3 Receive and Transmit interrupts */


    USART_ITConfig(LUMMOD_UART, USART_IT_IDLE, ENABLE);  // 开启 串口空闲IDEL 中断


   


    /* Enable the USART3 */


    USART_Cmd(LUMMOD_UART, ENABLE);  // 开启串口


    /* Enable USARTy DMA TX request */


    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送


    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收


}


 


 


void DMA_Uart_Init(void)


{


    DMA_InitTypeDef DMA_InitStructure;


   


    /* DMA clock enable */


    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 开启DMA1时钟


   


   


//=DMA_Configuration==============================================================================//


 


/*--- LUMMOD_UART_Tx_DMA_Channel DMA Config ---*/


 


    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);                           // 关DMA通道


    DMA_DeInit(LUMMOD_UART_Tx_DMA_Channel);                                 // 恢复缺省值


    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口发送数据寄存器


    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Tx_Buf;         // 设置发送缓冲区首地址


    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 设置外设位目标,内存缓冲区 ->外设寄存器


    DMA_InitStructure.DMA_BufferSize = LUMMOD_TX_BSIZE;                     // 需要发送的字节数,这里其实可以设置为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_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式


    DMA_Init(LUMMOD_UART_Tx_DMA_Channel, &DMA_InitStructure);               // 写入配置


    DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);                                 // 清除DMA所有标志


DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // 关闭DMA


    DMA_ITConfig(LUMMOD_UART_Tx_DMA_Channel, DMA_IT_TC, ENABLE);            // 开启发送DMA通道中断


   


/*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/


 


    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);                           // 关DMA通道


    DMA_DeInit(LUMMOD_UART_Rx_DMA_Channel);                                 // 恢复缺省值


    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口接收数据寄存器


    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf;         // 设置接收缓冲区首地址


    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                      // 设置外设为数据源,外设寄存器 -> 内存缓冲区


    DMA_InitStructure.DMA_BufferSize = LUMMOD_RX_BSIZE;                     // 需要最大可能接收到的字节数


    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的

推荐阅读

史海拾趣

Capital Advanced Technologies公司的发展小趣事

Capital Advanced Technologies(简称CAT)的创立,源于创始人对电子行业未来发展的深刻洞察。在电子科技迅猛发展的背景下,CAT应运而生,致力于为客户提供先进的电子技术和解决方案。公司初期,CAT便以高标准的研发实力和生产技术,迅速在市场中占据了一席之地。其独特的电路设计、精确的生产工艺和严格的品质控制,使得CAT的产品在市场上获得了良好的口碑。

中移物联网(Chinamobile)公司的发展小趣事

中移物联网在智能终端产品的创新方面也取得了显著成果。公司设计并生产了物联网专用模组和芯片,打造了智能组网、智能安防、智能家居、智能穿戴等行业终端。这些产品在市场上受到了广泛欢迎,特别是在智能家居和智能穿戴领域,中移物联网的产品凭借其高性能和稳定性,赢得了众多消费者的青睐。

福斯特(FIRST)公司的发展小趣事
ESP故障灯亮起,表示ESP系统可能存在问题。
Axiomtek公司的发展小趣事
确认传感器是否损坏或连接不良。
华大北斗(Allystar)公司的发展小趣事
具有强大的错误检测和纠错能力。
First Switchtech公司的发展小趣事

在追求商业成功的同时,First Switchtech公司(或类似公司)也始终关注可持续发展和社会责任。公司致力于环保产品的研发和生产,推出了多款符合国际环保标准的电子开关产品。同时,公司还积极参与公益事业,通过捐赠资金、技术支持等方式帮助贫困地区改善教育条件、提升医疗水平等。这些举措不仅提升了公司的社会形象,也促进了公司与社会的和谐共生。

需要注意的是,以上故事是基于电子行业普遍趋势和可能发展路径的构想,并非直接针对First Switchtech公司的具体事实。如需了解该公司的真实发展情况,建议直接访问其官方网站或查阅相关新闻报道。

问答坊 | AI 解惑

本文介绍目前性价比最高的先进锂电池充电管理芯片CN3052/CN3056

1 引言 CN305X系列是美国硅谷留学回来创办的半导体公司生产的先进锂电池充电管理芯片,CN305X系列芯片适合单节(4.1V或4.2V)和锂聚合物(Li-Pol)电池的充电需要,同时根据不同的应用提供了SOP和MSOP的可选封装形式,利用该芯片设计的充电器外围电路 ...…

查看全部问答>

提供一个下载期刊论文的资源

http://qbs.hznet.com.cn/bbs/ 1.杭州IP的直接进就可以了; 2.非杭州IP的用户请搜索一个可用的杭州代理IP,然后进去下载。…

查看全部问答>

50高分求助:CE软件盘为何无法输入

自己编写了一个软键盘,在模拟器上可以输入,可拿到2440板子上就不行了,不知是什么原因,下面是源代码,请高手帮我看看: HWND m_hFocus; CKeyboardDlg *pCharKeyboardDlg; CNumKeyboardDlg *pNumKeyboardDlg; //////////////////////////// ...…

查看全部问答>

问个udp广播的问题

我的设备有两个网卡驱动,一个是物理网口驱动,另一个是虚拟网口的驱动,物理网口的ip是192.168.110.4 ,虚拟网口的ip是192.168.11.6.我写了个udp广播的程序,发送广播,但是每次都是从物理网口发出去的,我怎样才能让他从虚拟网口发出去呢?是不是我的程序 ...…

查看全部问答>

节能灯及日光灯整流器用振荡磁环的选择

节能灯及日光灯整流器用振荡磁环的选择 背影子 时间:2010-08-23 转载  现节能灯和电子日光灯的应用已普及,但真正节能和质量好的却不是那么好做,关健是磁性材料的选择和使用上存在问题,这里主要谈一下脉冲变压器磁环,这是 ...…

查看全部问答>

DIY区能不能讨论一下开个机器人的专区?

不知道坛子里的人对机器人是否感兴趣?如果感兴趣的人多了,我们也可以组织对机器人进行DIY呀!~…

查看全部问答>

给我的16*64点阵电子钟穿衣服

2008年的时候曾经制作过16*64点阵屏幕,同时也制作出一个电子钟 一直没有找到合适的外壳,目前终于发现了合适的外壳,把我的16*64电子钟完善了一下     这个铝外壳成本不底哦, 传图片     [ 本帖最后由 飞雪008 于 20 ...…

查看全部问答>

win7应该安装ISE和modelsim的那个版本啊???????

win7系统安装ISE和modelsim,应该安装哪个版本啊?求助高手啊!!!!!…

查看全部问答>

现在做数字电源用什么IC好噢?

大家好!现在做数字电源用什么IC好噢?Ti又出了个UCD3138,可惜里面的原理图都看不清楚。…

查看全部问答>

Beaglebone怎么操作八位并口?

在Linux中当然可以一位一位地写,但是怎么直接操作一个8位并口呢?…

查看全部问答>