历史上的今天
返回首页

历史上的今天

今天是:2025年04月15日(星期二)

正在发生

2018年04月15日 | RT-Thread 串口DMA使用笔记--STM32F207

2018-04-15 来源:eefocus

RT-Thread学习笔记八

                           --------USART DMA方式发送
老规矩,首先说一下我的配置
STM32F207IGT6   MDK   RTT 1.1.0 

使用UART2作为finsh组件,UART3作为串口输出
/* register uart2 */
rt_hw_serial_register(&uart2_device,"uart2",RT_DEVICE_FLAG_RDWR| RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,&uart2);
配置成中断接收,数据流发送。

/* register uart3 */
rt_hw_serial_register(&uart3_device,"uart3",RT_DEVICE_FLAG_RDWR| RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_TX,&uart3);
配置成中断接收,DMA发送

相信大家已经能够正常使用finsh组件了,所以小弟就不献丑了,呵呵!
shaolin前辈写的finsh的使用
连接1

rt-thread下的串口驱动程序分析
连接2
主要写一下UART3 使用DMA发送
#ifdef RT_USING_UART3
struct stm32_serial_int_rx uart3_int_rx;
struct stm32_serial_dma_tx uart3_dma_tx;
struct stm32_serial_device uart3 =
{
        USART3,
        &uart3_int_rx,
        &uart3_dma_tx
};
struct rt_device uart3_device;
#endif

我仍然按照usart.c中rt_hw_usart_init()的顺序进行分析

/************************************ ******************************/
首先时钟配置RCC_Configuration();
#ifdef RT_USING_UART3
        /* Enable USART3 and GPIOC clocks */
        RCC_AHB1PeriphClockCmd(UART3_APBPeriph_GPIOX, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APBPeriph_UART3, ENABLE);
        RCC_AHB1PeriphClockCmd(RCC_APBPeriph_UART3_DMA,ENABLE);        
#endif
配置参数时都采用宏定义,如UART3_APBPeriph_GPIOX,RCC_APBPeriph_UART3,之所以不直接采用ST官方的参数,是因为这样不仅方便以后程序的移植,而且不容易出错。

#define UART3_GPIO_RX                GPIO_Pin_11
#define UART3_GPIO_TX                GPIO_Pin_10
#define UART3_GPIO                        GPIOC
#define UART3_APBPeriph_GPIOX        RCC_AHB1Periph_GPIOC
#define UART3_TX_PinSource GPIO_PinSource10
#define UART3_RX_PinSource GPIO_PinSource11
#define RCC_APBPeriph_UART3        RCC_APB1Periph_USART3
#define RCC_APBPeriph_UART3_DMA RCC_AHB1Periph_DMA1
#define UART3_TX_DMAy_Streamx DMA1_Stream3
#define UART3_TX_DMA_Channel DMA_Channel_4
#define UART3_TX_DMA_IRQHandler DMA1_Stream3_IRQn
#define UART3_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
//#define UART3_RX_DMAy_Streamx DMA1_Stream1
//#define UART3_RX_DMA_Channel DMA_Channel_4

/************************************ ******************************/
然后进行管脚配置GPIO_Configuration();
#ifdef RT_USING_UART3
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;        
        GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
        GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;

        GPIO_InitStruct.GPIO_Pin=UART3_GPIO_TX;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(UART3_GPIO,&GPIO_InitStruct);
        
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Pin=UART3_GPIO_RX;        
        GPIO_Init(UART3_GPIO,&GPIO_InitStruct);
        
        GPIO_PinAFConfig(UART3_GPIO, UART3_TX_PinSource, GPIO_AF_USART3);
        GPIO_PinAFConfig(UART3_GPIO, UART3_RX_PinSource, GPIO_AF_USART3);
#endif

/************************************ ******************************/
接着中断配置NVIC_Configuration();
#ifdef RT_USING_UART3
        /* Enable the USART3 Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        
        /* Enable the DMA1 Stream4 Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = UART3_TX_DMA_IRQHandler;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

这里需要注意说明的是STM32F2系列和STM32F1系列的DMA配置有些不同,F1系列配置时要注意DMAy_Channelx,F2系列用了另外一个名字DMAy_Streamx
DMAy_Channelx,:        where y can be 1 or 2 to select the DMA and x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel.

DMAy_Streamx,:        where y can be 1 or 2 to select the DMA and x can be 0 to 7 to select the DMA Stream

关于STM32F207的DMA有篇博客写的比较详细,大家可以参考一下。
连接3


/************************************ ******************************/
然后配置DMADMA_Configuration();
        DMA_InitTypeDef DMA_InitStructure;
        
