一、USART的工作频率和波特率


看用户手册一上来就糊涂了,手册里面写的PCLK是什么?翻看手册第4章有关CMU章节。似乎这里说的PCLK就是PCLK1?手册是第一版,纰漏其实蛮多的。

在官方库函数验证了猜测,这里说的PCLK指的就是PCLK1:


en_result_t USART_SetBaudrate(M4_USART_TypeDef *USARTx,uint32_t u32Baudrate,float32_t *pf32Err)
{/* …… *//* Get USART clock frequency */u32UsartDiv = USART_DIV(USARTx);u32UsartClk = PCLK_FREQ / u32UsartDiv; /* …… */
}

在设置波特率的函数中找到了这么一句话u32UsartClk = PCLK_FREQ / u32UsartDiv;看起来是在计算USART的频率的,展开PCLK_FREQ这个宏得到:

#define PCLK_FREQ                                                              \
(   SystemCoreClock >> (READ_REG32_BIT(M4_CMU->SCFGR, CMU_SCFGR_PCLK1S) >> CMU_SCFGR_PCLK1S_POS))

果然,PCLK就是PCLK1

手册中没有说明USART的工作频率限制,所以理论上最高的波特率就是PCLK1 / 8(Bps)。计算公式为:

本例波特率定在19200,并不是特别高。根据公式,并考虑到过采样要求,可以把串口的工作频率调到16分频。确定下来后,开始初始化USART。

二、初始化USART1

本例TX为PA09,RX为PA10,其功能20对应USART1的TX和RX:

故本例使用UASRT1:

/* UART unit definition */
#define USART_FUNCTION_CLK_GATE         (PWC_FCG3_USART1)
/* UART RX/TX Port/Pin definition */
#define USART_RX_PORT                   (GPIO_PORT_A)   /* PH13: USART1_RX */
#define USART_RX_PIN                    (GPIO_PIN_10)
#define USART_RX_GPIO_FUNC              (GPIO_FUNC_20_USART1_RX)#define USART_TX_PORT                   (GPIO_PORT_A)   /* PH15: USART1_TX */
#define USART_TX_PIN                    (GPIO_PIN_09)
#define USART_TX_GPIO_FUNC              (GPIO_FUNC_20_USART1_TX)/* Enable peripheral clock */PWC_Fcg3PeriphClockCmd(USART_FUNCTION_CLK_GATE, Enable);/* Configure USART RX/TX pin. */GPIO_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_GPIO_FUNC, PIN_SUBFUNC_DISABLE);GPIO_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_GPIO_FUNC, PIN_SUBFUNC_DISABLE);

使能时钟,给USART1上电,配置PA9和PA10的功能为TX和RX。

/* UART unit definition */
#define USART_UNIT                      (M4_USART1)
#define USART_BAUDRATE                  (19200UL)
#define USART_DATA_BITS                 (8U)
#define USART_CHECK_BITS                (0U)
#define USART_STOP_BITS                 (1U)
#define USART_FRAME_BITS                (USART_DATA_BITS + USART_CHECK_BITS + \USART_STOP_BITS + (1U))
    const stc_usart_uart_init_t stcUartInit = {.u32Baudrate = USART_BAUDRATE,.u32BitDirection = USART_LSB,.u32StopBit = USART_STOPBIT_1BIT,.u32Parity = USART_PARITY_NONE,.u32DataWidth = USART_DATA_LENGTH_8BIT,.u32ClkMode = USART_INTERNCLK_OUTPUT,.u32PclkDiv = USART_PCLK_DIV16,.u32OversamplingBits = USART_OVERSAMPLING_8BIT,.u32NoiseFilterState = USART_NOISE_FILTER_DISABLE,.u32SbDetectPolarity = USART_SB_DETECT_FALLING,};if (Ok != USART_UartInit(USART_UNIT, &stcUartInit)){for (;;){}}

参考官方例程写的,这个代码风格跟TRM和ADC的例程不一样。

  • 波特率19200

  • 左对齐

  • 停止位1

  • 校验位无

  • 数据位8

  • 时钟模式后面说

  • 64分频

  • 8位过采样,这个不是很清楚,猜测比特率为192,000,USART工作频率为6,250,000,为其32倍,32是个8位数。

  • 不开滤波

  • 开始位检测方式为RX管脚低电平

时钟模式

当作UART使用,并使用内部时钟时,可以设置为00或者01,本例只有TX RX,没有配置CK脚,按道理可以配置成00。但是配置成00的话,CR1寄存器中的RTOF标志立不起来,串口也进不了TIMEOUT中断。例程里面用的是01。使用01后就没有这个故障,以后再找原因。

