一、串口半双工

  • stm32的串口支持全双工使用,即数据可双向同时传递,特点是有Rx和Tx两根数据线。这很好,可以满足大部分需要的
  • 有些特殊场合,我们需要使用半双工,比如驱动某些数字舵机。这时数据也是双向传递,但是同一时刻只允许一个方向的数据进行传递。这种情况下只用到Tx这一根数据线。stm32的串口也支持半双工
  • 数据手册中对串口的半双工功能有以下描述:
  • 注意:
  1. 开启半双工后Rx引脚不在使用
  2. Tx引脚应配置为开漏拉高
  3. USART1~USART6都支持半双工
  4. TE为1时是发送使能,注意这个不会被硬件封锁。换句话说只要TE为1,串口就一直处于发送状态不能接受。查看手册CR1寄存器部分:

    可以看出:我们可以通过操作TE和RE位,配置串口处于仅发送或仅接受的状态

二、示例代码

1. 串口配置:

配置和普通的没啥区别,不同处在于Rx不用配了;Tx配为开漏拉高;使用库函数​​void USART_HalfDuplexCmd​​操作HDSEL位使能或失能半双工模式

void USART1_Half_Configuration(void)  
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1

GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //开漏上拉
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);


USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//USART_InitStructure.USART_Mode = USART_Mode_Rx ;
USART_Init(USART1,&USART_InitStructure);
USART_HalfDuplexCmd(USART1, ENABLE); //注意这个,启动半双工模式

USART_Cmd(USART1,ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、

}

2. 串口模式切换

操作串口CR1寄存器的TE位和RE位即可,不妨使用宏的形式

#define readOnly(x) x->CR1 |= 4; x->CR1 &= 0xFFFFFFF7;    //串口x配置为只读,CR1->RE=1, CR1->TE=0
#define sendOnly(x) x->CR1 |= 8; x->CR1 &= 0xFFFFFFFB; //串口x配置为只写,CR1->RE=0, CR1->TE=1

3. 一个简单的应用示例

  • USART1和USART2配置为半双工,从USART2发给USART1数据1开始,收到后将数据加一发给对方。
  • 连线:PA9 - PD5
  • debug观察res1和res2交替递增

以下展示部分示例代码:

//主函数部分
int main(void)
{
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //延时初始化
LED_Init();
USART1_Half_Configuration(); //串口1配置为半双工
USART2_Half_Configuration(); //串口2配置为半双工

sendOnly(USART2); //USART2只写
readOnly(USART1); //USART1只读
USART_SendData(USART2,1); //USART2先发给USART1
while(1)
{
times++;
if(times%30==0)LED0=!LED0; //闪烁LED,提示系统正在运行.
delay_ms(10);
}
}

//接收中断服务函数部分,完成数据互相发送
u8 Res1; //debug观察
void USART1_IRQHandler(void)
{

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res1 = USART_ReceiveData(USART1);
readOnly(USART2);
sendOnly(USART1);
USART_SendData(USART1,Res1+1); //USART1发给USART2
}
}

u8 Res2; //debug观察
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
Res2 = USART_ReceiveData(USART2);
readOnly(USART1);
sendOnly(USART2);
USART_SendData(USART2,Res2+1); //USART2发给USART1
}
}
  • 可以在github下载完整源码:​​完整源码​​,此程序在正点原子stm32f407平台测试通过