DMA_InitStructure.DMA_Channel = UART3_TX_DMA_Channel;         
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* Specifies whether the Peripheral address register should be incremented or not */
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* Specifies whether the memory address register should be incremented or not */
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_FIFOMode = 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_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; 
        
        DMA_DeInit(UART3_TX_DMAy_Streamx);
        DMA_InitStructure.DMA_PeripheralBaseAddr = USART3_DR_Base; 
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)0;   
DMA_InitStructure.DMA_BufferSize = 1;         
        DMA_Init(UART3_TX_DMAy_Streamx,&DMA_InitStructure);
//        DMA_Cmd(UART3_TX_DMAy_Streamx, ENABLE);/* move to rt_serial_enable_dma() by RTT */
        
        DMA_ITConfig(UART3_TX_DMAy_Streamx, DMA_IT_TC | DMA_IT_TE, ENABLE);
        DMA_ClearFlag(UART3_TX_DMAy_Streamx, UART3_TX_DMA_FLAG_TCIF);

/************************************ ******************************/
最后进行UART的配置
#ifdef RT_USING_UART3
        USART_DeInit(USART3);
        USART_InitStructure.USART_BaudRate            = 19200;
        USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits            = USART_StopBits_1;
        USART_InitStructure.USART_Parity            = USART_Parity_No ;
        USART_InitStructure.USART_HardwareFlowContro=USART_HardwareFlowControl_None;
        USART_InitStructure.USART_M = USART_Mode_Rx | USART_Mode_Tx;

        USART_Init(USART3, &USART_InitStructure);

        uart3_dma_tx.dma_channel= UART3_TX_DMAy_Streamx;
        
        /* register uart3 */
        rt_hw_serial_register(&uart3_device, "uart3",
                RT_DEVICE_FLAG_RDWR|RT_DEVICE_FLAG_INT_RX|RT_DEVICE_FLAG_DMA_TX,&uart3);
                
        /* Enable USART3 DMA Tx request */
        USART_DMACmd(USART3, USART_DMAReq_Tx , ENABLE);        

        /* enable interrupt */
        USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
        
        USART_ClearFlag(USART3,USART_FLAG_TXE);
#endif

uart3_dma_tx.dma_channel= UART3_TX_DMAy_Streamx;这条语句一定要当心,这里不是配置dma_channel,因为在seria.c中rt_serial_enable_dma()进行DMA的使能和失能
例如DMA_Cmd(dma_channel, ENABLE);
但是st的F2系列的库函数中DMA配置的是Stream,而不是channel
DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)

到这里UART3的初始化就OK了,再进入中断函数stm32f2xx_it.c配置

/*********************DMA中断******************************/
#define UART3_TX_DMAy_Streamx DMA1_Stream3
#define UART3_TX_DMA_IT_TCIF DMA_IT_TCIF3
#define UART3_TX_DMA_IT_TEIF DMA_IT_TCIF3
#define UART3_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
#define UART3_TX_DMA_FLAG_TEIF DMA_FLAG_TCIF3
void DMA1_Stream3_IRQHandler(void)
{
#ifdef RT_USING_UART3
    extern struct rt_device uart3_device;
          extern void rt_hw_serial_dma_tx_isr(struct rt_device *device);

    /* enter interrupt */
    rt_interrupt_enter();

    if(DMA_GetITStatus(UART3_TX_DMAy_Streamx,UART3_TX_DMA_IT_TCIF))
    {
      /* transmission complete, invoke serial dma tx isr */
      rt_hw_serial_dma_tx_isr(&uart3_device);
    }
    /* clear DMA flag */
    DMA_ClearFlag(UART3_TX_DMAy_Streamx,DMA_FLAG_TCIF3 | UART3_TX_DMA_FLAG_TEIF);

    /* leave interrupt */
    rt_interrupt_leave();
#endif
}

在中断函数中,大家一定要注意DMA_GetITStatus() 和 DMA_GetFLAGStatus(),不能混淆了,我就吃过亏。一旦弄错了,DMA中断程序不正常,就导致了DMA只能发送一次。

在APPTask.c中创建了一个任务,进行发送。


void usart_tx_thread_entry(void *p)
{
        char tx_buf[]="hello pc!\r\n";
        u8 datalen;
        while(1)
        {
                datalen = strlen(tx_buf);
                dev_uart3->write(dev_uart3,0,tx_buf,datalen);
                rt_thread_delay(RT_TICK_PER_SECOND*2);
        }
        
}
串口输出


因为我刚接触RTT系统,所以会有很多考虑不周全的方面,请大家指出,呵呵!2013.1.6
因为我不能发连接,所以文中提到连接地方到放到了附件pdf中


推荐阅读

史海拾趣

EOZ Secme公司的发展小趣事

在电子产品市场竞争日益激烈的背景下,EOZ Secme始终坚守品质至上的原则。公司从原材料采购、生产流程到售后服务都进行严格把控,确保每一件产品都符合高标准的质量要求。这种对品质的执着追求让EOZ Secme的产品在市场上赢得了良好的口碑,也使其品牌价值得到了显著提升。

Chip Technologies Inc公司的发展小趣事

