历史上的今天
返回首页

历史上的今天

今天是:2024年11月05日(星期二)

正在发生

2021年11月05日 | STM32F103ZET6 — USART

2021-11-05 来源:eefocus

串口通信介绍

UART串口通信,使用三线即可进行最基本的数据收发传送:

在数据线上的 Timing 遵循标准的串口通信协议,由起始位,数据,校验位,停止位组成,数据传输 LSB -> MSB:

板载 USART 资源介绍

当然,由于电平不一样,使用 RS232 标准进行串口数据传送,需要增加 MAX3232 进行电平转换,再接PC:

单板上的 T1IN 和 R1OUT 接到了 STM32 芯片的 USART1 的 TXD/RXD 管脚,故单板上使用了 USART1 来作为 RS232 和 PC 机进行数据传送:

USART 初始化配置

既然确定了使用了芯片上的 USART1,要正确使用该功能,需要进行如下配置:


1. 开启 USART1 时钟源,开启 GPIOA 组时钟源(因为使用 UASRT1之前,对 PA9/PA10 需要对管脚进行配置)


2. 复位 USART1  模块(使用之前,应当首先对该模块进行复位)


3. 配置管脚功能的 Remap


4. 配置 PA9/PA10 管脚,PA9 为 TXD,配置成为推挽输出,PA10 配置成为浮空输入

5. 配置串口的波特率(9600),数据长度(8bit),停止位(1bit),校验位(无),以及是否开启流控(无)


注意:波特率的配置,遵循一组计算公式(公式复杂),详见 STM32 的芯片手册


6. 配置 NVIC 控制器


7. 配置 USART1 的中断类型(即,工作过程中,会来些什么中断)


8. 使能 USART1 功能


void SK_UartInit(void)

{

    GPIO_InitTypeDef stGpioInit;

    USART_InitTypeDef stUsartInit;

    NVIC_InitTypeDef stNVIC;

 

    /* Step1: Open USART1 And GPIOA Clock */

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

 

    USART_DeInit(USART1);

 

    /* Step2: Config The Pin Remap */

    // According to the hardware diagram, just use UART1 On PA9 And PA10 in default

    GPIO_PinRemapConfig(GPIO_Remap_USART1, DISABLE);

 

    /* Step3: Config RXD/TXD Mode */

    // PA9 As TXD

    stGpioInit.GPIO_Pin = GPIO_Pin_9;

    stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;

    stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP; 

    GPIO_Init(GPIOA, &stGpioInit);

   

    // PA10 As RXD

    stGpioInit.GPIO_Pin = GPIO_Pin_10;

    stGpioInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOA, &stGpioInit);

 

    /* Step4: Reset USART1 before use it */

    USART_DeInit(USART1);

 

    /* Step5: Configure the UART Basic Settings */

    stUsartInit.USART_BaudRate = 9600;

    stUsartInit.USART_WordLength = USART_WordLength_8b;

    stUsartInit.USART_StopBits = USART_StopBits_1;

    stUsartInit.USART_Parity = USART_Parity_No;

    stUsartInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    stUsartInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

 

    USART_Init(USART1, &stUsartInit);

 

    // Configure RX Interrupt

    stNVIC.NVIC_IRQChannel = USART1_IRQn;

    stNVIC.NVIC_IRQChannelPreemptionPriority= 3 ;

    stNVIC.NVIC_IRQChannelSubPriority = 3;

    stNVIC.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&stNVIC);

 

#ifndef USART_USE_DMA

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

#endif

    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

 

    // Enable Usart1

    USART_Cmd(USART1, ENABLE);

}

在配置过程中,只打开了 RX 接收中断


TX 发送数据时刻,只需 polling 是否发送完成,在进行下一次的数据发送即可。


值得注意的是,数据收发都是使用了同一个DR寄存器,不同时刻,由硬件来进行区分。


USART 数据 TX 发送(Polling)

STM32 USART 数据发送是通过往 USART 的 DR 寄存器写值完成的,DR 寄存器支持每次写 1 Byte 的数据,每次写完数据后,硬件会将 DR 寄存器的值送到移位寄存器中,将数据发送出去。软件需要 polling 硬件的 TC (Transfer Complete)标志位,待 1 Byte 数据发送完成后,再次进行下一个数据的发送:


void SK_UsartSendChar(uint8_t ch)

{

    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);

    USART_SendData(USART1, ch);

}

 

void SK_UsartSendData(uint8_t *buf, uint32_t len)

{

    uint32_t i = 0;

 

    for (i = 0; i < len; i++)

        SK_UsartSendChar(buf[i]);

}

 

USART 数据 RX 接收(IRQ)

对于数据接收,使用 Polling 显然不是一种好办法,用中断的方式进行数据接收,接收的数据带简单的自定义格式:帧头+长度+Data的方式:


