stm32的IO口模拟串口

主要借鉴https://blog.csdn.net/tonyiot/article/details/82502953这篇文章

在实际应用中往往会出现芯片的串口不够用的情况,此时可以利用多余的IO来模拟串口,实现数据的收发。
在利用IO模拟串口时,按照传输协议进行操作即可。

关键之处在于:
   对于发送,计算好不同波特率对应的延时时间进行数据发送。
   对于接收,通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。

本文的波特率选用9600
发送延时计算: 1/9600≈104us,按照此时间进行数据的发送

#define TX_PIN 		GPIO_Pin_6 
#define RX_PIN 		GPIO_Pin_7
#define TX_DATA_H()  	GPIO_SetBits(TX_PORT, TX_PIN)     //高电平
#define TX_DATA_L()  	GPIO_ResetBits(TX_PORT,TX_PIN)    //低电平
#define COM_RX_READ 	GPIO_ReadInputDataBit(RX_PORT, RX_PIN)//端口电平读取
#define BuadRate_9600 104			//模拟9600的频率
enum{
		COM_START_BIT, 	//
		COM_D0_BIT, 		//bit0
		COM_D1_BIT, 		//bit1
		COM_D2_BIT, 		//bit2
		COM_D3_BIT, 		//bit3
		COM_D4_BIT, 		//bit4
		COM_D5_BIT, 		//bit5
		COM_D6_BIT, 		//bit6
		COM_D7_BIT, 		//bit7
		COM_STOP_BIT,	  //停止位
		};
unsigned char recvStat = COM_STOP_BIT;
uint8_t recvData = 0;//接收的数据
uint8_t len;
uint8_t uart_buf[20];//接收缓冲区
void VirtualCOM_Config(void)
{
	VirtualTx_Config();//发送端口初始化	
	EXITRx_Init();//接收端口初始化,及接收中断配置
	TIM2_Configuration(103,71); //设置对应模特率的定时器的定时时间
}

发送端口配置

void VirtualTx_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
	//为数据输出口,模拟TX
	GPIO_InitStructure.GPIO_Pin = TX_PIN;			//PA.6 端口配置	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(TX_PORT, &GPIO_InitStructure);		
	GPIO_SetBits(TX_PORT, TX_PIN);	
}
void EXITRx_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	
	//为数据输入口,模拟RX	
	GPIO_InitStructure.GPIO_Pin = RX_PIN;			//PA.7
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	    		 
	GPIO_Init(RX_PORT, &GPIO_InitStructure);
	//GPIOA.7 中断线以及中断初始化配置下降沿触发
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7);
  	EXTI_InitStructure.EXTI_Line = EXTI_Line7;	
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void TIM2_Configuration(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);				
	//定时器TIM2初始化:t= (arr+1) *(psc+1) / Tck_tim						
	TIM_TimeBaseStructure.TIM_Period = arr; 	  
	TIM_TimeBaseStructure.TIM_Prescaler = psc;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 	
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); 	
	TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); 		
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 
 	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;     
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	
	NVIC_Init(&NVIC_InitStructure); 
}

void VirtualCOM_ByteSend(uint8_t Data)
{
    uint8_t i;
    TX_DATA_L();                // 发送起始位
    delay_us(BuadRate_9600);
    for(i=0; i<8; i++)       	  // 发送8位数据位
    {
        if (Data & 0x01)
            TX_DATA_H();        // 1
        else
            TX_DATA_L();        // 0
        Data >>= 1;
        delay_us(BuadRate_9600);
    }
    TX_DATA_H();                // 发送结束位
    delay_us(BuadRate_9600);
}
void SendBytes(u8 *str,u8 len)	
{
	u16 i;
	for(i=0; i<len; i++)
		VirtualCOM_ByteSend(str[i]);
}
	//发送字符串
void VirtualCOM_StringSend(u8 *str)
{
	while(*str != 0)
	{
		VirtualCOM_ByteSend(*str);
		str++;
	}
}
void EXTI9_5_IRQHandler(void)
{
	//确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(EXTI_Line7) != RESET)
	{
		if(!COM_RX_READ)//检测接收引脚高低电平,如果低电平,则说明检测到下降沿	
		{
			if(recvStat == COM_STOP_BIT)//为停止位
			{
				recvStat = COM_START_BIT; //接收到开始位								
				recvData  = 0;
				
				TIM_Cmd(TIM2, ENABLE);//打开定时器,接收数据	
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line7); //清除EXTI_Line7中断挂起标志位
	}
}
void TIM2_IRQHandler(void)
{
	if(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET) 
	{
		TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
		recvStat++; 			//改变状态机
		if(recvStat == COM_STOP_BIT)//收到停止位
		{ 
			TIM_Cmd(TIM2, DISABLE);//关闭定时器
			uart_buf[len++] = recvData;
			return; 
		}				
		if(COM_RX_READ) //'1'
		{
			recvData |= (1 << (recvStat - 1));
		}
		else //'0'
		{
			recvData &= ~(1 <<(recvStat - 1));
		}
	}
}
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值