实训Day04

2021-05-27

实训, UART

UART通信

0x01 什么是UART?

  • 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器

    • 同步通信:发送方发出数据后,等接收方发回响应以后才发下一个数据包

    • 异步通信:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式

    • 总结

      • 同步通信是阻塞方式,异步通信是非阻塞方式
      • 常见通信总线协议中,I2C、CPI属于同步通信,UART属于异步通信
    • 注意

      • 同步通信的通信双方必须先建立同步,双方的时钟需要调整到同一个频率

      • 异步通信两个通信设备没有时钟信号,因此需要约定好波特率,常见的有4800,9600,115200等,土工室数据起始位和停止位必不可少

  • 一种常用也是最简单的串行数据传输协议。数据线只需要两根就可以实现全双工

    • Tx:发送数据线

    • Rx:接收数据线

      image-20210528101648592

      RS-232标准:上位机和下位机串口通信时的接线方式,主要是接线需要接对

0x02 上位机和下位机

通信,通信,两台设备才能进行通信

  • 模块之间的通信,根据其在系统中的功能与作用可分为上位机 与 下位机
  • 上位机:处理性能强的计算机,数据的统一处理都在上位机完场
  • 下位机:数据采集和发送的终端,处理性能单一的计算机
  • 注意:一般一个上位机会对应多个下位机,这种模式可以使数据处理的更加全面,同时也大大降低成本。

0x03 UART数据帧

  • 在协议层(UART protocol 串口协议),规定了数据包的内容,串口发送和接收的数据包都是以帧为单位,Frame

  • 数据包(帧Frame)内容组成:起始位(1bit)+数据位(5~9bit)+校验位(0/1bit)+停止位(0.5,1,1.5bit)

    • 起始位:一个周期的低电平

    • 数据位:5~9bits数据位,具体多少需要双方协商,并优先传送最低位(LSB)

    • 校验位

      • 0校验:校验位总为“0”

      • 1校验:校验位总为“1”

      • 奇校验:有效数据和校验位中“1”的个数为奇数

        譬如0101,2个1,为达到奇校验,校验位为“1”

      • 偶校验:有效数据和校验位中“1”的个数为偶数

        譬如0100,1个1,为达到偶校验,校验位为“1”

    • 停止位:高电平,1个停止位

  • 波特率(Baudrate):传输速率,即每一秒传输多少个bit为

    • 4800
    • 9600
    • 115200

0x04 串口配置

1.结构体halUARTCfg_t

  • halUARTCfg_t结构体是串口初始化的结构体,可以对串口的初始化进行相应设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct
{
bool configured;//是否设置串口
uint8 baudRate; //波特率设置
bool flowControl;//控制流设置
uint16 flowControlThreshold;//RX缓存安全字节数(了解)
uint8 idleTimeout;//RX来数据超时时间(了解)
halUARTBufControl_t rx;//接收数据长度
halUARTBufControl_t tx;//发送数据长度
bool intEnable;//中断使能
uint32 rxChRvdTime;//接收数据时间
halUARTCBack_t callBackFunc;//回调函数
}halUARTCfg_t;

2.串口初始化实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void InitUart(void)
{
halUARTCfg_t uartConfig; // 声明一个halUARTCfg_t结构体
uartConfig.configured = TRUE;
uartConfig.baudRate = HAL_UART_BR_9600; // 波特率直接修改数字
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = 64;
uartConfig.rx.maxBufSize = 128;
uartConfig.tx.maxBufSize = 128;
uartConfig.idleTimeout = 6;
uartConfig.intEnable = TRUE;
uartConfig.callBackFunc = SerialApp_CallBack;
HalUARTOpen (HAL_UART_PORT_0, &uartConfig); // 初始化,将前面配置的uartConfig作为参数传递进去
}
  • 最后的HalUARTOpen函数,将串口初始化结构体作为参数对串口0进行初始化

3.串口读操作函数

  • 串口的读操作函数为HalUARTRead(),主要功能是读取上位机通过串口发送至设备节点的数据。

  • uint16 HalUARTRead(uint8 port, uint8 *buf, uint16 len)

    • @port:串口端口的设置,可以设置为串口0或串口1;

    • @buf:数据缓冲区;

    • @len:数据长度。

    • 返回值:实际成功读取的字节数