三、配置TIMEOUT中断

以常用的1起始位,8数据位,0校验位,1停止位来说,UART的一帧数据是10bit,其中有效的数据位为8bit,即一帧只有一字节。为了避免每收到一个字节就通过一次中断来进行处理,造成的频繁中断问题,一般会采用DMA接收方式。STM32提供了一个idle中断,它在RX空闲的时候触发,可以用来表示应用层的一帧数据发送完毕,我们可以在这个中断中将DMA的传输目的地地址复位。HC32F4A0的USART没有IDLE中断,取而代之的是一个灵活性更强但是使用起来也相对更复杂的TIMEOUT中断。

IDLE是在监测到数据接收后(即串口的RXNE位被置位)开始检测,当总线上在一个字节对应的周期内未再有新的数据接收时,触发空闲中断IDLE位就会被被硬件置1。这个触发条件时固定,一个周期没收到新数据就会触发。而TIMEOUT中断的空闲时长则允许用户自定义。想要使用该中断,甚至还必须需要引入TMR0这个外设来协助完成。





这里不详细介绍TMR0,只列出用以配合USART1的TIMEOUT中断的配置方式。
配置USART1的TIOMEOUT中断(调用了从STM32移植过来的MISC):

/* UART unit interrupt definition */
#define USART_RXTO_INT_SRC              (INT_USART1_RTO)
#define USART_RXTO_INT_IRQn             (Int002_IRQn)/* Setting up USART1 interrupts*/stc_irq_signin_config_t stcIrqSigninCfg;NVIC_InitTypeDef NVIC_InitStructure;  /* Register RX timeout IRQ handler && configure NVIC. */    stcIrqSigninCfg.enIRQn = USART_RXTO_INT_IRQn;stcIrqSigninCfg.enIntSrc = USART_RXTO_INT_SRC;stcIrqSigninCfg.pfnCallback = &USART_RxTimeout_IrqCallback;(void)INTC_IrqSignIn(&stcIrqSigninCfg);NVIC_InitStructure.NVIC_IRQChannel = USART_RXTO_INT_IRQn;      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;         NVIC_InitStructure.NVIC_IRQChannelCmd = Enable;NVIC_Init(&NVIC_InitStructure);/* Enable TX && RX && RX interrupt function */USART_FuncCmd(USART_UNIT, (USART_RX | USART_INT_RX | USART_TX | \USART_RTO | USART_INT_RTO), Enable);

配置TMR0

/* UART unit definition */
#define USART_FRAME_BITS                (USART_DATA_BITS + USART_CHECK_BITS + \USART_STOP_BITS + (1U))
/* Timer0 unit & channel definition */
#define TMR0_UNIT                       (M4_TMR0_1)
#define TMR0_CH                         (TMR0_CH_A)
#define TMR0_FUNCTION_CLK_GATE          (PWC_FCG2_TMR0_1)/*** @brief  Configure TMR0.* @param  None* @retval None*/
static void TMR0_Config(void)
{uint32_t u32CmpVal;stc_tmr0_init_t stcTmr0Init;PWC_Fcg2PeriphClockCmd(TMR0_FUNCTION_CLK_GATE, Enable);/* Clear CNTAR register for channel A */TMR0_SetCntVal(TMR0_UNIT, TMR0_CH, 0U);/* TIMER0 basetimer function initialize */(void)TMR0_StructInit(&stcTmr0Init);stcTmr0Init.u32ClockDivision = TMR0_CLK_DIV8;stcTmr0Init.u32ClockSource = TMR0_CLK_SRC_XTAL32;stcTmr0Init.u32HwTrigFunc = (TMR0_BT_HWTRG_FUNC_START | TMR0_BT_HWTRG_FUNC_CLEAR);if (TMR0_CLK_DIV1 == stcTmr0Init.u32ClockDivision){u32CmpVal = (USART_FRAME_BITS*3 - 4UL);}else if (TMR0_CLK_DIV2 == stcTmr0Init.u32ClockDivision){u32CmpVal = (USART_FRAME_BITS*3/2UL - 2UL);}else{u32CmpVal = (USART_FRAME_BITS*3 / (1UL << (stcTmr0Init.u32ClockDivision >> TMR0_BCONR_CKDIVA_POS)) - 1UL);}DDL_ASSERT(u32CmpVal <= 0xFFFFUL);stcTmr0Init.u16CmpValue =  (uint16_t)(u32CmpVal);(void)TMR0_Init(TMR0_UNIT, TMR0_CH, &stcTmr0Init);/* Clear compare flag */TMR0_ClearStatus(TMR0_UNIT, TMR0_CH);
}

