MCU GPIO模拟UART:【完整代码】

在做项目的过程中,经常发生UART口不够用的情况,可以用GPIO口来软件模拟UART。

硬件MCU型号:华芯微特SWM181(其它MCU大同小异)

实现功能:debug串口

原理:GPIO+定时器

DBG_RX_T Dbg_Rx = {
	{0x00},
	RX_BUF_LEN - 1,
	0,
	0,
	0,
	DBG_RX_IDLE
};

DBG_TX_T Dbg_Tx = {
	NULL,
	0,
	0,
	0,
	0
};

#define DBG_TX_LOW()			GPIO_ClrBit(GPIOA, PIN1)
#define DBG_TX_HIGH()			GPIO_SetBit(GPIOA, PIN1)
#define DBG_RX_Value()		GPIO_GetBit(GPIOA, PIN0)
/****************************************************************************************************************************************** 
* 函数名称:	Dbg_Rx_Clear()
* 功能说明: 接收模块状态清除
* 输    入: 无
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
static void Dbg_Rx_Clear(void)
{
	Dbg_Rx.ChrIdx = 0;
	Dbg_Rx.State = DBG_RX_IDLE;
}
/****************************************************************************************************************************************** 
* 函数名称:	Dbg_RX_State()
* 功能说明: IOUART接收模块当前状态
* 输    入: 无
* 输    出: uint32_t		IOUART_RX_IDLE、IOUART_RX_BUSY、IOUART_RX_SUCCESS、UOUART_RX_TIMEOUT、IOUART_RX_FRAMERR
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t Dbg_RX_State(void)
{
	return Dbg_Rx.State;
}
/****************************************************************************************************************************************** 
* 函数名称:	Dbg_RX_Count()
* 功能说明: IOUART接收模块接收到的字符数
* 输    入: 无
* 输    出: uint32_t		接收到的字符个数
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t Dbg_RX_Count(void)
{
	return Dbg_Rx.ChrIdx;
}
#define DBG_RX_IDLE												0	    //空闲中,接收到停止位进入
#define DBG_RX_BUSY												1	    //接收中,接收到起始位进入
#define DBG_RX_BUFFULL										2	    //接收满,停止接收
#define DBG_RX_TIMEOUT										3	    //收超时,停止接收
#define DBG_RX_FRAMERR										4	    //帧错误,停止接收
#define DBG_RX_PARITYERR 									5	    //奇偶校验错误,停止接收

#define DBG_STOP_1												0
#define DBG_STOP_2												1
#define DBG_STOP													DBG_STOP_1 
#define DBG_PARITY_NONE										0
#define DBG_PARITY_EVEN										1
#define DBG_PARITY_ODD										2
#define DBG_PARITY												DBG_PARITY_NONE 

#define RX_BUF_LEN												16

typedef struct
{
	 char 									Buff[RX_BUF_LEN];   //接收:Buf
	 uint32_t 							ChrCnt;							//接收字符数
	 volatile uint32_t 			ChrIdx;							//接收:字符数索引号
	 volatile uint32_t 			BitIdx;							//接收:字符位索引号
	 volatile uint32_t 			TimeoutCnt;					//接收:超时数量
	 volatile uint32_t 			State;							//接收状态
	
}DBG_RX_T;

typedef struct
{
	char* 									Buff;   						//发送:Buf
	uint32_t 								ChrCnt;							//发送字符数
	volatile uint32_t 			ChrIdx;							//发送:字符数索引号
	volatile uint32_t 			BitIdx;							//发送:字符位索引号
	volatile uint32_t 			TxBusy;							//发送状态
	
}DBG_TX_T;

1:硬件初始化端口

void Dbg_Init(void)
{	
	DBG_TIM_BAUD_9600       = SystemCoreClock/9600;
	DBG_TIM_BAUD_9600_X_1_5 = SystemCoreClock/9600*1.5 * 0.85;
	
	//tx init
	GPIO_Init(GPIOA, PIN1, 1, 0, 0, 0);																		//GPIOA.1初始化为输出
	DBG_TX_HIGH();																												//UART空闲时输出高电平
	TIMR_Init(TIMR0, TIMR_MODE_TIMER, SystemCoreClock/9600, 1);
	IRQ_Connect(IRQ0_15_TIMR0, IRQ0_IRQ, 1);															//定时器2中断链接到IRQ0中断线,高优先级
	
	//rx init
	TIMR_Init(TIMR1, TIMR_MODE_TIMER, SystemCoreClock/9600, 1);
	IRQ_Connect(IRQ0_15_TIMR1, IRQ1_IRQ, 3);
	GPIO_Init(GPIOA, PIN0, 0, 1, 0, 0);																		//GPIOA.0初始化为输入,上拉
	EXTI_Init(GPIOA, PIN0, EXTI_FALL_EDGE);																//下降沿触发中断
	IRQ_Connect(IRQ0_15_GPIOA0, IRQ2_IRQ, 2);															//A0引脚外部中断链接到IRQ1中断线,高优先级
	EXTI_Open(GPIOA, PIN0);	
}

2:接收GPIO引脚中断服务函数

void IRQ2_Handler(void)
{
	if(EXTI_State(GPIOA, PIN0) == 0)  //避免init,内核产生中断
	{
		return;
	}
	
	EXTI_Clear(GPIOA, PIN0);    //清除中断标志
	
	if(Dbg_Rx.State == DBG_RX_IDLE)				//有必要检测是否真产生中断
	{
		Dbg_Rx.BitIdx = 0;
		Dbg_Rx.State = DBG_RX_BUSY;

		TIMR_Stop(TIMR1);
		TIMR_SetPeriod(TIMR1, DBG_TIM_BAUD_9600_X_1_5);
		TIMR_Start(TIMR1);
	}
}

3:接收位定时器中断服务函数

void IRQ1_Handler(void)
{
	static char ChrShf;		//接收移位寄存器
	//printf("[%s]: 22222 \r\n",__func__);
	if(TIMR_INTStat(TIMR1))
	{
		TIMR_INTClr(TIMR1);
			
		if(Dbg_Rx.State == DBG_RX_BUSY)
		{			
			//1: 接收到第0位
			if(Dbg_Rx.BitIdx == 0)
			{
				TIMR_Stop(TIMR1);
				TIMR_SetPeriod(TIMR1, DBG_TIM_BAUD_9600);
				TIMR_Start(TIMR1);
			}
			
			//2: 接收完8位数据
			if(Dbg_Rx.BitIdx < 8)
			{
				if(DBG_RX_Value()) 
				{
					ChrShf |=  (1 << Dbg_Rx.BitIdx);
				}
				else
				{					
					ChrShf &= ~(1 << Dbg_Rx.BitIdx);
				}
			}
			//3: 接收到停止位
			else if(Dbg_Rx.BitIdx == 8)
			{
				Dbg_Rx.State = DBG_RX_IDLE;			
				Dbg_Rx.Buff[Dbg_Rx.ChrIdx] = ChrShf;
				
				if(DBG_RX_Value() == 1)
				{
					Dbg_Rx.ChrIdx++;
					
					if(Dbg_Rx.ChrIdx == Dbg_Rx.ChrCnt)
					{
						Dbg_Rx.State = DBG_RX_BUFFULL;					
						EXTI_Close(GPIOA, PIN0);
					}
				}
				else
				{
					Dbg_Rx.State = DBG_RX_FRAMERR;					
					EXTI_Close(GPIOA, PIN0);
				}
			}
			
			Dbg_Rx.BitIdx++;			
			Dbg_Rx.TimeoutCnt = 0;
		}
		//4: 接收超时完成
		else if((Dbg_Rx.State == DBG_RX_IDLE) && (Dbg_Rx.ChrIdx != 0))
		{
			Dbg_Rx.TimeoutCnt++;
			
			if(Dbg_Rx.TimeoutCnt == 20)	//2个字符时间
			{
				Dbg_Rx.State = DBG_RX_TIMEOUT;			
				EXTI_Close(GPIOA, PIN0);				
				TIMR_Stop(TIMR1);				
				Dbg_Rx.TimeoutCnt = 0;
				
				//解析字符
				Dbg_Rx_Data_Parse(Dbg_Rx.Buff,Dbg_Rx.ChrCnt+1);
				Dbg_Rx_Clear();	
				EXTI_Open(GPIOA, PIN0);
			}
		}
	}
}

4:接收数据解析函数

通过接收特定数据,执行特定任务

static void Dbg_Rx_Data_Parse(char *buf , uint32_t len)
{
	uint32_t i = 0;
	uint8_t sw = 0x00;
	//uint8_t p1 = 0, p2 = 0;
	
	for(i=0;i<len;i++)
	{
		if(buf[i] == '\r')
		{
			if(buf[0] == 'V' && buf[1] == 'E' && buf[2] == 'R' && buf[3] == ':')
			{
				sw = 0x01;
			}	
			else if(buf[0] == 'H' && buf[1] == 'W' && buf[2] == 'F' && buf[3] == ':')
			{
				sw = 0x02;
			}
			else if(buf[0] == '4' && buf[1] == 'G' && buf[2] == 'D' && buf[3] == ':')
			{
				sw = 0x03;
			}
		}
	}
	
	switch(sw)
	{
		case 0x01:                              //打印软硬件本号
			hal_version_test(); 
		break;
		
		case 0x02:                              //打印软硬件本号
			hw_fun(); 
		break;
		
		case 0x03:                              //打印软硬件本号
			//Ml307a_Lauch_Init();
		memset(Ml307a_Rx.Buff,0x00,M_4G_BUF_LEN);
			//Ml307a_Mqtt_Connect();
		Ml307a_Send_Cmd(AT_SET_MQTTSUB_CMD,AT_SET_MQTTSUB_RSP);
		break;
		
		default:
			printf("[%s]: debug data parse is error!!! sw = %d buf = %s len = %d \r\n",__func__,sw,buf,len);
	}
}

5:发送中断服务函数

void IRQ0_Handler(void)
{
	if(TIMR_INTStat(TIMR0))
	{
		TIMR_INTClr(TIMR0);
		
		if(Dbg_Tx.TxBusy == 1)
		{
			if(Dbg_Tx.BitIdx == 0)
			{
				DBG_TX_LOW();				//起始位
			}
			else if(Dbg_Tx.BitIdx < 9)
			{
				if(Dbg_Tx.Buff[Dbg_Tx.ChrIdx] & (0x01 << (Dbg_Tx.BitIdx-1)))
					DBG_TX_HIGH();
				else
					DBG_TX_LOW();
			}
			else if(Dbg_Tx.BitIdx == 9)
			{
				DBG_TX_HIGH();				//停止位
				
				Dbg_Tx.ChrIdx++;
				if(Dbg_Tx.ChrIdx < Dbg_Tx.ChrCnt)
				{
					Dbg_Tx.BitIdx = 0;
					
					goto T3Svr_End;		//跳过“BitIdx++”,否则发不出起始位
				}
				else
				{
					TIMR_Stop(TIMR0);
					
					Dbg_Tx.TxBusy = 0;
				}
			}
			
			Dbg_Tx.BitIdx++;
		}
		
T3Svr_End:
		;
	}
}

6:实现串口打印log信息,重写fputc

int fputc(int ch, FILE *f)
{
	Dbg_Tx_Send((char *)&ch, 1);
	while(Dbg_TX_IsBusy());
	return ch;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DevinLGT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值