4.串口写操作函数

  • 串口的写操作函数为HalUARTWrite(),主要功能是实现向上位机或其他设备通过串口发送数据。

    • uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len)

    • @port:串口端口的设置,可以设置为串口0或串口1;

    • @buf:数据缓冲区;

    • @len:数据长度。

    • 返回值:实际成功发送的字节数

0x05 DHT11温湿度传感器

只简单介绍使用过的部分,因为只是进行了简单的使用,并没有深入了解

image-20210528110346937

1.DHT11数据传输协议

  • DHT11通过DATA线与MCU实现半双工通讯。每次通讯时间4ms左右(听不懂)

  • 数据:小数部分和整数部分

  • 数据传输:一次完整的数据传输为40bit,高位先出

  • 数据格式

    40bits = 8bit湿度整数数据+8bit湿度小数数据

        +8bit温度整数数据+8bit温度小数数据
        +8bit**校验和** 
    
  • 校验

    • 数据传送正确时,校验和数据 = 8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据所得结果的末8位。
    • 即校验和数据 =( 8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据)% 256(2的8次方)

2.使用DHT11温湿度显示数据

1).P0_4引脚初始化

  • DHT11使用的是CC2530的P0_4引脚,初始化引脚的输入和输出模式
  • 在dht11.c中进行初始化,具体CPIO初始化可参考Day02记录
1
2
3
4
5
6
7
8
9
10
11
12
13
void DATA_IO_input_Cfg(void)//设置为上拉输入模式
{
P0SEL &= 0xEF; // 0001 0000把P0_4设置为普通模式
P0DIR &= 0xEF; // 1110 1111把P0_4置为输入模式
P1INP &= 0xEF; // 1110 1111 把P1_2设置为上下拉模式
P2INP &= 0xDF; // 1101 1111 把P1设置为上拉模式
}

void DATA_IO_output_Cfg(void)//设置为输出模式
{
P0SEL &= 0xEF; // 0001 0000把P0_4设置为普通模式
P0DIR |= 0x10; // 0001 0000把P0_4置为输入模式
}
  • 因为数据采集和处理函数已经给我们准备好了,因此我们只要在Z-stack协议栈中创建事件进行调用即可

    数据采集和处理函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    int Start_DHT11(void)//开始采集数据
    {
    unsigned char buf[5];
    DATA_IO_output_Cfg();//设置为输出模式
    DATA = 0;//发送开始信号
    MicroWait(20000);//延时至少18ms
    DATA = 1;//拉高20~40us
    MicroWait(40);

    DATA_IO_input_Cfg();//设置为输入模式
    while(!(DATA ==0));//等DHT11回响应信号

    while(!(DATA ==1));//等DHT11把总线拉高,准备接收数据
    buf[0] = Read_Byte();
    buf[1] = Read_Byte();
    buf[2] = Read_Byte();
    buf[3] = Read_Byte();
    buf[4] = Read_Byte();

    if((buf[0]+buf[1]+buf[2]+buf[3])%256 == buf[4])
    {
    humidity = buf[0];
    temperature = buf[2];
    return 1;
    }
    return 0;
    }

步骤:为事件定义编码-宏–>添加提取事件类处理代码–调用事件设置函数

2).为事件定义编码-宏

在GenericApp.h中添加

1
#define GENERICAPP_MY_EVT1       0x0004

3).添加提取事件类处理代码

GenericApp.c中的GenericApp_ProcessEvent()函数中最后部分添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ( events & GENERICAPP_MY_EVT1 )
{
unsigned char temp[32];
if(Start_DHT11() == 1) // 此处调用了dht11.c中的温湿度采集和处理程序
{
sprintf(temp,"temperature:%d,humidity:%d\r\n",temperature,humidity);
HalUARTWrite(HAL_UART_PORT_0,temp,strlen((char *)temp));
}
else
{
HalUARTWrite(HAL_UART_PORT_0,"error\r\n",strlen("error\r\n"));
}
return (events ^ GENERICAPP_MY_EVT1); // 清零
}

4).添加调用事件设置函数代码

GenericApp.c中的GenericApp_ProcessEvent()函数中switch中添加

1
2
3
4
if (GenericApp_NwkState == DEV_ZB_COORD) // 网络状态变成协调器
{
osal_set_event(GenericApp_TaskID,GENERICAPP_MY_EVT1);
}

附录

串口通信(UART)介绍