RTB的起初按照STM32的IDLE来设置的,即10bit长度对应的周期数时长。结果后面实测时发现,在10bit,甚至20bit长度的等待时延下,进入中断后读取DMA的目标地址有时候不准确,放长到30bitUSART_FRAME_BITS*3长度时测试每次都可以读准。

配置DMA:

#define USART1_DMA_TRIGGER_SOURCE              (EVT_USART1_RI)DMA_SetTriggerSrc(M4_DMA1, USART1_DMA_CH, USART1_DMA_TRIGGER_SOURCE);

接收到一个字节就触发DMA接收。

#define USART1_DMA_SRC_ADDR                     ((uint32_t)(&M4_USART1->DR) + 2UL)
#define USART1_DMA_CH                          (DMA_CH0)
#define USART1_BUFFER_SIZE                     (512Ul)
__IO uint8_t USART1_R_data[USART1_BUFFER_SIZE] __attribute__ ((at(0x20001000)));stc_dma_init_t stcDmaInit;(void)DMA_StructInit(&stcDmaInit);stcDmaInit.u32IntEn = DMA_INT_DISABLE;stcDmaInit.u32BlockSize = 1UL;stcDmaInit.u32TransCnt = 0UL;stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT;stcDmaInit.u32DestAddr = (uint32_t)(&USART1_R_data[0]);stcDmaInit.u32SrcAddr = USART1_DMA_SRC_ADDR;stcDmaInit.u32SrcInc = DMA_SRC_ADDR_FIX;stcDmaInit.u32DestInc = DMA_DEST_ADDR_INC;(void)DMA_Init(M4_DMA1, USART1_DMA_CH, &stcDmaInit);DMA_Cmd(M4_DMA1, Enable);DMA_ChannelCmd(M4_DMA1, USART1_DMA_CH, Enable);

USART的32为数据寄存器DR的高16位保存接收数据信息,所以&M4_USART1->DR) + 2UL

中断服务函数:

