极客秀
搜索

IIC通信可以软件实现,串口未尝不可!

1

上期回顾

上期我们从数据链路层介绍了串口通信面临的四个问题,以及串口通讯是如何优化克服这个问题的。为了更深刻的理解串口通讯协议,我们本期仿照串口通信协议利用GPIO翻转实现模拟串口。

虽然 软件模拟串口非常没有必要 ,连最基本的单片机基本都会配备硬件串口,但是这样子走一遍这个 流程可以加强我们了解串口通信的组成。不喜勿喷!

2

准备工作

为了实现标准串口通信,我们需要实现特定波特率下的通信,也就是固定的时间下发送一位数据以及实现开始位停止位和校验位。

为了实现精准的波特率,我们可以利用单片机的硬件定时器,当波特率设置为9600的时候。我们计算此时的时间应该是:表示每秒传输9600个比特(位)

因此,每一位的时间为:

1秒 ÷ 9600 = 0.0001042秒 = 104.2微秒

因此我们的定时器参数可以这样子设置,这样子可以实现103微秒的定时。至于和标准的104微秒,这多出来的1微秒用以给中断服务函数的语句执行。

并且上期我们说过,实际上略微的偏差并不影响接收端的读写,通过开始位和停止位可以调节这个偏差。

3

程序实现


struct MyUART{  uint8_t Data;//每帧数据  uint8_t Check;//校验位  uint8_t DR;//发送信号  uint8_t Num;//移位  uint8_t CR;//发送完毕} myuart;

我们定义一个结构体定义我们的串口:包括帧数据,校验位,发送标志位,移位以及发送完毕标志位。


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){  if(myuart.DR)  {    switch(myuart.Num)    {      case 0: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 0);        myuart.Num++;        break;      case 1: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 2: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 3: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 4: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 5: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 6: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 7: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 8: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (myuart.Data & (1 << (myuart.Num - 1))) ? 1 : 0);        myuart.Check+= (myuart.Data & (1 << (myuart.Num - 1)));        myuart.Num++;        break;      case 9: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, myuart.Check%2);        myuart.Num++;        break;      case 10: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 1);        myuart.DR = 0;        myuart.Check  = 0;        myuart.Num=0;        myuart.CR=1;        break;    }  }}

在定时器的中断函数中,我们先较为浅薄的使用switch来分别来实现:起始位,8位数据位,校验位以及停止位的IO引脚电平设置。


void print(unsigned char * s,int size){  int num = 0;  myuart.DR = 1;//开始发送  while(num<size)  {    if(myuart.CR)//如果发送结束    {      myuart.Data = *(s+num);      myuart.DR = 1;//发送开始标志位      myuart.CR = 0;//清空结束标志位      num++;    }  }  }

接着我们定义一个print函数用来测试我们的串口通讯,我们采用轮询的方式检查标志位,并赋值数据来实现。


  HAL_TIM_Base_Start_IT(&htim1);  myuart.DR = 0;  myuart.Check  = 0;  myuart.Num=0;  /* USER CODE END 2 */  
  /* Infinite loop */  /* USER CODE BEGIN WHILE */  while (1)  {    /* USER CODE END WHILE */  
    /* USER CODE BEGIN 3 */    print("Hellorn",8);    HAL_Delay(1000);  }

打开定时器以及添加我们的串口输出函数。

事实上只要大家充分了解通讯协议的组成,任何的通讯协议都可以用软件模拟的方式来实现。因此大家学习之余也可以了解了解底层,加深其理解٩(๑ᵒ̴̶̷͈᷄ᗨᵒ̴̶̷͈᷅)و

1.转载请保留原文链接谢谢!
2.本站所有资源文章出自互联网收集整理,本站不参与制作,如果侵犯了您的合法权益,请联系本站我们会及时删除。
3.本站发布资源来源于互联网,可能存在水印或者引流等信息,请用户擦亮眼睛自行鉴别,做一个有主见和判断力的用户。
4.本站资源仅供研究、学习交流之用,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担。
5.联系方式(#替换成@):pm#vimge.com

  相关内容