基于s32k146的IO口模拟串口

zhe最近在搞一个IO口模拟LIN的代码,其中包括IO口模拟UART的部分,就记录一下,希望对像我们这样的初学者能有所帮助。

1. 串口协议

串口的特点:全双工,串行,异步

串口协议(我这里选用最常见的一种):一个起始位,八个字节长度,无奇偶校验,一个停止位,一帧包括十个比特。图的话就不上传了,这个大家应该都很清楚。

2. 如何模拟

2.1 模拟发送

发送倒是很简单,直接按协议发就完事,只需要注意波特率的设置,我这里选用9600,每个比特传输时间为104us,这个怎么算其实很简单,用1/9600转换成us就行了,我这里发的话就没有用到定时器,直接使用滴答计时器延时函数。代码如下:

void uart1_putchar(uint8_t val)
{
	uint8_t i = 0;
	UART_TX_LOW() ;//拉低发送引脚,起始位
	delay_us((1000000*1)/LIN_BPS);
	for(;i<8;i++){
		if(val&0x01){
			UART_TX_HIGH();
			delay_us((1000000*1)/LIN_BPS);
		}else{
			UART_TX_LOW();
			delay_us((1000000*1)/LIN_BPS);
		}
		val>>=1;
	}
	UART_TX_HIGH();//拉高发送引脚,停止位
	delay_us((1000000*1)/LIN_BPS);                                                           
}

2.2 模拟接收

接收的话稍微复杂一点,需要用到外部中断和定时器中断,我们把定时器周期调为52us,外部中断为下降沿中断,逻辑为:外部触发下降沿中断,中断函数中如果读取引脚电平为0且为起始位的话,启动定时器中断,关闭外部中断,在第一次进定时器中断时,多次读取对应电平,如果均为低电平,则表示起始位正确,然后偶数次进中断不做处理,奇数次进中断多次读取电平值,取其多数值,最后index为19时表明一个字节接收完毕,启动外部中断,关闭定时器中断。代码如下:

void PORTE_IRQHandler(void)//外部中断
{
	if((PTE->PDIR & (1<<16))==0x00)
	{
		delay_us(10);
		if(UART_RX_Value() == 0){
			if(RBtIdx == 0){
				INT_SYS_DisableIRQ(PORTE_IRQn);//使能外部中断
				LPIT0->TMR[1].TCTRL = 0x00000001;//使能定时器中断
			}
		}
	}
	PORTE->ISFR |= 0x4000;//清楚中断标志
}
void LPIT0_Ch1_IRQHandler (void){
                    LPIT0->MSR |= LPIT_MSR_TIF1_MASK;//清楚中断问题
                    RBtIdx++;
				    if(RBtIdx == 1){
				    	if(FourTimesDetectStart() == 0){
				    		RBtIdx = 0;
				    		return ;
				    	}
				    }else if(RBtIdx == 19)
				    {
				    	LPIT0->TMR[1].TCTRL = 0x00000000;
				    	INT_SYS_EnableIRQ(PORTE_IRQn);
				    	RBtIdx = 0;
				    }
				    else if(RBtIdx % 2)
				    {
				    	RxData>>=1;

				    	if(FourTimesDetectData())
				    	{
				    		RxData|=0x80;
				    	}
				    }

}

 3 附加引脚初始化,定时器初始化,和延时函数代码初始化

我使用的IO引脚是PB4(out)和PD16(in)

	PCC->PCCn[PCC_PORTB_INDEX] |= PCC_PCCn_CGC_MASK;
	PTB->PDDR |= 1<<4;
	PORTB->PCR[4] =  0x00000100;
	PTB-> PSOR |= 1<<4;

	PCC->PCCn[PCC_PORTE_INDEX] |= PCC_PCCn_CGC_MASK;
	PTE->PDDR &= ~(1<<16);
	PORTE->PCR[16] = PORT_PCR_MUX(1)|PORT_PCR_IRQC(0x0a)|PORT_PCR_PS(1)|PORT_PCR_PE(1);//上拉,下降沿中断
	S32_NVIC_EnableIRQ(PORTE_IRQn,9);

定时器使用的是IT0的第一个通道

	PCC->PCCn[PCC_LPIT_INDEX] = PCC_PCCn_PCS(6);    
	PCC->PCCn[PCC_LPIT_INDEX] |= PCC_PCCn_CGC_MASK; /* Enable clk to LPIT0 regs */
	LPIT0->MCR = 0x00000001;    
	LPIT0->MIER = 0x00000001;  
	LPIT0->TMR[1].TVAL = 2080;    /* Chan 0 Timeout period: 0.04M clocks */			//52us

延时函数初始化

void s32_systick_init(void){
	S32_SysTick->CSR = S32_SysTick_CSR_ENABLE(0);
	fac_us = 80000000 / 1000000;
	S32_SysTick->RVR = 0xFFFFFF; //重装载寄存器
	S32_SysTick->CVR = 0; //当前计数
	S32_SysTick->CSR = 0; //控制寄存器
	S32_SysTick->CSR |= S32_SysTick_CSR_TICKINT(1u)|S32_SysTick_CSR_ENABLE(1)|S32_SysTick_CSR_CLKSOURCE(1u);
}

void delay_us(uint32t nus){
	uint32_t ticks;
	uint32_t told,tnow,tcnt=0;
	uint32_t reload=S32_SysTick->RVR;				//LOAD的值
	ticks=nus*fac_us; 						//需要的节拍数
	told= S32_SysTick->CVR ;      				//刚进入时的计数器值
	while(1)
	{
		tnow= S32_SysTick->CVR ;
		if(tnow!=told)
		{
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}
	}
}

4.总结

IO口模拟UART的收发按照UART的时序来即可,虽然看起来很简单,但是动手的时候还是会有一些问题的,踏踏实实做好每一步,不要眼高手低。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值