多机通信(HCF005)
-
UARTX实现多机通信
一主多从,主机发送地址,从机识别成功后,向主机发送数据。
-
通信过程
UARTx_SCON.SM2 置“1”, 可开启多机通讯位。UARTx_SCON.SM2置“0”, 可关闭多机通讯位。
开启多机通讯后,发送数据时,主机可以通过 UARTx_SCON.TB8 来区分当前帧
是地址帧(UARTx_SCON.TB8=1)还是数据帧(UARTx_SCON.TB8=0)。接收数据时,
从机会忽略 RB8 位(第 9 位) 为“0”的当前接收帧。
- 当为数据帧时,该帧数据不会存入到从机的 UARTx_SBUF 寄存器中,从机也不会产生接收中
断。
- 当为地址帧时,由于多机通讯中自动地址识别功能已开启,使得从机可以检测接收到的地址与
其自身地址是否相符合。(从机开启多机通信,则开启了自动识别地址功能)
当从机地址相符合,从机会对 UARTx_SCON.RB8 置“1”, UARTx_ISR.RI 置“1”。
从机软件看到 UARTx_SCON.RB8=1 并且 UARTx_ISR.RI=1 后,将 UARTx_SCON.SM2 位清“0”,接受数据帧。(UARTx_SCON.SM2 位清“0”,似乎只能软件请零;但是因为所实现功能为从机地址识别成功后,向主机发送数据,而不是接受主机数据,所以此处SM2 位清“0”与否均可向主机发送数据。但主机作为接收方,必须SM2 位清“0”才能接受数据。)
如果地址不符合,从机硬件保持 UARTx_SCON.RB8 和UARTx_ISR.RI 为“0”,软件保持 UARTx_SCON.SM2 位为“1”,继续处于地址监听状态。
-
给定地址
UART 设 备 的 UARTx_SADDR 寄 存 器 用 来 表 示 自 己 的 设 备 给 定 地 址 ,
UARTx_SADEN 寄 存 器 是 地 址 掩 码 , 可 以 用 来 定 义 地 址 中 的 无 关 位 。当
UARTx_SADEN 的某一位为“0”, 表示该位地址为无关位, 也就是说在地址匹配过程
中,该位地址不参与地址匹配。
即SADDR=0X10(从机地址为0X10),SADEN =0X01,只识别最低位有效,无论地址0X20,0X30,,,,都认为有效;SADEN =0X0F,则识别地址时,低四位均要比较;SADEN =0XFF,则识别地址8位。
-
具体步骤、代码
- 配置串口模式位2/3,其余正常配置。
- 主从均开启多机模式(UARTx_SCON.SM2 置“1”),主机先发送地址,随后关闭多机模式(UARTx_SCON.SM2 置“0”)。
- 从机成功识别地址,准备向主机发送数据前,不需要关闭多机模式。
- 注意,此时正常通信,TB8位也可以作为奇偶校验位来使用。Uart_SetTb8(M0P_UART1,UartEven,u8RxData[0])该函数设置奇偶校验位。
主机代码:
```c
#include <stdio.h>
uint8_t ARR1= 0X10,ARR2= 0X11;//从机地址
int main(void) {
App_ClkCfg();
App_UartInit();
while(1)
{
delay1ms(1000);
RxData[1] = Uart_SendAddressFrame(M0P_UART1, ARR1);
RxData[2] = Uart_SendAddressFrame(M0P_UART1, ARR2);
}
}
//串口配置
#include "usart.h"
#include "uart.h"
#include "bt.h"
#include <stdio.h>
extern uint16_t RxData[5];
extern uint8_t RxFlag;
/*****串口波特率设置***/
static void _UartBaudCfg(void)
{
uint16_t timer=0;
stc_uart_baud_cfg_t stcBaud;
stc_bt_cfg_t stcBtCfg;
DDL_ZERO_STRUCT(stcBaud);
DDL_ZERO_STRUCT(stcBtCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralBt,TRUE);
Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1,TRUE); //
stcBaud.bDbaud = 0u;
stcBaud.u32Baud = 115200;
stcBaud.enMode = UartMode3;
stcBaud.u32Pclk = Sysctrl_GetPClkFreq();
timer = Uart_SetBaudRate(M0P_UART1, &stcBaud); //
stcBtCfg.enMD = BtMode2;
stcBtCfg.enCT = BtTimer;
Bt_Init(TIM1, &stcBtCfg);
Bt_ARRSet(TIM1,timer);
Bt_Cnt16Set(TIM1,timer);
Bt_Run(TIM1);
}
/*****串口初始化***/
void App_UartInit(void)
{
stc_uart_cfg_t stcCfg;
_UartBaudCfg();
stcCfg.enRunMode = UartMode3;
Uart_Init(M0P_UART1, &stcCfg);
// M0P_UART1->SCON_f.SM2 = TRUE;
// M0P_UART0->SCON_f.TB8 = 1;
// Uart_EnableIrq(M0P_UART1, UartRxIrq); //使能接受中断
Uart_ClrStatus(M0P_UART1, UartRC);
EnableNvic(UART1_IRQn, IrqLevel2, TRUE);
}
/*****串口1中断服务函数***/
void Uart1_IRQHandler(void)
{
if(TRUE == Uart_GetStatus(M0P_UART1, UartRC))
{
Uart_ClrStatus(M0P_UART1, UartRC);
RxData[1] = Uart_ReceiveData(M0P_UART1);
RxFlag=1;
arr++;
}
}
/***带有地址帧发送数据 中断***/
uint8_t Uart_SendAddressFrameIT(M0P_UART_TypeDef* UARTx, uint8_t address)
{
// uint8_t u8RxDate = 0;
UARTx->SCON_f.SM2 = TRUE;
UARTx->SCON_f.TB8 = 1;
Uart_SendDataPoll(UARTx,address);
delay10us(1);
UARTx->SCON_f.SM2 = FALSE;
if(UARTx->ISR_f.FE ==1)
UARTx->ISR_f.FE =0;
}
/***带有地址帧发送数据 无中断 使用该函数关中断***/
uint8_t Uart_SendAddressFrame(M0P_UART_TypeDef* UARTx, uint8_t address)
{
uint8_t u8RxDate = 0;
UARTx->SCON_f.SM2 = TRUE;
UARTx->SCON_f.TB8 = 1;
Uart_SendDataPoll(UARTx,address);
delay10us(1);
UARTx->SCON_f.SM2 = FALSE;
if(UARTx->ISR_f.FE ==1)
UARTx->ISR_f.FE =0;
while(UARTx->ISR_f.RI == 0){;}
u8RxDate = Uart_ReceiveData(UARTx);
Uart_ClrStatus(UARTx, UartRC);
return u8RxDate;
}
/****系统时钟配置***/
//系统时钟配置
static void App_ClkCfg(void)
{
stc_sysctrl_clk_cfg_t stcCfg;
///< 时钟初始化前,优先设置要使用的时钟源:此处设置RCH为24MHz(默认值为4MHz)
Sysctrl_SetRCHTrim(SysctrlRchFreq22_12MHz);
///< 选择内部RCH作为HCLK时钟源;
stcCfg.enClkSrc = SysctrlClkRCH;
///< HCLK SYSCLK
stcCfg.enHClkDiv = SysctrlHclkDiv1;
///< PCLK 为HCLK
stcCfg.enPClkDiv = SysctrlPclkDiv2;
///< 系统时钟初始化
Sysctrl_ClkInit(&stcCfg);
}
从机代码:
识别地址成功,从机会对RB8 置“1”,RI 置“1”。是否硬件清零不知,所以软件清零。
从机主函数:
M0P_UART1->SCON_f.SM2 = TRUE;重新开启多机通信。