串口通讯整理及实现方案总结

串口通讯整理及实现方案总结

项目介绍

按下 KEY_ADD 按键,主机发送 “ADDKP;” 给从机,从机接受字符串并判断,如果符合则将 i 的值 +1并打印在 OLED 上,当 i == 6 时,主机变从机,从机变主机,继续执行以上操作。

串口通信整理

串行通信
1.串行通信的三种传送方式
  • 单工:通信双方一端固定为发送端,一端固定为接收端,仅支持单向数据传输。

  • 半双工:通信双方既可以为发送端,也可以为接收端。支持数据在两个方向上传输,但在任何时刻仅支持数据单向传输,实际上是一种可以切换方向的单工通信。

  • 全双工:通信双方都有独立的发送和接收能力,支持数据同时在两个方向上传输,实际上是两个单工通信方式的结合。

2.串行通信的两种通信方式
  • 异步通信:异步通信采用固定的通信格式,数据以相同的帧格式传送。每一帧由起始位数据位奇偶校验位停止位组成。若停止位后不是紧接着传送下一个字符,则让线路保持为空闲位,线路处于等待状态。即每一帧数据发送完到下一帧数据中间间隔的时间是不确定的。
  • 同步通信:同步通信时,通信双方共用一个时钟,数据开始传送前用同步字符来指示(常约定1~2 个),并由时钟来实现发送端和接收端的同步,即检测到规定的同步字符后,下面就连续按顺序传送数据,直到一块数据传送完毕。同步传送时,字符之间没有间隙,不需要起始位和停止位,仅在数据开始时用同步字符SYNC来指示。
  • 异步通信传输以字节为单位的,但是同步通信传输以数据块()为单位。
3.起始位、奇偶校验位、停止位、波特率
  • 起始位:在通信线上没有数据传送时处于逻辑“1”状态。当发送设备发送一个字符数据时,首先发出一个逻辑“0”信号,这个逻辑低电平就是起始位,发送器通过发送起始位而开始一个字符传送,接收方可用起始位使自己的接收时钟与发送方的数据同步,起始位所起的作用就是表示字符传送开始。
  • 奇偶校验位:在串口通信中一种简单的检错方式,数据通信时通信双方必须约定一致的奇偶校验方式。校验方法有奇校验偶校验0校验1校验以及无校验
  • 停止位:用于表示单个数据包的最后一位,可以为1位1.5位2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
  • 波特率:指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,这是一个衡量符号传输速率的参数。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,波特率为10位240个/秒=2400bps。
4.串口通信协议整理
  • UART:

    通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器。将数据由串行通信与并行通信间做传输转换,作为并行输入称为串行输出的芯片。UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。嵌入式设备中常常使用到的是TTLTTL转RS232的这种方式。常用的就三根引线:发送线TX接收线RX电平参考地线GND

    数据发送过程:空闲状态,线路处于高电平;当收到发送指令后,拉低线路的一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位,一帧数据发送完成。

    数据接收过程:空闲状态,线路处于高电平;当检测到线路的下降沿(高电平变为低电平)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备接收数据或存入缓冲。

    由于UART是异步传输,没有传输同步时钟,为了保证数据的正确性,UART采用16倍数据波特率的时钟进行采样。每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误码。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。

  • I2C:

    I2C(Inter-Integrated Circuit)是一种同步串行通信协议,其拥有一根数据线SDA和一根时钟线SCL。其总线通过上拉电阻与电源相连接。每个接到I2C总线上的器件都有唯一的地址。其中,主动发起操作的一方为主机,另外一方为从机。

    起始信号:SCL为高电平,SDA的电平由高跳到低表示开始信号

    终止信号:SCL为高电平,SDA的电平由低跳到高表示终止信号

    数据传输过程:当没有数据传输的时候,两根总线都为高电平;当采集IIC上的数据时,其时钟线SCL必须是高电平且SDA的数据必须保持稳定不变—将SDA的电平与SCL的高电平进行“与”操作后,以便确定SDA上是1还是0;在SCL为低电平的时候,SDA上的数据可以进行跳变。

    数据传输开始时,需要发送一个起始信号;数据传输结束后,需要发送一个终止信号;每8bit数据传输结束,都需要一个ACK。若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。起止信号都由Master发出,而ACK则可能由Master或者SLAVE来发出。

  • SPI:

    SPI(Serial Peripheral Interface)是一种同步串行通讯协议,由一个主设备和一个或多个从设备组成。其拥有四根硬脚引线,分别为MISO(主机输入,从机输出)MOSI(主机输出,从机输入)SCLK(串行时钟信号)SS(片选信号)。因为一个主设备可以挂多个从设备,则通过片选引脚对从设备进行选择。从设备的工作时钟则是来自于主设备的SCK线。

    数据传输过程:主机先将NSS信号拉低,保证开始接收数据。当接收端检测到时钟的边沿信号时,它将立即读取数据线上的信号,这样就得到了一位数据。主机发送到从机时:主机产生相应的时钟信号,然后数据一位一位地将从MOSI信号线上进行发送到从机。主机接收从机数据:如果从机需要将数据发送回主机,则主机将继续生成预定数量的时钟信号,并且从机会将数据通过MISO信号线发送。