随着电子行业的竞争加剧,Chip Technologies Inc面临着来自各方面的挑战。为了应对这些挑战,公司及时调整了发展策略,加大了在研发和市场拓展方面的投入。同时,公司还加强了内部管理,提升了生产效率和质量水平。这些举措使得公司在激烈的市场竞争中保持了稳健的发展态势。

Hirect公司的发展小趣事
经过晶闸管调制后的电压波形可能含有谐波成分,需要通过滤波电路进行滤波处理,得到平滑的输出电压。
AR RF/Microwave Instrumentation公司的发展小趣事

随着科技的不断发展,AR RF/Microwave Instrumentation意识到要想在激烈的市场竞争中保持领先地位,必须不断进行技术创新。公司投入大量资源进行研发,成功推出了一系列具有创新性的产品,如宽带高功率放大器、固态射频放大器等。这些产品不仅性能卓越,而且能够满足不同客户的需求,进一步巩固了公司在行业内的地位。

DCX-CHOL Enterprises公司的发展小趣事

随着公司规模的扩大和市场竞争的加剧,DCX-CHOL Enterprises意识到品质管理的重要性。公司投入大量资源提升生产线自动化水平,引进先进的品质检测设备,并建立了严格的质量管理体系。这些措施有效地提高了产品的品质稳定性和可靠性,赢得了客户的信赖。同时,公司还注重员工培训和技能提升,培养了一支高素质、专业化的技术和管理团队。

HTSEMI( Jin Yu Semiconductor )公司的发展小趣事

为了加速国际化进程,金宇半导体于2020年制定了详细的国际化战略。公司首先在欧洲设立了研发中心,以吸引更多国际顶尖人才加入。随后,金宇半导体又通过一系列并购整合动作,收购了多家在特定领域具有领先技术的海外企业。这些并购不仅增强了金宇半导体的技术实力和市场影响力,还为其在全球范围内的业务拓展提供了有力支持。通过国际化战略的实施,金宇半导体正逐步成为一家具有全球竞争力的半导体企业。

问答坊 | AI 解惑

我在汇丰IT找工作的经历

汇丰今天发offer,我先恭喜今天收到offer的电子系兄弟,文壮,传艺,还有数学系的铭楷等,祝暂时没收到的xdjm很快就收到心仪的offer。下面把我的汇丰经历跟大家分享一下。      汇丰IT是我开始找工作时的第一目标,因为有好几 ...…

查看全部问答>

求助:KEIL能单步执行程序,但是不能自动运行全部

一开始用了个最简单的点亮LED来实验,编译通过,无错无警告 DEBUG时用STEP OVER单步执行没问题,但是想用GO自动运行就没法 估计是哪里设置没设对,但就是不清楚,哪位朋友之前遇到这种问题过?郁闷 [ 本帖最后由 skyjeankk 于 2010-1-19 11:10 编 ...…

查看全部问答>

ISD1100/1420

     各位兄弟,我现在有块电路板,AT89C52单片机 与 ISD1110 语音芯片相片相连接,其中 ISD1110 只有A0、A4、A6、 A7、PLAYE 与 AT89C52 连接,请问怎么样才可以根据需要把语音芯片中的某段语音播放出来。万分感谢!…

查看全部问答>

DMA寄存器

最近在看DMA的资料,发现一个问题,S3C2410的DMA的地址寄存器DISRC和DIDST标注的都是[30:0],即是31位,这里应该添物理地址,但物理地址不是32位的吗?这应该怎么解释?在这里弱问一下。…

查看全部问答>

如何在wince应用程序里给Form 添加背景图片

本人是用VS2005+wince5.0进行智能设备应用程序开发的,想问大家如何在wince应用程序里给Form 添加背景图片呀???是不是一定要用绘图函数来画图呀?…

查看全部问答>

热电偶和热电阻接线方式

热电偶和热电阻接线方式 热电偶都是两线制的 热电阻三线制的可以接 目前热电阻的引线主要有三种方式 ○1二线制:在热电阻的两端各连接一根导线来引出电阻信号的方式叫二线制:这种引线方法很简单,但由于连接导线必然存在引线电阻r,r大小与导线的 ...…

查看全部问答>

关于STM32模拟电源和模拟地的问题

我做了个STM32的板子,本来没打算使用芯片的模拟部分的功能,因此模拟电源的VCC和模拟地引脚都空着,没有接,结果发现JLINK根本连接不上,后来用飞线把模拟VCC和模拟地接上之后,JLINK才能连上,我不太明白其中的道理了,不知道哪位网友遇到过这种 ...…

查看全部问答>

请问你们的风火轮那买的

就是MCU 工程师炼成记记里面那个…

查看全部问答>

冒死推荐Hi-Fi功放电路分析!

    冒死推荐Hi-Fi功放电路分析,有玩功放的可以多多交流。 …

查看全部问答>