一、串口半双工

  • 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平台测试通过
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