typedef struct {

    volatile uint32_t hdr_found;

    volatile uint32_t data_len;

    volatile uint32_t data_ready;

}SK_USART_RX_CTL_t;

 

SK_USART_RX_CTL_t g_stUsartRxCtl;

 

uint8_t g_SK_UsartRxDataBuf[SK_USART_RX_BUF_LEN] = {0};

 

/*

 * Protocol: 1st. Frame start at 0x5A

 *           2nd. Second is data length

 *           3rd. Data

*/

/*

void USART1_IRQHandler(void)

{

    uint8_t rx_data = 0;

    static uint8_t cnt = 0;

    if (USART_GetITStatus(USART1, USART_IT_RXNE))

    {

        rx_data = USART_ReceiveData(USART1);

    }

    if (SK_USART_RX_FRM_HEADER == rx_data && !g_stUsartRxCtl.hdr_found)

    {

        g_stUsartRxCtl.hdr_found++;

        return;

    }

    if (g_stUsartRxCtl.hdr_found && g_stUsartRxCtl.data_len == 0)

    {

        g_stUsartRxCtl.data_len = rx_data;

        if (g_stUsartRxCtl.data_len > SK_USART_RX_BUF_LEN)

            g_stUsartRxCtl.data_len = SK_USART_RX_BUF_LEN;

        return;

    }

    if (cnt < g_stUsartRxCtl.data_len)

    {

        g_SK_UsartRxDataBuf[cnt++] = rx_data;

        if (g_stUsartRxCtl.data_len == cnt)

        {

            g_stUsartRxCtl.data_ready = 1;

            cnt = 0;

            SK_SetLedStatus(SK_LED_1, SK_LED_ON);

            delay_ms(2000);

            SK_SetLedStatus(SK_LED_1, SK_LED_OFF);

            SK_UsartSendData(g_SK_UsartRxDataBuf, g_stUsartRxCtl.data_len);

        }

    }

}

 

USART 数据 RX 接收(IRQ + DMA)

虽然可以使用 IRQ 的方式进行一个 Byte 一个 Byte 的数据接收(中断),但您不觉得这会让 CPU 累死么?看着都费劲。


好嘛,STM32 UASRT 数据接收又不带 FIFO,不过没关系,用 DMA 放飞 CPU 吧!!


STM32 的 DMA1 支持 7个通道:

如上图所示:Ch4 用作 USART1_TX,Ch5 用作 USART1_RX。让 CPU 在歇一会。uploading.4e448015.gif?imageView2/2/w/550转存失败重新上传取消


USART DMA 配置


DMA 指的是 (Direct Memory Access)直接内存存取,不经过 CPU。STM32 的 DMA 支持外设到内存,内存到外设,以及内存到内存。只要咱们告诉 DMA 控制器,从什么地方去取外设数据,数据有多少,数据宽度是多少,以及将数据放置到内存的什么地方,它便可以带你飞。


当然,DMA 也支持中断的配置,能够配置成为数据传送一半的时候来中断,or,数据传送完来中断,从各方面解决了您的烦恼。让您无忧无虑进行数据传送。


回到正题上来,配置 USART DMA要有入下几个步骤:


1. 开启 DMA1 时钟(这不废话么)


2. 配置 NVIC,并使能(也是废话)


3. 复位 DMA1 的 Ch4/Ch5


4. 设定外设地址为 USART1 的 DR 寄存器,即数据寄存器


5. 设置接收数据的内存地址(本地的一个缓存 RX BUF指针)


6. 设置数据方向为 USART1 的 DR 寄存器 ----> 内存


7. 设置 DMA 传输的数据大小(最大 65536)


8. 设置关闭外设地址自动增加


9. 设置启用缓存 BUF 地址自动增加 (数据传来后,自动存在本地 RX BUF 并指针递增)


10. 配置外设传输数据宽度为 8bit (USART DR 寄存器就 8bit)


11. 配置本地缓存数据宽度为 8bit


12. 配置 DMA 传输模式为 one shot(即传输完一次后,就停止了,也可以配置成为循环模式)


13. 设置 DMA CH15 的优先级


14. 关闭 memory to memory(废话,使用的是外设到内存的数据传输)


15. 开启 DMA1 CH15


16. 使能数据传输完成的 DMA1 中断


17. 在 USART 寄存器中,开启 RX DMA 请求(此项是在 USART 寄存器中进行配置)


好啦,此刻配置基本完成:


void SK_UsartDmaInit(void)

{

    DMA_InitTypeDef stDMA_InitStructCh4;

    DMA_InitTypeDef stDMA_InitStructCh5;

 

    /// Step 1 : Open the DMA1 Clock

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

 

    // NVIC Config

    DMA_NVIC_Config();

 

    /// Step 2 : Reset DMA1_CH4(For USART1 TX) and DMA1_CH5(For USART1 RX)

    DMA_DeInit(DMA1_Channel4);

 

    // Configure the USART1 TX DMA Transfer for DMA CH4

    // Configure the Peripheral address

    stDMA_InitStructCh4.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;

    // Configure the TX Data Buffer address

    stDMA_InitStructCh4.DMA_MemoryBaseAddr     = (uint32_t)g_SK_UsartTxDataBuf;

    // Configure the data direct: Memory to Peripheral

    stDMA_InitStructCh4.DMA_DIR                = DMA_DIR_PeripheralDST;

    // Configure the data Len

    stDMA_InitStructCh4.DMA_BufferSize         = SK_USART_TX_BUF_LEN;

    // Configure the Peripheral Address auto add (disable)

    stDMA_InitStructCh4.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;

    // Configure the Memory Address auto add (enable)

    stDMA_InitStructCh4.DMA_MemoryInc          = DMA_MemoryInc_Enable;

    // Configure the Peripheral Data Size = 1 byte

    stDMA_InitStructCh4.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    // Configure the Memory Data Size = 1 byte

    stDMA_InitStructCh4.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;

    // Configure Normal mode

    stDMA_InitStructCh4.DMA_Mode               = DMA_Mode_Normal;

    // Configure Priority as medium

    stDMA_InitStructCh4.DMA_Priority           = DMA_Priority_Medium;

    // Disable memory to memory

    stDMA_InitStructCh4.DMA_M2M                = DMA_M2M_Disable;

    // Config DMA1 CH4

    DMA_Init(DMA1_Channel4, &stDMA_InitStructCh4);

    // Enable Ch4

    DMA_Cmd(DMA1_Channel4, ENABLE);

    // Enable IRQ when transfer finished

    DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);

 

    /// Step 3: Reset DMA1_CH5(For USART1 RX)

    DMA_DeInit(DMA1_Channel5);

    // Configure the USART1 RX DMA for DMA CH5

    // Configure the Peripheral address

    stDMA_InitStructCh5.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;

    // Configure the TX Data Buffer address

    stDMA_InitStructCh5.DMA_MemoryBaseAddr     = (uint32_t)g_SK_UsartRxDataBuf;

    // Configure the data direct: Memory to Peripheral

    stDMA_InitStructCh5.DMA_DIR                = DMA_DIR_PeripheralSRC;

    // Configure the data Len

    stDMA_InitStructCh5.DMA_BufferSize         = SK_USART_RX_BUF_LEN;

    // Configure the Peripheral Address auto add (disable)

    stDMA_InitStructCh5.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;

推荐阅读

史海拾趣

EXXELIA Group公司的发展小趣事

关于EXXELIA Group公司在电子行业的发展,由于字数和篇幅限制,我无法直接给出5个各500字以上的相关故事。但我可以为您提供一些关于EXXELIA Group公司的发展历程和关键事件,您可以从中挑选和拓展成故事。

  1. EXXELIA的创立与早期发展
    EXXELIA Group,原名Temex,起初是一家专注于被动元件和精密子系统制造的公司。随着电子行业的快速发展,EXXELIA看到了市场对高品质被动元件的需求,并逐渐扩大产品线,包括电容器、电感器、变压器等。公司通过不断的技术创新和严格的质量控制,逐渐在行业中树立了良好的口碑。
  2. 技术创新与突破
    在某个关键时期,EXXELIA投入大量资源进行技术研发,成功开发出一种新型的高性能电容器。这项技术创新显著提高了电子产品的性能和稳定性,受到了市场的热烈欢迎。这一成功使得EXXELIA在电子行业中的地位更加稳固。
  3. 市场拓展与合作
    随着全球电子市场的不断扩大,EXXELIA开始寻求国际市场的发展机会。公司与多家国际知名企业建立了合作关系,共同开发新产品,拓展市场份额。这些合作为EXXELIA带来了更多的业务机会,也进一步提升了公司的品牌影响力。
  4. 应对市场挑战
    在电子行业竞争日益激烈的环境下,EXXELIA也面临着各种市场挑战。为了应对这些挑战,公司不断调整市场策略,优化产品结构,加强与客户的沟通与合作。通过这些努力,EXXELIA成功稳住了市场地位,并持续为客户提供优质的产品和服务。
  5. 未来展望与战略布局
    面对未来电子行业的发展趋势,EXXELIA积极进行战略布局。公司将继续加大技术研发投入,推动产品创新;同时,加强与国际合作伙伴的交流与合作,共同探索新的市场机会。通过这些举措,EXXELIA期望在未来几年内实现更快速的发展。

请注意,以上内容主要基于公开信息和行业常识进行编写,具体细节可能需要根据实际情况进行调整和完善。希望这些信息能为您提供一些灵感和参考。

芯联(CHIPLINK)公司的发展小趣事

面对激烈的市场竞争和不断上升的成本压力,芯联集成通过优化成本结构、提高生产效率等措施,成功降低了生产成本。同时,公司还加强了与供应商的合作,实现了供应链的优化管理。这些努力使芯联集成在保持产品质量的同时,降低了产品售价,提升了盈利能力。

General Diode Corp公司的发展小趣事
通过电磁力驱动送丝机构,实现焊丝的均匀、稳定送进,保证焊接过程的连续性。
Flamar公司的发展小趣事

随着电子行业的全球化趋势日益明显,Flamar公司积极实施国际化战略,通过设立海外研发中心、生产基地和销售网络,不断拓展全球市场。公司先后在欧洲、亚洲等地建立分支机构,与当地企业建立紧密的合作关系,共同推动电子技术的创新与应用。通过国际市场的开拓,Flamar公司不仅实现了业务规模的快速增长,还进一步提升了品牌的国际知名度和影响力。

Herotek Inc公司的发展小趣事

在电子元件的制造过程中,粘接技术是关键环节之一。Henkel凭借其深厚的技术积累和创新能力,在电子元件粘接领域取得了显著成就。其研发的粘合剂产品具有优异的粘接强度、耐温性能和耐化学腐蚀性能,能够满足各种复杂工况下的使用需求。例如,在汽车电子领域,Henkel的粘合剂产品被广泛应用于传感器、控制器等关键部件的粘接固定中,确保了汽车电子系统的稳定性和可靠性。

南京国博公司的发展小趣事

国博电子在军用领域同样占据重要地位。作为参与国防重点工程的重要单位,公司为陆、海、空、天等各型装备配套了大量的关键产品,确保了以有源相控阵T/R组件为代表的关键军用元器件的国产化自主保障。通过军民融合的发展模式,国博电子不仅为国防建设提供了有力支持,也促进了自身技术的不断升级和产品的持续优化。

问答坊 | AI 解惑

给手机增加蓝牙功能设计指南

本帖最后由 jameswangsynnex 于 2015-3-3 19:57 编辑 手机蓝牙功能设计攻略,初步介绍及具体实现方案 …

查看全部问答>

实用电子电路设计制作例解

实用电子电路设计制作例解…

查看全部问答>

AVR ATMega16 步进电机驱动程序,已经调试通过【转】

这两天做步进电机的驱动成功,现将程序共享,愿起到抛砖引玉的作用。 此程序可驱动五引线步进电机,通过按键可控制步进电机的转速、方向、三种驱动方式,三种驱动方式分别为:单四拍驱动、双四拍驱动、单双八拍驱动,其中以单双八拍驱动转速最慢。 ...…

查看全部问答>

笔记本电池专用料

FDS6690A  FAIRCHILD  09+   100K AO4468        AOS        09+   300K APM4435A   ANPEC      09+&nb ...…

查看全部问答>

电路板书架-原来电路也可以如此简约

电路板书架,简单,美观。 原来错综复杂的电路图也可以如此简约好看。 即体现了职业,又显示了品位。你是不是也想有一套? 安装图纸 …

查看全部问答>

基于FPGA的高阶数字锁相环的设计

上述代码经过在Quartus II上编译仿真后其波形如图2所示。     设计中适当选取K值特别的重要。如果K值偏大,这样计数器对少量噪声干扰不可能计满,就不会有进位或者借位脉冲,有利于抑制随机噪声;但就会使捕捉带减小,导致环路进入锁定 ...…

查看全部问答>

用SmsSendMessage发送,收到乱码

发送到普通手机上没问题,发送到SP提供商时软吗 int SendSMSForMobile(BOOL bSendConfirmation, BOOL bUseDefaultSMSC, LPCTSTR lpszSMSC, LPCTSTR lpszRecipient, LPCTSTR lpszMessage) {         BOOL bRet = FALSE; & ...…

查看全部问答>

出售团队

本团队共四人,一硬件(硬件部门总工),三软件(一软件部门总工、两底层、蓝牙和PC应用开发工程师、平均年龄28)。一直从事蓝牙、单片机和PC软件的开发。手里有几十个现成的项目。从事了3年的蓝牙耳机、DONGLE以及各种适配器的开发,该团队一起工 ...…

查看全部问答>

那里有DumpRom.exe工具软件下载啊?怎么提取BIN/NB0文件里面的某个文件?

那里有DumpRom.exe工具软件下载啊?怎么提取BIN/NB0文件里面的某个文件?…

查看全部问答>

pt1000的用法

本帖最后由 paulhyde 于 2014-9-15 09:01 编辑 哪位高手帮我讲解下这个电路图 每升高一度对应多少电压值的变化 如果有别的关于pt1000使用的电路图 更好  不胜感激  …

查看全部问答>