实现方案(基于UART通信协议)

该实现方案分为三部分

1.无线通信模块

使用HJ-WUS1无线串口模块,分为 USB主机 与 无线从机,配置相同的本机地址,波特率与频率。

2.电脑与单片机之间通信

单片机使用:K60(库来自山外)

单片机---->电脑

实际上只要调用K60库中的uart_putstr函数或者uart_putchar函数就可以实现数据的发送。如果需要实现多个数据的发送,可以将数据存在一个数组中,将数组多次发送。

void Data_Send(UARTn_e uartn,unsigned short int *pst,uint8 x) 
{
  unsigned char _cnt=0;  unsigned char sum = 0;
  unsigned char data_to_send[23];         //发送缓存
  data_to_send[_cnt++]=0xAA;
  data_to_send[_cnt++]=0xAA;
  data_to_send[_cnt++]=x;
  data_to_send[_cnt++]=0;
  data_to_send[_cnt++]=(unsigned char)(pst[0]>>8);  //高8位
  data_to_send[_cnt++]=(unsigned char)pst[0];  //低8位
  data_to_send[_cnt++]=(unsigned char)(pst[1]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[1];
  data_to_send[_cnt++]=(unsigned char)(pst[2]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[2];
  data_to_send[_cnt++]=(unsigned char)(pst[3]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[3];
  data_to_send[_cnt++]=(unsigned char)(pst[4]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[4];
  data_to_send[_cnt++]=(unsigned char)(pst[5]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[5];
  data_to_send[_cnt++]=(unsigned char)(pst[6]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[6];
  data_to_send[_cnt++]=(unsigned char)(pst[7]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[7];
  data_to_send[_cnt++]=(unsigned char)(pst[8]>>8);
  data_to_send[_cnt++]=(unsigned char)pst[8];

  data_to_send[3] = _cnt-4;
  sum = 0;
  for(unsigned char i=0;i<_cnt;i++)
    sum += data_to_send[i];
        
    data_to_send[_cnt++] = sum;
    
        for(unsigned char i=0;i<_cnt;i++)
          uart_putchar (uartn,data_to_send[i] );
      
}

每次发送数据的时候都是8位8位发送的,但是一个数据一共16位,所以要将一个数据分成高八位低八位分别传输。最前面的4个数据对应的是 ANOTC匿名地面站V5的协议(帧头AAAA,功能字02,长度18,校验SUM,数据为飞机传感器数据)

打开ANOTC匿名,配置好对应的端口打开即可

电脑---->单片机

单片机里直接调用uart_getchar函数,可以每次传一个数字,然后经过计算拼接成浮点类型,也发送字符串,将字符串数组接收后进行拆解。这里的接收函数可以写在while(1),虽然没什么问题,但最好写在接收中断里。

3.单片机之间主从通信

我在这里设置了区分主机与从机的标志位。在主机发送数据的时候会记录发送的次数,从机接收数据的时候也会记录接收的次数,当次数达到一定的数的时候,主从机的角色进行交换,保存的数据清零。(老老实实使用两套代码,都写在一套里逻辑会显得凌乱)

//send.h

typedef struct
{
  int communicationStatus;
}Communication;

extern void AppDataSendFunction(void);
extern void Data_Communicate_Master(void);
extern void Data_Communicate_Send(void);
extern int j;
extern Communication communication;
//send.c

int j = 0;

// 0-->从机 1-->主机
Communication communication = 
{
  .communicationStatus = 1,
};

void Data_Communicate_Master(void)
{
  uart_putstr(UART3,"ADDKP;");
}

void Data_Communicate_Send(void)
{
  KeyMessage.KeyValue=KeyScan();
  
  if(communication.communicationStatus == 1)
  {
    if(KeyMessage.KeyValue == KEY_ADD)   
    { 
      j++;
      Data_Communicate_Master();
     
      if(j == 6)
      {
        j = 0;
        communication.communicationStatus = 0;
      }
    }
  }
}

void AppDataSendFunction(void)
{
  if(communication.communicationStatus == 1)
    Data_Communicate_Send();
}
//get.h

extern void AppDataGetFunction(void);
extern void Data_Communicate_Get(void);
extern void Data_Communicate_Slave(int i);
extern int i;
//get.c

int i = 0;

void Data_Communicate_Slave(int i)
{
  /** @StrToCmd(int i)			   
   * 实现接收字符串,发送的字符串以";"结尾作为末位判断标志,在用strcmp函数进行判			   	  * 断,如果结果 ==0 ,则通过oled打印传入的 i
  **/
  StrToCmd(i);
}

void Data_Communicate_Get(void)
{
  if(communication.communicationStatus == 0)
  {
    i++;
    Data_Communicate_Slave(i); 
    
     if(i == 6)
     {
       i = 0;
       communication.communicationStatus = 1;
     }
  }
}

void AppDataGetFunction(void)       
{
  if(communication.communicationStatus == 0)
    Data_Communicate_Get();
}

AppDataSendFunction()放在while(1)中,将AppDataGetFunction()放在接收中断中。主从机的转换即通过communicationStatus的转变实现,初始的时候一个单片机烧进1,另一个烧进0,就可以标志主从机。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值