STM32L0XX用IO口模拟串口UART接收

UART通信协议

  • 波特率:通常为9600,19200,115200等选项,单位为bit/s,bit(比特)是表示信息的最小单位。一个0或者1就是1bit。所以9600bit/s,就是一秒的时间内可以传输9600bit的数据。
  • 起始位:在发送的时候,先发出一个逻辑”0”的信号,告诉对方,我要开始传输数据了。在接收的时候,就是正常情况下高电平,然后检测到低电平0,就代表对方发来了信息,我要开始接收了。
  • 数据位:一般是8位,8位为一个字节,这是真正要传输的信息。
  • 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。就是数1的位数,如果设置了偶校验,那1的数量就必须是偶数,不是的话代表数据传输得不对。这个位可有可无。
  • 结束位:同起始位同理。
  • :起始位+数据位+(校验位)+结束位,就是一帧数据。
  • 帧间隔:即传送数据的帧与帧之间的间隔大小。在这里插入图片描述
    此次试验使用的波特率为9600bps,传输1bit的时间约等于0.104ms。数据由1bit起始位+8bit数据位+1bit停止位共10bit的数据帧组成,起始位为低电平,停止位为高电平。IO口默认拉高,数据传输由低位到高位。
说明

每个芯片有不同的库函数,所以这个例程只是给大家一个参考,给大家一个思路,大家可以根据自己现有的芯片库函数编写对应的IO串口函数。

串口通讯初始化
#define SU_TRUE    1
#define SU_FALSE   0


void softuart_init( void )
{
    flag_rx_ready = SU_FALSE; //标志位置0
    flag_rx_off   = SU_FALSE;

    io_init();		   		  //IO管脚初始化
    tim_init(tim6);	  
    set_tim_cb(tim6, softuart_tim_cb);//设置定时器中断函数
}

一开始先设置softuart()初始化,将所有标志位都置0。先进行IO口的初始化,然后对定时器进行初始化。初始化TIM6基本定时器,将softuart_tim_cb()这个函数设置为tim6定时器的中断函数。

定时器初始化
void tim_init(tim_id_t id)    
{
 if(id == tim6)
    {
        uint32_t uwPrescalerValue = (uint32_t) ((SystemCoreClock / 1000000) - 1);
        __HAL_RCC_TIM6_CLK_ENABLE();
        tim6_handle.Instance = TIM6;
        tim6_handle.Init.Period = 103;
        tim6_handle.Init.Prescaler = uwPrescalerValue;
        tim6_handle.Init.ClockDivision = 0;
        tim6_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
        HAL_NVIC_SetPriority(TIM6_IRQn, 0, 2);
        HAL_NVIC_EnableIRQ(TIM6_IRQn);
        HAL_TIM_Base_Init(&tim6_handle);
        HAL_TIM_Base_Start_IT(&tim6_handle);
        HAL_TIM_Base_Stop_IT(&tim6_handle);
    }
}

在这里插入图片描述

IO口初始化
static void io_init(void)
{
    GpioInit(&rx, SORFUART_RX, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1);
    GpioSetInterrupt(&rx, IRQ_FALLING_EDGE, IRQ_LOW_PRIORITY, rx_irqhandler);
}

初始化IO口,将rx引脚设置为输入引脚,然后设置rx中断,为下降沿中断,且中断调用函数为rx_irqhandler函数。

下降沿中断函数
static void rx_irqhandler(void) 
{
    if(flag_rx_ready == SU_FALSE) 
    {
        // wait some time so that measure the middle part of signal
        delay();
        flag_rx_ready = SU_TRUE;
        start_rx(); //开启定时器中断读取数据
    }
}

#define start_rx()             (HAL_TIM_Base_Start_IT(&tim6_handle))	
//开启读取定时器中断
  • Rx引脚检测到下降沿,便会引起IO口中断,然后调用中断rx_irqhandler()。这个函数先判断flag_rx_ready,这是代表rx引脚是否准备好的标志。如果标志位为1,说明引脚正在准备接受数据,所以就不用理会这次的下降沿信号,先让他处理好它手头的事情。如果标志位为0,说明引脚正在空闲状态,什么事都没。先等待一会,等数据位到来。然后把标志位置1,这样就不会有下一次中断干扰到数据的接收。然后开启定时器中断,读取数据。
    • Start_rx()函数是一个宏定义,定义为HAL库的一个函数,就是开启定时器中断的意思。
    • softuart_tim_cb()函数是定时器中断函数,在开始的初始化函数里面设置好的。
      最终得到一个容量为128字节大小的inbuf数组,数据都存储在里面。
定时器中断函数
void softuart_tim_cb(void)
{
    static unsigned char flag_rx_waiting_for_stop_bit = SU_FALSE;  //读取结束标志位
    static unsigned char rx_mask = 1;
    static unsigned char rx_bit_cnt = 0;
    static unsigned char internal_rx_buffer = 0;

    unsigned char flag_in;
    // Receiver Section
    if ( flag_rx_off == SU_FALSE ) 					//【关闭rx】为0,就是rx正在启动状态
		{
			  if ( flag_rx_waiting_for_stop_bit ) 	//rx在等待停止位当中
			  {
            	inbuf[qin] = internal_rx_buffer;  		//将一个字节的数据存入inbuf数组
            	internal_rx_buffer = 0;					//清0,等待下一个字节的数据读入
            	if(softuart_notify != NULL) 
				{
                	softuart_notify(UART_NOTIFY_RX);
            	}
           	 	stop_rx();	//停止rx
            	if ( ++qin >= SOFTUART_IN_BUF_SIZE ) //读取数据缓冲区为128,所以数据缓冲区一共有128字节
				{
               		 // overflow - reset inbuf-index
               			 qin = 0;
           		 }
          	    flag_rx_ready = SU_FALSE; //【开启rx】置0
                flag_rx_waiting_for_stop_bit = SU_FALSE;//数据存储完成,不再等待停止位
               }
             else 
				{  //、【等待停止位】为0,就是还没有到等待停止位的时候
					 if ( flag_rx_ready == SU_TRUE )          //rx正在启动状态
					{
                		flag_in = get_rx_pin_status(); 		  //读取输入管脚状态
               			 if ( flag_in ) 					      //如果输入为1
						{
                   			 internal_rx_buffer |= rx_mask; 	  //读入数据1
                		}
               			 rx_mask <<= 1;
                		if ( ++rx_bit_cnt == RX_NUM_OF_BITS ) //如果读取的数据达到8个
						{
				  			  flag_rx_waiting_for_stop_bit = SU_TRUE; //数据位读完了,等待停止位
                  			  rx_bit_cnt = 0;		                   	//重新计数字节
                  			  rx_mask = 1;				
              		   }
            }
        }
    }
}
从输入缓冲区inbuf【128】读取一个字节数据
uint8_t softuart_getchar( uint8_t *data )
{
    if(qout != qin) 
		{
    	*data = inbuf[qout];
    	if ( ++qout >= SOFTUART_IN_BUF_SIZE ) 
			{
            qout = 0;
    	}
     return 0;
    }
    return 1; //如果qout=qin,说明此时qin正在输入,返回1代表busy
}

参考

IO口模拟UART串口

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值