在上一次的灵动微课堂中和大家分享过MM32F013x-UART 9bit通信实例,本次微课堂在此实例的基础上实现UART多处理器通信。MM32F013x系列MCU支持UART多处理器通信,其工作原理是主从机设备采用复用漏极开路,主从机外部接上拉电阻,在空闲时使从机处于静默模式,主机要控制从机执行任务时主机发送指令唤醒从机并发送数据控制从机执行相应任务。
1.UART静默模式
MM32F013x系列MCU UART静默模式的特点:
- 任何接收状态位都不会被设置;
- 所有的接收中断都被禁止;
- UART\_CCR寄存器中的RWU位被置1。RWU可以被硬件自动控制或在某个条件下由软件写入。
根据UART\_CCR寄存器中的WAKE位状态,UART多处理器通信有二种方法进入或退出静默模式分别是:
WAKE 位被设置0:进行空闲总线检测。
WAKE 位被设置1:进行地址标记检测。
空闲总线检测:
空闲帧唤醒可以同时唤醒所有从机,在从机处于静默模式时主机发送空闲帧(即所有位均为1的数据)实现多个从机同步被唤醒。
地址标记检测:
地址标记唤醒可以唤醒单个从机,从机进入静默模式时,主机向从机寻址发送地址帧,从机自动比对地址,地址配对正确则该从机被唤醒,否则继续进入静默模式。这样只有被主机寻址的从机才被唤醒激活并接收数据,从而减少未被寻址的从机参与带来的多余的UART服务开销。
2. 配置流程
与UART多处理器通信相关的主要寄存器有UART通用控制寄存器、UART接收地址寄存器UART\_RXADDR和UART接收掩码寄存器UART\_RXMASK其描述如下寄存器表所示:
图1
如上图1所示为UART通用控制寄存器UART\_CCR,在MM32F013x UM手册的第489和第490页有关于该寄存器位的详细描述。本实例UART多处理器通信要用到的相关于UART通用控制寄存器UART\_CCR位的说明如下:
Bit13:
WAKE(rw,reset:0x00)唤醒方法,该位决定UART多机通信从机唤醒的方法。
1:地址标记唤醒。库函数设置:
UART_WakeUpConfig(UART1,UART_WakeUp_AddressMark);
0:空闲总线唤醒。库函数设置:
UART_WakeUpConfig(UART1, UART_WakeUp_IdleLine);
Bit12:
RWU(rw, reset:0x00)接收唤醒,该位用来决定是否把UART置于静默模式。该位可以由软件设置或清除。当唤醒序列到来时,硬件也会自动将其清零。
1:接收器处于静默模式。库函数设置:
UART_ReceiverWakeUpCmd(UART1, ENABLE);
0:接收器处于正常工作模式。库函数设置:
UART_ReceiverWakeUpCmd(UART1, DISABLE);
注:在设置地址标记唤醒时,如果接收 buffer 非空则不能软件修改。
Bit11:
B8EN(rw, reset:0x00)UART同步帧发送第9bit使能控制位。该位使能后校验使能PEN不起作用。
1:使能同步帧第9bit发送。库函数设置:
UART_Enable9bit(UART1, ENABLE);
0:禁止同步帧第9bit发送。库函数设置:
UART_Enable9bit(UART1, DISABLE);
Bit10:
B8TOG(rw,reset:0x00)UART同步帧发送第9bit自动翻转控制位。
1:使能第9bit自动翻转。
库函数设置:
UART_Set9bitAutomaticToggle(UART1, ENABLE);
0:禁止第9bit自动翻转。库函数设置:
UART_Set9bitAutomaticToggle(UART1, DISABLE);
注:在 B8TXD 和 B8POL 的值相同时,在配置完寄存器后传输的第二个数据开始翻转,第一个数据默认为地址位。
Bit8:
B8TXD(rw,reset:0x00)UART同步帧发送数据第9bit。
1:发送同步帧第9bit为高电平。库函数设置:
UART_Set9bitLevel(UART1, ENABLE);
0:发送同步帧第9bit为低电平。库函数设置:
UART_Set9bitLevel(UART1, DISABLE)
图2
如上图2所示为UART接收地址寄存器UART\_RXADDR,在MM32F013x UM手册的第491页有关于该寄存器位的详细描述。本实例UART多处理器通信要用到的相关于UART\_RXADDR接收地址寄存器位的说明如下:
Bit31:8:Reserved,始终读为0x00
Bit7:0:RXADDR(rw,reset:0x00)UART 同步帧数据本机匹配地址。
库函数设置:
UART_SetRXAddress(UART1, SLAVEADDR);
其中地址参数SLAVEADDR可以设置为宏定义形式。
如果 RXMASK =0xFF时接收到的同步帧数据与本机匹配地址相同时,产生RXB8\_INTF。地址 0是广播地址,收到后都会响应。
图3
如上图3所示为UART接收掩码寄存器UART\_RXMASK,在MM32F013x UM手册的第492页有关于该寄存器位的详细描述。本实例UART多处理器通信要用到的相关于UART\_RXMSK接收掩码寄存器位的说明如下:
Bit31:8:Reserved,始终读为0x00
Bit7:0:RXMASK(rw,reset:0xFF)UART数据位全为“0”时,接收到任何数据都产生同步帧中断请求。如果数据位为“1”,RDR和RXADDR的相应位匹配时,产生同步帧中断请求。
库函数设置:
UART_SetRXMASK(UART1,SLAVEADDR);
其中地址参数SLAVEADDR可以设置为宏定义形式。
根据上文与UART 9bit多处理器通信相关的寄存器位的描述,本实例从机唤醒模式使用标记从机地址方式,在MM32F013x-UART 9bit通信实例的基础上增加从机UART 9bit多处理器通信相关的寄存器位的初始化,这里以库函数方式给出,增加的4行代码如下所示:
//Wake
up method
UART_WakeUpConfig(UART1,
UART_WakeUp_AddressMark);
//Synchronous
frame match address
UART_SetRXAddress(UART1,
SLAVEADDR);
//Synchronous
frame match address mask
UART_SetRXMASK(UART1,
SLAVEADDR);
//Receive
wake up method
UART_ReceiverWakeUpCmd(UART1,
ENABLE);
3. 程序配置
3.1 初始化主机MM32F013x UART1 9bit通信
本实例使用MM32F0133C7P核心板作为UART多处理器通信,主机初始化代码如下所示:
bsp_UART1_Master_irq_9Bit_Init(u32
baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
UART_InitTypeDef UART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1,ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10,GPIO_AF_1);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate = baudrate;
UART_InitStructure.WordLength =UART_WordLength_8b;
UART_InitStructure.StopBits =UART_StopBits_1;
UART_InitStructure.Parity = UART_Parity_No;
UART_InitStructure.HWFlowControl =UART_HWFlowControl_None;
UART_InitStructure.Mode = UART_Mode_Rx |UART_Mode_Tx;
UART_Enable9bit(UART1, ENABLE);
UART_Set9bitLevel(UART1, DISABLE);
UART_Set9bitAutomaticToggle(UART1, ENABLE);
UART_Init(UART1, &UART_InitStructure);
UART_ITConfig(UART1, UART_IT_RXIEN,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel =UART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority= 3;
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
NVIC_Init(&NVIC_InitStructure);
UART_Cmd(UART1, ENABLE);
}
3.2 初始化从机MM32F013x UART1 9bit通信
MM32F0133C7P UART1从机初始化代码如下所示:
注意:多从机通信,初始化从机串口时需要修改从机地址宏为0x01、0x02等。
#define SLAVEADDR (0x01)
void bsp_UART1_Slave_irq_9Bit_Init(u32 baudrate)
{
GPIO_InitTypeDef
GPIO_InitStructure;
UART_InitTypeDef
UART_InitStructure;
NVIC_InitTypeDef
NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10, GPIO_AF_1);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_OD;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_OD;
GPIO_Init(GPIOA,&GPIO_InitStructure);
UART_StructInit(&UART_InitStructure);
UART_InitStructure.BaudRate= baudrate;
UART_InitStructure.WordLength= UART_WordLength_8b;
UART_InitStructure.StopBits= UART_StopBits_1;
UART_InitStructure.Parity= UART_Parity_No;
UART_InitStructure.HWFlowControl= UART_HWFlowControl_None;
UART_InitStructure.Mode= UART_Mode_Rx | UART_Mode_Tx;
UART_WakeUpConfig(UART1,UART_WakeUp_AddressMark);
UART_WakeUpConfig(UART1,UART_WakeUp_IdleLine);
UART_SetRXAddress(UART1,SLAVEADDR);
UART_SetRXMASK(UART1,0x02);
UART_ReceiverWakeUpCmd(UART1,ENABLE);
UART_Enable9bit(UART1,ENABLE);
UART_Set9bitLevel(UART1,DISABLE);
UART_Set9bitAutomaticToggle(UART1,ENABLE);
UART_Init(UART1,&UART_InitStructure);
UART_ITConfig(UART1,UART_IT_RXIEN, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel= UART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority= 0;
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
NVIC_Init(&NVIC_InitStructure);
UART_Cmd(UART1,ENABLE);
}
3.3 编写MM32F013x UART1主机中断服务函数
MM32F0133C7P UART1主机中断服务函数,代码如下所示:
#define RX_MASTER_LEN (3)
u8 g_Rx_Master_Buf[RX_MASTER_LEN] = {0x00};
u8 g_Rx_Master_Cnt = 0;
void UART1_IRQHandler(void)
{
u8 res;
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
{
UART_ClearITPendingBit(UART1, UART_IT_RXIEN);
res = UART_ReceiveData(UART1);
g_Rx_Master_Buf[g_Rx_Master_Cnt] = res;
if(g_Rx_Master_Cnt < RX_MASTER_LEN-1)
{
g_Rx_Master_Cnt++;
}
else
{
g_Rx_Master_Cnt = 0;
}
}
}
3.4 编写MM32F013x UART1从机中断服务函数
MM32F0133C7P UART1从机中断服务函数,代码如下所示:
#define RX_SLAVE_LEN (3)
u8 g_Rx_Slave_Buf[RX_SLAVE_LEN] = {0x00};
u8 g_Rx_Slave_Cnt = 0;
void UART1_IRQHandler(void)
{
u8 res;
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
{
UART_ClearITPendingBit(UART1, UART_IT_RXIEN);
res = UART_ReceiveData(UART1);
g_Rx_Slave_Buf[g_Rx_Slave_Cnt] = res;
if(g_Rx_Slave_Cnt < RX_SLAVE_LEN-1)
{
g_Rx_Slave_Cnt++;
}
else
{
g_Rx_Slave_Cnt = 0;
}
}
}
3.5 编写MM32F013x UART1主从机通用发送数据函数
MM32F0133C7P UART1主从机通用发送函数,代码如下所示:
u8 g_Tx_Master_Buf[2] = {0xAA,0x55};
void bsp_UART_Send_Byte_Data(UART_TypeDef* uart,u8 data)
{
UART_SendData(uart, data);
while(!UART_GetFlagStatus(uart, UART_FLAG_TXEPT));
}
void bsp_UART_Send_Bytes_Data(UART_TypeDef* uart, u8* buf, u16 len)
{ while(len--)
{
bsp_UART_Send_Byte_Data(uart,*buf++);
}
}
3.6 编写MM32F013x UART1主机发送从机地址函数
MM32F0133C7P UART1主机发送从机地址函数,代码如下所示:
u8 g_Tx_Master_Buf[2] = {0xAA,0x55};
#define SLAVEADDR1 (0x01)
#define SLAVEADDR2 (0x02)
void bsp_UART_Send_SlaveAddr(UART_TypeDef* uart,u8 data)
{
UART_SendData(uart, data);
while(!UART_GetFlagStatus(uart, UART_FLAG_TXEPT));
}
3.7 编写MM32F013x UART1主机按键发送从机地址和数据函数
宏定义按键GPIO端口和管脚,本实例只用到MM32F013x核心板的PA0作为KEY1对应核心板。
#define KEY1_GPIO_Port GPIOA
#define KEY1_Pin GPIO_Pin_0
#define KEY2_GPIO_Port GPIOB
#define KEY2_Pin GPIO_Pin_2
#define KEY3_GPIO_Port GPIOB
#define KEY3_Pin GPIO_Pin_10
#define KEY4_GPIO_Port GPIOB
#define KEY4_Pin GPIO_Pin_11
#define KEY1 GPIO_ReadInputDataBit(KEY1_GPIO_Port,KEY1_Pin) //read key1
#define KEY2 GPIO_ReadInputDataBit(KEY2_GPIO_Port,KEY2_Pin) //read key2
#define KEY3 GPIO_ReadInputDataBit(KEY3_GPIO_Port,KEY3_Pin) //read key3
#define KEY4 GPIO_ReadInputDataBit(KEY4_GPIO_Port,KEY4_Pin) //read key4
#define KEY1_DOWN_VALUE 1 //KEY1
#define KEY2_DOWN_VALUE 0 //KEY2
#define KEY3_DOWN_VALUE 0 //KEY3
#define KEY4_DOWN_VALUE 0 //KEY4
#define KEY1_PRES 1 //KEY1
#define KEY2_PRES 2 //KEY2
#define KEY3_PRES 3 //KEY3
#define KEY4_PRES 4 //KEY4
//Init Key GPIO
void bsp_Key_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB ,ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = KEY1_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = KEY2_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY2_GPIO_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = KEY3_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY3_GPIO_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = KEY4_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY4_GPIO_Port, &GPIO_InitStructure);
}
u8 bsp_Key_Scan(u8 mode)
{
static u8 key_up = 1;
if(mode)
{
key_up = 1;
}
if(key_up && ((KEY1 == KEY1_DOWN_VALUE) || (KEY2 == KEY2_DOWN_VALUE) || \
(KEY3 == KEY3_DOWN_VALUE) || (KEY4 == KEY4_DOWN_VALUE)))
{
DELAY_Ms(10);
key_up = 0;
if(KEY1 == KEY1_DOWN_VALUE)
{
return KEY1_PRES;
}
else if(KEY2 == KEY2_DOWN_VALUE)
{
return KEY2_PRES;
}
else if(KEY3 == KEY3_DOWN_VALUE)
{
return KEY3_PRES;
}
else if(KEY4 == KEY4_DOWN_VALUE)
{
return KEY4_PRES;
}
}
else if((KEY1 != KEY1_DOWN_VALUE) && (KEY3 != KEY3_DOWN_VALUE) && \
(KEY4 != KEY4_DOWN_VALUE) && (KEY2 != KEY2_DOWN_VALUE))
{
key_up = 1;
}
return 0;
}
u8 Key_Nnum = 0;
void bsp_Process_Key_Task(void)
{
static u8 Key_Value = 0;
Key_Value = bsp_Key_Scan(0);
switch(Key_Value)
{
case KEY1_PRES:
if(Key_Nnum == 0)
{
Key_Nnum = 1;
bsp_UART_Send_SlaveAddr(UART1, SLAVEADDR1); //Send SlaveAddr1
bsp_UART_Send_Bytes_Data(UART1, g_Tx_Master_Buf, sizeof(g_Tx_Master_Buf)); //Send data
}
else if(Key_Nnum == 1)
{
Key_Nnum = 0;
bsp_UART_Send_SlaveAddr(UART1, SLAVEADDR2); //Send SlaveAddr2
bsp_UART_Send_Bytes_Data(UART1, g_Tx_Master_Buf, sizeof(g_Tx_Master_Buf)); //Send data
}
break;
case KEY2_PRES:
break;
case KEY3_PRES:
break;
case KEY4_PRES:
break;
default :
break;
}
}
3.8 编写MM32F013x UART1主机接收从机返回的数据函数
处理MM32F0133C7P UART1主机接收数据函数,代码如下所示:
void bsp_UART_Master_Rec_Task(void)
{
if(((g_Rx_Master_Buf[0] == SLAVEADDR1 ) || (g_Rx_Master_Buf[0] == SLAVEADDR2)) && (g_Rx_Master_Buf[1] == 0xAA) && (g_Rx_Master_Buf[2] == 0x55))
{
LED1_TOGGLE();
g_Rx_Master_Cnt = 0;
memset(g_Rx_Master_Buf,0,sizeof(g_Rx_Master_Buf));
}
}
3.9 编写MM32F013x UART1从机接收主机发送的数据函数
处理MM32F0133C7P UART1主机接收数据函数,代码如下所示:
void bsp_UART_Slave_Rec_Task(void)
{
if((g_Rx_Slave_Buf[0] == SLAVEADDR) && (g_Rx_Slave_Buf[1] == 0xAA) && (g_Rx_Slave_Buf[2] == 0x55))
{
LED1_TOGGLE();
bsp_UART_Send_Bytes_Data(UART1, g_Rx_Slave_Buf, sizeof(g_Rx_Slave_Buf));
g_Rx_Slave_Cnt = 0;
memset(g_Rx_Slave_Buf,0,sizeof(g_Rx_Slave_Buf));
}
}
注意:g\_Rx\_Slave\_Buf[1]为主机发送给从机的地址由从机接收判断,多从机通信需要修改从机地址宏:#define SLAVEADDR (0x02)等。
3.10 MM32F013x UART1 9bit多处理器通信功能演示
MM32F0133C7P多处理器通信功能演示:
主机main函数中调用DELAY\_Init、LED\_Init、bsp\_Key\_GPIO\_Init和主机串口初始化函数bsp\_UART1\_Master\_irq\_9Bit\_Init,在while(1)后台调用按键发送从机地址和发送数据函数bsp\_Process\_Key\_Task以及处理接收从机返回数据函数bsp\_UART\_Master\_Rec\_Task代码如下所示:
s32 main(void)
{
//SysTick init
DELAY_Init();
//LED Init
LED_Init();
//Key GPIO Init
bsp_Key_GPIO_Init();
//UART1 9Bit irt Init
bsp_UART1_Master_irq_9Bit_Init(115200);
while(1)
{
//Key processing multi-processor communication
bsp_Process_Key_Task();
//Processing master receiving tasks
bsp_UART_Master_Rec_Task();
}
}
从机main函数中调用LED\_Init和从机串口初始化函数bsp\_UART1\_Slave\_irq\_9Bit\_Init,在while(1)后台调用处理从机接收任务函数bsp\_UART\_Slave\_Rec\_Task代码如下所示:
s32 main(void)
{ //LED Init
LED_Init();
//UART1 9bit init
bsp_UART1_Slave_irq_9Bit_Init(115200);
while(1)
{
//Processing slave receiving tasks
bsp_UART_Slave_Rec_Task();
}
}
分别编译以上主机和从机工程代码,然后烧录软件到MM32F0133C7P核心板上,烧录多从机时需修改从机工程代码的从机地址宏SLAVEADDR,编译后再烧录。本实例从机烧录0x01和x02为例作为演示,即1台主机和2台从机作为演示。
4. 接线方式
MM32F0133C7P多处理器通信UART主机和从机接线方法:
各从机TX线与连接,RX线与连接,从机线与连接的TX接到主机RX端,从机线与连接的RX接到主机的TX端,主从机分别外接5.1\~10K值范围之一的上拉电阻,本实例接5.1K上拉电阻。
MM32F0133C7P UART多处理器通信演示1台主机和2台从机分别发送和接收数据:
主机第1次按下MM32F0133C7P核心板上的S1按键,主机发送1字节从机地址0x01,接着发送2字节数据0xAA和0x55,从机1(地址0x01)在静默模式下自动比对主机寻址从机的地址0x01比对成功从机1被唤醒继续接收0xAA和0x55数据,收到完整的3字节数据时从机LED1状态翻转LED1亮并原样返回收到的数据给主机,此时从机2仍处于静默模式,主机完全收到从机1返回的3字节数据即0x01,0XAA,0x55时主机LED1状态翻转LED亮,实物现象如下图4所示。
***图4(上从机1、中主机、下从机2) ***
主机第2次按下MM32F0133C7P核心板上的S1按键,主机发送1字节从机地址0x02,接着发送2字节数据0xAA和0x55,从机2(地址0x02)在静默模式下自动比对主机寻址从机的地址0x02比对成功从机2被唤醒继续接收0xAA和0x55数据,收到完整的3字节数据时从机LED1状态翻转LED1亮并原样返回收到的数据给主机,此时从机1仍处于静默模式其LED1维持上次点亮状态,主机完全收到从机2返回的3字节数据时即0x02,0XAA,0x55时主机LED1状态翻转LED灭,实物现象如下图5所示:
图5(上从机1、中主机、下从机2)
5. 波形抓取
逻辑分析仪抓取到的MM32F0133C7多处理器通信波形图如下图6和图7所示:
图6上波形红色框注为主机发送:0x01、0xAA、0x55寻址从机1,从机(地址0x01)从静默模式唤醒并收到主机发送的数据0xAA和0x55时原样返回给主机如图6下波形蓝色框注:0x01、0xAA、0x55,此时从机2(地址0x02)并未作响应仍处于静默模式。
图6
图7上红色框注为主机发送:0x02、0xAA、0x55寻址从机2,从机(地址0x02)从静默模式唤醒并收到主机发送的数据0xAA和0x55时原样返回给主机如图7下波形蓝色框注:0x02、0xAA、0x55,此时从机1(地址0x01)并未作响应仍处于静默模式。
图7