前言
大家好,在上一期我们主要介绍了关于家庭能源网关这个项目的选材。本期我们讲的是如何实现项目主控MCU华大HC32F005的UART中断配置以及发送和接收数据,为我们后续项目读取测量到的电流、电压、功率做准备。
大家有需要可以自行下载有关HC32F005demo,例程包含了所有外设的初始化以及配置。手册在上一章下载,或者去官网下载,这里就不过多介绍HC32F005其他资源。重点介绍UART这部分。
UART介绍
HC32F005有 2 个通用 UART 模块(UART0/1),支持半双工和全双工传输;支持 8bit、 9bit 数据格式;支持 Mode0/1/2/3 四种不同传输模式;UART0 的波特率由 TIMER0 产生,UART1 的波特率由 TIMER1 产生;支持多机通讯模式;支持自动地址识别; 支持给定地址和广播地址。 通用 UART(UART0/1)只有一个时钟输入 PCLK,寄存器配置逻辑和数据收发逻辑 都工作在该时钟域。如下图是UART结构框图:
UART工作模式
Mode0(同步模式,半双工)
当工作在 Mode0 时,UART 工作在同步模式,其波特率为固定的 PCLK 时钟的 1/12。 UART 接收数据由 RXD 输入、UART 发送数据有 RXD 输出,RXD 此时为输入输 出端口。UART 同步移位时钟由 TXD 输出,TXD 此时为输出端口。注意,本模式只 能作为主机发送同步移位时钟,不可以作为从机从外部接收该时钟。该模式下,传输 的数据位宽只能是 8 位的,没有起始位和结束位。 将 UARTx_SCON.SM0 和 UARTx_SCON.SM1 清零,可进入 Mode0 工作模式。
Mode1(异步模式,全双工)
当工作在 Mode1 时,发送数据通过 TXD 发送,接收数据通过 RXD 接收。该数据 由 10 位组成:起始位“0”开始,紧接着 8 位数据位(低位在先,高位在后),最后是结 束位“1”。 该模式下,波特率由定时器模块产生,并且是可编程的。UART0 的波特率由 TIMER0 产生,UART1 的波特率由 TIMER1 产生。 将 UARTx_SCON.SM0 清 0,UARTx_SCON.SM1 置 1,可进入 Mode1 工作模式。
Mode2(异步模式,全双工)
当工作在 Mode2 时,发送数据通过 TXD 发送,接收数据通过 RXD 接收。该数据 由 11 位组成:起始位“0”开始,接着是 8 个数据位,1 个 TB8 位和结束位。额外的 TB8 位是用来在多机通讯环境下使用,当 TB8=1,表明所接收的是地址帧;当 TB8=0, 表明所接收的是数据帧。当不需要多机通讯时,此位也可以作为奇偶校验位来使用。 该模式下,波特率可以独立产生,不需要外部 TIMER 产生。 将 UARTx_SCON.SM0 置 1,UARTx_SCON.SM1 清 0,可进入 Mode2 工作模式。
Mode3(异步模式,全双工)
Mode3 的数据格式,传输时序以及操作方式都与 Mode2 相同,唯一的区别是 Mode3 的波特率由 TIMER 产生,而不是像 Mode2 由设备自己独立产生。Mode3 的波特率 是可编程的,波特率生成方式与 Mode1 相同。本产品中,UART0 的波特率由 TIMER0 产生,UART1 的波特率由 TIMER1 产生。 将 UARTx_SCON.SM0 置 1,UARTx_SCON.SM1 置 1,可进入 Mode3 工作模式。
注:选用哪种工作模式根据我们用哪个UART决定,不可以随便用,否则无法收发数据
UART波特率配置
Mode0
当工作在 Mode0 时,波特率被固定在 PCLK 的 1/12,不需要 TIMER 的支持。
Mode1/3
当工作在 Mode1 或者 Mode3 时,波特率由 TIMER 的溢出时间决定。具体公式如下图所示:
BaudRate = (UARTx_SCON.DBAUD+1) * Freq/32 * (65536 - TM)
其中,UARTx_SCON.DBAUD 表示双倍波特率,Freq 为 PCLK 时钟频率,TM 为 TIMER 计数值。注意,TIMER 必须配置为 16 位自动重载入模式,计数寄存器和重载 寄存器都得写入 TM 值。
Mode2
当工作在 Mode2 时,波特率被固定在如下公式所得值:
BaudRate = (UARTx_SCON.DBAUD+1) * Freq/64
其中,UARTx_SCON.DBAUD 表示双倍波特率,Freq 为 PCLK 时钟频率。
帧错误检测
Mode1/2/3 具有帧错误检测功能,硬件会自动检测接收到的帧数据是否带有效的 Stop 位。如果没有收到有效 Stop 位,则 UARTx_ISR.FE 置 1。UARTx_ISR.FE 位由硬件 置 1,软件清 0,如果软件未及时清 0,则后续收到数据即使带有效 Stop 位,也不会 把 UARTx_ISR.FE 标志清 0。
多机通讯
Mode2/3 具有多机通讯功能,为此在其帧格式中增加了 1 位 TB8/RB8。将 UARTx_SCON.SM2 置“1”,可开启多机通讯位。
当开启多机通讯位后,发送数据时,主机可以通过 UARTx_SCON.TB8 来区分当前帧 是地址帧(UARTx_SCON.TB8=1)还是数据帧(UARTx_SCON.TB8=0)。接收数据时, 从机会忽略 RB8 位(第 9 位)为“0”的当前接收帧。当收到帧的 RB8 位(第 9 位) 为“1”表明其是地址帧,从机会继续判断接收到的地址与其自身地址是否相等。如果 匹配,则从机会对 UARTx_SCON.RB8 置“1”,并对 UARTx_ISR.R I 置“1”,以表明 该帧为地址帧并且地址已经匹配。从机软件看到 UARTx_SCON.RB8=1 并且 UARTx_ISR.RI=1 后,先把 UARTx_SCON.SM2 位清“0”,然后准备接受给它的数据 帧。如果地址不等,表明主机并不是寻址该从机,从机硬件保持 UARTx_SCON.RB8 和 UARTx_ISR.RI 为“0”,软件保持 UARTx_SCON.SM2 位为“1”,从机继续处于地址 监听状态。
UART中断配置流程
1.串口引脚初始化
void App_PortInit(void)
{
stc_gpio_cfg_t stcGpioCfg;
DDL_ZERO_STRUCT(stcGpioCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //使能GPIO模块时钟
///<TX
stcGpioCfg.enDir = GpioDirOut;
Gpio_Init(GpioPort2, GpioPin3, &stcGpioCfg);
Gpio_SetAfMode(GpioPort2, GpioPin3, GpioAf6); //配置P23 端口为URART0_TX
///<RX
stcGpioCfg.enDir = GpioDirIn;
Gpio_Init(GpioPort2, GpioPin4, &stcGpioCfg);
Gpio_SetAfMode(GpioPort2, GpioPin4, GpioAf6); //配置P24 端口为URART0_RX
}
2.串口波特率设置
void UartBaudCfg_Init(void)
{
uint16_t timer=0;
stc_uart_baud_cfg_t stcBaud;
stc_bt_cfg_t stcBtCfg;
stc_uart_cfg_t stcCfg;
DDL_ZERO_STRUCT(stcBaud);
DDL_ZERO_STRUCT(stcBtCfg);
DDL_ZERO_STRUCT(stcCfg);
//外设时钟使能
Sysctrl_SetPeripheralGate(SysctrlPeripheralBt,TRUE);//模式0/2可以不使能
Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1,TRUE);
stcBaud.bDbaud = 1u;//双倍波特率功能
stcBaud.u32Baud = 9600;//更新波特率位置
stcBaud.enMode = UartMode1; //计算波特率需要模式参数
stcBaud.u32Pclk = Sysctrl_GetPClkFreq(); //获取PCLK
timer = Uart_SetBaudRate(M0P_UART1, &stcBaud);
stcBtCfg.enMD = BtMode2;
stcBtCfg.enCT = BtTimer;
Bt_Init(TIM1, &stcBtCfg);//调用basetimer1设置函数产生波特率
Bt_ARRSet(TIM1,timer);
Bt_Cnt16Set(TIM1,timer);
Bt_Run(TIM1);
stcCfg.enRunMode = UartMode1;//测试项,更改此处来转换4种模式测试
Uart_Init(M0P_UART1, &stcCfg);
}
3.相关中断配置
///< UART中断配置
Uart_EnableIrq(M0P_UART1, UartRxIrq);
Uart_ClrStatus(M0P_UART1, UartRC);
EnableNvic(UART1_IRQn, IrqLevel3, TRUE);
4.设置中断函数
//UART1中断函数
void Uart1_IRQHandler(void)
{
if(Uart_GetStatus(M0P_UART1, UartTC)) //UART1数据发送
{
Uart_ClrStatus(M0P_UART1, UartTC); //清中断状态位
fnUart_SendIR();//发送数据
}
if(Uart_GetStatus(M0P_UART1, UartRC)) //UART1数据接收
{
Uart_ClrStatus(M0P_UART1, UartRC); //清中断状态位
com_1.rbuf[com_1.rcnt] = Uart_ReceiveData(M0P_UART1); //接收数据字节
com_1.rcnt=(com_1.rcnt+1)%256;//更新计数接收
}
}
注意:到这里我们已经完成串口中断的配置流程,但是还不能实现串口的收发数据。还有个最重要的步骤要配置,那就是还要选择中断向量号,但是这个中断向量号并不是对应我们使用的哪个串口,具体选择哪个中断向量号,可参考如下图:
UART中断向量号配置
void IRQ007_Handler(void){
Uart1_IRQHandler();
}
总结
经过上面的配置流程我们就可以实现串口中断数据的发送和接收,有兴趣的可以打印一下结果,这里我就不做打印。