/*** @brief  USART RX timeout IRQ callback.* @param  None* @retval None*/
static void USART_RxTimeout_IrqCallback(void)
{TMR0_Cmd(TMR0_UNIT, TMR0_CH, Disable);USART_ClearStatus(USART_UNIT, USART_CLEAR_FLAG_RTOF);  DMA_ChannelCmd(M4_DMA1, USART1_DMA_CH, Disable);uint32_t DMA_DestAddr = DMA_GetDestAddr(M4_DMA1, USART1_DMA_CH);uint8_t* p = (uint8_t*)DMA_DestAddr;*p = '\0';DMA_SetDestAddr(M4_DMA1, USART1_DMA_CH, (uint32_t)(&USART1_R_data[0]));DMA_ChannelCmd(M4_DMA1, USART1_DMA_CH, Enable);//RS485_SendData(USART1_R_data, 10);}
  • 关TMR01的通道A(每次由USART1硬件激活)
  • 清USART1中断标志
  • 关DMA通道
  • 读DMA目标地址指针,并在该处加上‘\0’截止符。
  • 重置DMA目标地址
  • 开启DMA通道。

四、USART发送单字节/多字节/字符串

/* 发送单字节 */
static void RS485_SendByte(uint8_t byte)
{USART_SendData(USART_UNIT, (uint16_t)byte);while (Reset == USART_GetStatus(USART_UNIT, USART_SR_TXE)){       }
}/* 发送多字节 */
void RS485_SendData(uint8_t* str, uint32_t len)
{uint32_t k=0;do {RS485_SendByte(*(str + k));k++;} while((k < USART1_BUFFER_SIZE)&&(k < len));while (Reset == USART_GetStatus(USART_UNIT, USART_FLAG_TC)){      }
}/* 发送字符串 */
void RS485_SendString(char* str)
{uint32_t k=0;do {RS485_SendByte(*(str + k));k++;} while((*(str + k) != '\0')&&(k < USART1_BUFFER_SIZE));while (Reset == USART_GetStatus(USART_UNIT, USART_FLAG_TC)){      }
}

很多EE出身的工程师包括我在内,在做嵌入式开发时着重与功能开发,软件细节上比较粗糙。在很多嵌入式软件里都可以看到开发者在中断中调用USART发送函数。但这么做是不被提倡的,如果确实有这个实时性的必要,至少要在死循环内加上一个超时异常。

华大半导体HC32F4A0笔记(三),RS485通信,使用串口USART1,DMA接收相关推荐

  1. 华大半导体HC32F4A0笔记(一),PWM输入捕获,使用TIM6

    一.启动时钟 PWC_Fcg2PeriphClockCmd(PWC_FCG2_TMR6_x, Enable); TMR6对应FCG2,根据PWM输入引脚来确定是TMR6_x(本例为TMR6_2).运行 ...

  2. 华大半导体HC32F4A0笔记(五),使用CMSIS-DSP库进行FFT运算

    一.开启FPU功能 点这个麻将牌四筒,展开CMSIS,把DSP勾了. 点开后 然后点这个锤子 No Auto Includes的勾不要打,让它自动include,因为CMSIS-DSP库在KEIL的安 ...

  3. 华大半导体HC32F4A0笔记(四),SPI读写国产128kB EEPROM 上海贝岭BL25CMIA

    一.配置SPI引脚 本例中SPI只连接了一个设备,即国产128kB EEPROM上海贝岭BL25CMIA. NSS脚采用软件控制. 其它引脚通过查看手册可知其连接的SPI外设为SPI1. /* SPI ...

  4. 华大半导体HC32F4A0系列ARM芯片EXMC并口通信时序的FPGA实现

    华大半导体HC32F4A0系列ARM芯片EXMC并口通信时序的FPGA实现 EXMC简介 外部存储器控制器EXMC是一个用来访问各种片外存储器.实现数据交换的独立模块.EXMC通过配置可以把内部的AM ...

  5. 华大半导体 HC32F4A0 系列开发笔记

    HC32F4A0 系列开发笔记目录 选用芯片 对比STM32的优势 踩过的坑和一些总结: 1--串口空中断bug 2--标志位的寄存器 3--写IAP遇到的问题 4--CPU主频配置 5--RTC时钟 ...

  6. STM32工作笔记0054---串口通信实验讲解--自定义发送接收数据协议

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152

  7. 基于UCOSII的RS485通信(STM32F107)

    一.实现效果 基于ucosii实时操作系统的RS485通信,采用USART + DMA进行收发, 二.开发环境 开发工具:KEIL V5 开发板: STM32f107RC 采用方式:USART + D ...

  8. STM32物联网项目-RS485通信(Modbus协议)

    RS485通信(Modbus协议) 协议介绍 RS485介绍:http://t.csdn.cn/bOuFX Modbus协议:http://t.csdn.cn/mgioX CubeMX配置 RS-48 ...

  9. C++实现RS485通信

    1.虚拟串口配置,使用虚拟串口助手配置虚拟串口,注:选择一个未被使用的串口进行实验 虚拟串口配置程序下载地址:https://download.csdn.net/download/wangzhiyan ...

最新文章

  1. R语言plotly可视化:plotly可视化累积cumulative直方图(Cumulative Histogram)
  2. Nature:盐粒大小的相机,可以拍出清晰彩色照片,未来或可应用到手机
  3. Python数据可视化2.6 一些最好的可视化实践
  4. hdf5与hdfs的区别
  5. 从二值检索到层次竞买图——让搜索广告关键词召回焕然新生
  6. (八)深入浅出TCPIP之TCP长连接与短连接详解
  7. mac wordpress php7,Mac 下基于 wordpress 搭建个人博客系统
  8. Android so减包相关
  9. cad pu插件下载lisp_25个常用CAD插件 合集 下载
  10. 软件项目管理和软件工程的区别
  11. linux wifi信道,2.4G wifi 的频道/信道 20M 40M的概念,区别
  12. 网赚项目 - 利用社区广告牌,空手赚钱创业项目
  13. [SIGMOD 2021] SharPer: Sharding Permissioned Blockchains Over Network Clusters
  14. python算法技巧——列表、字符串实用技巧
  15. 综合型集团该如何利用数字化转型支撑磅礴多元的服务?
  16. 通过源码简要分析之Android消息机制Handler、Looper、MessageQueue运行机制
  17. 现代浏览器观察者 Observer API 指南
  18. LC.826. Most Profit Assigning Work
  19. 大工计算机学院博士答辩要求,大连理工大学博士毕业论文要求和发表
  20. 设计师必备的PPT辅助神器-iSlide

热门文章

  1. 8月10日模拟赛题解
  2. 关于H1N1的预防,目前为止最好的忠告
  3. 如何使用流量精灵刷网站流量
  4. android 输入法更换_一种动态切换Android系统输入法的弹出模式的方法与流程
  5. VVC学习之五:VTM帧内预测工具详解
  6. echarts gallery,echarts社区地址(可视化作品分享的地址)
  7. 美国数学建模2022F题总结(含数据)
  8. STP生成树算法广播风暴的产生
  9. 小米电视3s测试软件,【当贝市场】小米电视3s测屏软件有哪些?
  10. matlab里面gtophat什么意思,MATLAB图象处理函数