当前位置:   article > 正文

【嵌入式】串口通信及其案例_嵌入式串口通信应用实践

嵌入式串口通信应用实践

目录

前言

1. 简介

1.1 UART口与COM口

1.2 UART口的特征

1.3 UART口兼容COM口

2.使用场景

3.工作模式(仅了解即可)

3.1 单工模式(Simplex)

3.2 半双工模式(Half duplex)

3.3 全双工模式(Full duplex)

4.RS232串口通信标准和接口定义

4.1外观

4.2物理层

4.3协议层

4.3.1 波特率

4.3.2 通讯的起始位和停止位

4.3.3 数据位

4.3.4 数据校验

4.3.5 数据传输率

5. USB转RS232串口电路

6.演示RS232串口协议

6.1 STM32F103芯片

6.2 下位机开发板介绍

6.2.1 下位机开发板中STM32F103ZET芯片引脚分配图

 6.2.2 下位机串口原理图

6.3 下位机串口通信代码实现

6.4 上位机串口通信代码实现

6.5 USB转RS232串口驱动安装

6.6 效果展示

7.应用:读取温度传感器(DS18B20)的传感值

7.1 DS18B20温度传感器介绍

7.2 下位机开发板上DS18B20原理图 

7.3 下位机实现读取DS18B20传感器温度值

7.4 实现上位机与下位机通信的串口指令

7.5 上位机温度曲线显示功能实现

7.6 效果展示

参考


前言

      本文简要介绍串口概念、物理接口、通信协议,最后借助串口从下位机将温度传感器的实测温度上传至上位机进行展示。本文不以详细教授嵌入式编程技巧,仅展示串口通信和嵌入式编程的大概流程,仅作为“科普”目的。阅读此文无需具备嵌入编程经验,感兴趣即可。

1. 简介

       串行通信端口( cluster communication port ),简称串口,即COM口。串行通信是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,但传送速度较慢(后文会计算其传输速度)。

1.1 UART口与COM口

      嵌入式里面说的串口,一般是指UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)口, 但是我们经常搞不清楚UART口和COM口的区别,  实际上UART、COM指的物理硬件接口形式。

图1-1-1 UART接口

      UART有4个引脚(VCC, GND, RX, TX), 用的TTL电平,  低电平为0(0V),高电平为1(3.3V或以上),如图1-1-1所示。

图1-1-2 RS232串口(母头)

      COM口是台式机上面常用的接口(图1-1-2),9个引脚, 用的RS-232标准电平,  使用负逻辑电平,定义+3 ~ +15V为低电平,而-15 ~ -3V为高电平。

1.2 UART口的特征

       一般uart控制器在嵌入式系统里面都做在cpu一起,像飞思卡尔的IMX6芯片、意法半导体的STM32芯片就是这样,有多个uart控制器。
       UART引脚介绍(COM口有9个引脚,但是常用的也是下面这几个):

              VCC:供电引脚,一般是3.3v,在我们的板子上没有过电保护,这个引脚一般不接更安全
              GND:接地引脚,有的时候rx接受数据有问题,就要接上这个引脚,一般也可不接
              RX:接收数据引脚
              TX:发送数据引脚

1.3 UART口兼容COM口

      由于UART口的VCC、GND、RX、RT引脚与COM口的这几个引脚具有一样的用途和功能。而绝大多数COM口通信中也仅需要使用这四个引脚,因此在很多场景下UART口可以作为COM口的兼容口使用。

2.使用场景

       串行接口(COM口)是一种可以将接受来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接受的串行数据流转换为并行的数据字符供给CPU的器件。

  串口是计算机上一种非常通用的设备通信协议。大多数计算机(不包括笔记本电脑)包含两个基于RS-232的串口,串口同时也是仪器仪表设备通用的通信协议。同时,串口通信协议也可以用于获取远程采集设备的数据。

  RS-232(ANSI/EIA-232标准)是IBM-PC及其兼容机上的串行连接标准。可用于许多用途,比如连接鼠标、打印机或者Modem(调制解调器),同时也可以接工业仪器仪表。RS-232只限于PC串口和设备间点对点的通信。RS-232串口通信最远距离是50英尺(15.24米)。

3.工作模式(仅了解即可)

      串行通信的基础是单线传输,数据通常是在两个站点之间进行传输,按照数据流的方向分为3种传输模式:

3.1 单工模式(Simplex)

      单工模式的数据是单向的,通讯双方一方为发送端,另一方则固定为接收端。信息只能沿一个方向传输,使用一根数据线。例如收音机,只能接收发射塔给它的数据,并不能给发射塔发数据。

这里写图片描述
图 3-1-1 单工通信模式


3.2 半双工模式(Half duplex)

      半双工模式是指通讯双方都具有发送器和接收器,双方既可以发射也可以接收,但是接收和发射不能同时进行。半双工一般用在两个方向都传输数据的场合,例如对讲机。

这里写图片描述
图 3-2-1 半双工通信模式

3.3 全双工模式(Full duplex)

      全双工数据通讯分别由两根可以在两个不同的站点同时发送和接收的传输线进行传输数据,通讯双方能在同一时刻进行发送和接收操作。

      全双工模式下,每一端都有发送器和接收器,有两条传输线可以在交互式应用场合中使用,信息传输效率高,例如手机。

这里写图片描述
图 3-3-1 全双工通信模式

      此文我们研究的RS232串口标准通信就可以工作在全双工通信模式下。因其将数据传输和接收采用独立的2根线实现,因此可以同时发送和接收数据。接下来将详细介绍RS232串口标准。

4.RS232串口通信标准和接口定义

4.1外观

图 4-1-1 RS232串口硬件接口

4.2物理层

       这里讲解RS-232标准,RS-232标准主要规定了信号的用途,通讯接口以及信号的电平标准。
       使用RS-232标准串口的设备间通信结构图如下:

这里写图片描述
图4-2-1 RS232标注串口设备间通信结构  [注:DB9是旧式计算机中RS232标准接口的称呼]

       两个通讯设备的“DB9接口”之间用串口线建立起连接,串口线中使用RS-232标准传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以信号需要经过一个“电平转换芯片”转换成控制器能识别的“TTL标准”电平[参看 表4-2-1]信号。
       RS232通信使用的电平标准详细表如下:

这里写图片描述
表 4-2-1 RS232通信使用的电平标准


        常见的电子电路中常用TTL的电平标准。理想状态下使用5V表示二进制逻辑1,使用0V表示二进制逻辑0。为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V表示逻辑1,+15V表示逻辑0。
        其中接线口以针式引出信号线的称为公头(图 4-1-1 右侧),以孔式引出信号线的称为母头(图 4-1-1 左侧)。
        DB9 接口中的公头及母头的各个引脚的标准信号线接法:

这里写图片描述
图4-2-2 RS232标准DB9公/母头及其引脚

       RS-232的每一个引脚都有它的作用,也有信号流动方向。原先的RS-232是用来连接调制解调器[注:俗称“猫”,Modem,是Modulator(调制器)与Demodulator(解调器)的简称],因此它的引脚位意义通常也和调制解调器传输相关。随着行业发展,RS-232标准接口被用到更多的设备上。
       从功能上来看,全部信号线分为3类:数据线(TXD、RXD)、地线(GND)和联络控制线(DSR、DTR、RI、DCD、RTS、CTS)

这里写图片描述
表 4-2-2 RS232各引脚在上位机和下位机之间的作用以及信号流向


       两个通讯设备之间的地电位可能不一样,这会影响收发双方的电平信号,所以两个串口设备之间必须要使用地线连接,即共地。
       串口线中的联络控制线使用逻辑1表示信号有效,逻辑0表示信号无效。例如,当计算机端控制DTR信号线为逻辑1时,其目的是告知远端的调制解调器本机已准备好接收数据,0表示还没准备就绪。
       在目前的其它工业控制使用的串口通讯中,一般只使用RXD、TXD以及GND三条信号线,直接传输数据信号,RTS、CTS等联络控制线被裁剪掉。

4.3协议层

       根据通讯的数据同步方式,可分为同步和异步两种,根据通讯过程中是否使用到时钟信号进行区分
       在同步通信中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数据。例如,通讯中通常双方会统一规定在时钟信号的上升沿或者下降沿对数据线进行采样。
       在异步通信中,不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些用于同步的信号位,或者将主题数据进行打包,以数据帧的格式传输数据。通讯中还需要双方规约好数据的传输速率等,以便更好地同步。
       两种同步方式优劣:在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符,所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。

       本文讲解常用的“异步通信”数据同步方式。

       串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口,通讯双方的数据包格式要规约一致才能正常收发数据。串口通讯协议层中,规定了数据包的内容,它由起始位、主体数据位、校验位以及停止位组成。如图4-3-1所示。

这里写图片描述
图 4-3-1 串口异步通信字符帧


 

4.3.1 波特率

        讲波特率之前首先了解一下通信速率。
        通信速率通常是以比特率来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易和比特率混淆的概念是“波特率”,它表示每秒传输了多少码元。
        码元是通信信号调制的概念,时间间隔相同的符号来表示一个二进制数字,这样的信号就称为码元。如常见的通信传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;若传输中,有0V、2V、4V和6V分别表示00、01、10、11,那么每个码元可以表示四种状态,两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通信中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率,其实二者是有区别的。而RS232串口通信中,一个码元等于一个二进制位。
        异步通信由于没有时钟信号(DB9接口[RS232标准口的另一种称呼]没有信号线),所以两个通信设备需要规约好波特率,即每个码元的长度,以便对信号进行解码。图 4-3-1中一个矩形表示一个码元。常见的波特率为4800,9600,115200,921600。

4.3.2 通讯的起始位和停止位

       串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑0的数据位表示,而数据包的停止信号可由0.5、1、1.5或2个逻辑0/1的数据位表示,只要双方约定一致即可。图 4-3-1中约定起始位/停止位分别是1个逻辑0和1个逻辑1的数据位表示。

4.3.3 数据位

      在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为5、6、7 或8 位长。图 4-3-1中的D0~D7。

4.3.4 数据校验

       在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。
奇校验:要求有效数据和校验位中“1”的个数为奇数
偶校验:与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数
0/1校验:不管有效数据中的内容是什么,校验位总为0或者1
 

4.3.5 数据传输率

       由图4-3-1可以计算出一个“字符帧”的有效数据比率。一个字符帧由4部分组成:
               数据位:D0~D7,占据8位
               起始位:1位
               奇偶校验位:1位
               停止位:1位
       即一个字符帧一共有11位,那么“一个字符帧的有效数据传输比率= 8 / 11”。
       在RS232串口通信中,一个码元等于一个二进制位,即此时“波特率=比特率”。假设在某次数据传输过程中,字符帧连续传输,无空闲,则常用波特下的数据传输比率如下:
               波特率4800,换算比特率即 4800bps: 有效数据传输率 = 4800 bps*( 8/11) = 3490 bps = 3490 bps / 8 = 436 B/s
               波特率9600,换算比特率即 9600bps: 有效数据传输率 = 9600 bps*( 8/11) = 6981 bps = 6981 bps / 8 = 872 B/s
               波特率115200,换算比特率即 115200bps: 有效数据传输率 = 115200 bps*( 8/11) = 83781 bps = 83781 bps / 8 = 10472 B/s
               波特率921600,换算比特率即 921600bps: 有效数据传输率 = 921600 bps*( 8/11) = 670254 bps = 670254 bps / 8 = 83781 B/s
       可见RS232串口传输速率相比以太网的确慢很多,因此不适合用于大数据传输场景,适合传输控制信号和小数据量。

5. USB转RS232串口电路

        随着USB接口及其协议的普及,串口也逐渐退出了公众视野,仅存于某些特定设备行业中。很多个人电脑已经不再有RS232串口硬件接口,如果需要使用RS232串口可以通过USB接口转RS232串口线(如图 5-1)来连接串口设备。
 

图 5-1 USB转RS232串口线

        由于USB接口使用4根线(如图 5-2),而且USB协议与RS232使用的9根线及其协议完全不一样,因此需要一个USB转RS232的电路(如图 5-3)完成协议的转换。

图 5-2 USB TypeA 接口
图 5-3 USB转UART电路


6.演示RS232串口协议

       本节将介绍如何基于STM(意法半导体)设计的STM32F103芯片(基于ARM Cotex-M3核心)演示RS232串口通信。STM32F103芯片内部已经实现了RS232标准的串口协议,接下来将详细介绍。

6.1 STM32F103芯片

图 6-1-1 STM32F103ZET系列芯片引脚布局

6.2 下位机开发板介绍

         本文我们仅关注STM32F103ZET芯片(图6-2-1中心的那块芯片)、RS232串口、DS18B20温度传感器。

图6-2-1 下位机开发板

6.2.1 下位机开发板中STM32F103ZET芯片引脚分配图

图 6-2-1-1下位机开发板中STM32F103ZET芯片引脚分配图

 6.2.2 下位机串口原理图

图 6-2-2-1 实现RS232标准的SP3232芯片引脚与COM口连接原理图

6.3 下位机串口通信代码实现

       初始化串口配置

  1. /****************************************************************************
  2. * Function Name : USART1_Config
  3. * Description : Configurates the USART1.
  4. * Input : baudRate:波特率
  5. * Output : None
  6. * Return : None
  7. ****************************************************************************/
  8. void USART1_Config(uint16_t baudRate)
  9. {
  10. GPIO_InitTypeDef GPIO_InitStructure;
  11. USART_InitTypeDef USART_InitStructure;
  12. /* 打开RCC时钟 */
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  15. /* 设置TXD的GPIO参数 */
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  17. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  18. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //串口输出PA9
  19. /* 初始化串口输入IO */
  20. GPIO_Init(GPIOA, &GPIO_InitStructure);
  21. /* 设置RXD的GPIO参数 */
  22. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //模拟输入
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //串口输入PA10
  24. GPIO_Init(GPIOA, &GPIO_InitStructure);
  25. /* 设置USART的参数 */
  26. USART_InitStructure.USART_BaudRate = baudRate; //波特率
  27. USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据长8位
  28. USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
  29. USART_InitStructure.USART_Parity = USART_Parity_No; //无效验
  30. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//使能硬件流
  31. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //开启发送和接受模式
  32. /* 初始化USART1 */
  33. USART_Init(USART1, &USART_InitStructure);
  34. /* 使能USART1 */
  35. USART_Cmd(USART1, ENABLE);
  36. #ifdef USE_USART1RX_INTERRUPT
  37. USART1_NVIC_RxConfig();
  38. USART_ITConfig(USART1, USART_IT_RXNE ,ENABLE);
  39. #endif
  40. }

       实现串口数据接收函数:串口的数据接收是通过硬件中断(USART1中断)发生时,调用中断响应函数USART1_IRQHandler实现

  1. /****************************************************************************
  2. * Function Name : USART1_NVIC_RxConfig
  3. * Description : 设置接收中断的中断等级,并打开总中断
  4. * Input : None
  5. * Output : None
  6. * Return : None
  7. ****************************************************************************/
  8. void USART1_NVIC_RxConfig(void)
  9. {
  10. NVIC_InitTypeDef NVIC_InitStructure;
  11. /* 设置NVIC参数 */
  12. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
  13. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级为0
  14. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //打开USART1的全局中断
  15. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能。
  16. NVIC_Init(&NVIC_InitStructure);
  17. }
  18. /****************************************************************************
  19. * Function Name : USART1_IRQHandler
  20. * Description : 串口1的中断函数
  21. * Input : None
  22. * Output : None
  23. * Return : None
  24. ****************************************************************************/
  25. uint8_t USART1_RX_Buff[64], USART1_RX_State;
  26. void USART1_IRQHandler (void)
  27. {
  28. uint8_t dat;
  29. /* 接收中断(接收到的数据必须是0x0D 0x0A结尾(即回车))表示接收结束 */
  30. if(USART_GetITStatus(USART1, USART_IT_RXNE))
  31. {
  32. dat = USART_ReceiveData(USART1);
  33. /* 没接收完,使用USART1_RX_State的最高位作结束标识 */
  34. if(((USART1_RX_State & 0x80) == 0) && ((USART1_RX_State & 0x3F) < 63))
  35. {
  36. /* 已经接收到0x0D,使用USART1_RX_State的第6位(从0位开始)作接收到0x0D的标识 */
  37. if(USART1_RX_State & 0x40)
  38. {
  39. if(dat == 0x0A) //第二个字节接收到0x0A接收结束
  40. {
  41. USART1_RX_State |= 0x80;
  42. USART1_RX_Buff[USART1_RX_State & 0x3F] = 0;
  43. }
  44. else
  45. {
  46. USART1_RX_State = 0;
  47. }
  48. }
  49. else
  50. {
  51. if(dat == 0x0D) //接收到0x0D,标识USART1_RX_State的第6位(从0位开始)
  52. {
  53. USART1_RX_State |= 0x40;
  54. }
  55. else
  56. {
  57. USART1_RX_Buff[USART1_RX_State & 0x3F] = dat;
  58. USART1_RX_State++;
  59. }
  60. }
  61. }
  62. }
  63. }

       实现串口数据发送函数 

  1. /****************************************************************************
  2. * Function Name : USART1_SetWord
  3. * Description : 通过串口1发送字符串.
  4. * Input : None
  5. * Output : None
  6. * Return : None
  7. ****************************************************************************/
  8. void USART1_SendWord(uint8_t *wd)
  9. {
  10. while(*wd) //检测是否发送数据是否为空
  11. {
  12. USART_SendData(USART1, *wd);
  13. while (USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //等待发送完毕
  14. wd++;
  15. }
  16. }
  17. // 串口数据发送函数
  18. void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
  19. {
  20. /* Check the parameters */
  21. assert_param(IS_USART_ALL_PERIPH(USARTx));
  22. assert_param(IS_USART_DATA(Data));
  23. /* Transmit Data */
  24. USARTx->DR = (Data & (uint16_t)0x01FF);
  25. }

6.4 上位机串口通信代码实现

       上位机服务程序采用Qt库(C++ GUI库以及框架)实现。

       创建串口通信对象(QSerialPort类实现了串口通信客户端功能)

serial = new QSerialPort(this);

       初始化串口通信参数

  1. void MainWindow::openSerialPort()
  2. {
  3. SettingsDialog::Settings p = settings->settings();
  4. serial->setPortName(p.name);
  5. serial->setBaudRate(p.baudRate);
  6. serial->setDataBits(/*p.dataBits*/QSerialPort::Data8);
  7. serial->setParity(/*p.parity*/QSerialPort::NoParity);
  8. serial->setStopBits(/*p.stopBits*/QSerialPort::OneStop);
  9. serial->setFlowControl(/*p.flowControl*/QSerialPort::NoFlowControl);
  10. if (serial->open(QIODevice::ReadWrite)) {
  11. console->setEnabled(true);
  12. console->setLocalEchoEnabled(p.localEchoEnabled);
  13. ui->actionConnect->setEnabled(false);
  14. ui->actionDisconnect->setEnabled(true);
  15. ui->actionConfigure->setEnabled(false);
  16. showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6")
  17. .arg(p.name).arg(p.stringBaudRate).arg(p.stringDataBits)
  18. .arg(p.stringParity).arg(p.stringStopBits).arg(p.stringFlowControl));
  19. } else {
  20. QMessageBox::critical(this, tr("Error"), serial->errorString());
  21. showStatusMessage(tr("Open error"));
  22. }
  23. }

     串口读写

  1. void MainWindow::readData()
  2. {
  3. QByteArray data = serial->readAll();
  4. }
  1. void MainWindow::writeData(const QByteArray &data)
  2. {
  3. serial->write(data);
  4. }

6.5 USB转RS232串口驱动安装

       如果你使用的USB转RS232串口线是非通用型,则需要到你所购买此线的厂家官网下载对应驱动,然后安装到操作系统。例如我用的是“优越者Y-105系列USB转串口线”,则需要在其官网下载“PL2303u2c_veryhuo.com.rar”驱动(如图6-5-1),然后安装到操作系统中:

图6-5-1 特定厂商的USB转串口驱动

        安装成功后,使用此USB转RS232串口线接下位机,Windows“设备管理器”中的“端口(COM和LPT)”节点(如图6-5-2)下即会出现如图6-5-3所示的COM设备(COM设备节点是从1编号,如COM1、COM7)

图6-5-2 COM设备节点

图6-5-3 COM7设备

        如果你购买的USB转RS232串口线是通用型(CH340\CH341),则无需安装驱动,Windows系统直接支持,即插即用!如图6-5-4所示

图6-5-4 通用型USB转RS232串口线

6.6 效果展示

图6-6-1 上位机连接下位机串口成功

7.应用:读取温度传感器(DS18B20)的传感值

7.1 DS18B20温度传感器介绍

图 7-1-1 DS18B20 产品特性及引脚介绍

      翻译图 7-1-1中DS18B20的产品特性介绍:

        特征
        % 独特的1-Wire(1线)接口仅需一个端口引脚即可进行通信
        % 多点功能简化了分布式温度感测应用
        % 无需外部组件
        % 可以通过数据线供电。 电源范围为3.0V至5.5V
        % 零待机功耗
        % 可测量-55°C至+ 125°C的温度。 华氏温度等效值为-67°F至+ 257°F
        % -10°C至+ 85°C的±0.5°C精度
        % 温度计分辨率可在9到12位(bit位)之间编程
        % 在750毫秒内(最大)将12位(bit位)温度转换为数字字(2Byte)
        % 用户定义的非易失性温度警报设置
        % 警报搜索命令可识别和寻址温度超出编程限制的设备(温度警报条件)
        % 应用场景包括恒温控制,工业系统,消费产品,温度计或任何热敏系统

7.2 下位机开发板上DS18B20原理图 

图 7-2-1 DS18B20原理图
图 7-2-2 DS18B20产品文档:使用VDD来供应温度转换电流

7.3 下位机实现读取DS18B20传感器温度值

表7-3-1 DS18B20 输出数据与温度关系表

7.4 实现上位机与下位机通信的串口指令

上位机代码

       上位机向下位机发送"g\r\n"字符串

  1. void MainWindow::openSerialPort()
  2. {
  3. // 略:串口初始化,参看第6节
  4. timer->start(TIMER_INTVAL); // 开启定时器,定时发送获取温度指令
  5. } else {
  6. // 略
  7. }
  8. }
  9. // 执行定时任务函数:发送获取温度指令
  10. void MainWindow::sendTempCommand()
  11. {
  12. writeData("g\r\n");
  13. }

下位机代码

       下位机接收到指令后进行解析,如果收到"g\r\n"指令则向上位机发送DS18B20的温度值,格式为: "+25.2\r\n" 或者 "-25.2\r\n"

  1. int main(void)
  2. {
  3. uint32_t i = 0;
  4. int16_t tempValue;
  5. uint8_t tempStr[8] = {0,0,0,0,0,0,0,0}; /*温度字符串*/
  6. uint16_t len;
  7. /* 初始化 */
  8. FLASH_Init(); //FLASH芯片初始化
  9. SYSTICK_Config(); //系统时钟初始化
  10. USART1_Config(9600); //USART1串口初始化
  11. NVIC_Config(); //初始化中断分组
  12. DS18B20_Init(); //温度传感器初始化
  13. while(1)
  14. {
  15. /* 方案1:周期性主动发送DS18B20温度数据 */
  16. // 方案2:收到获取温度指令后再发送温度值
  17. tempValue = DS18B20_ReadTemp();
  18. GUI_DisplayTempData(tempValue);
  19. // 检测是否收到温度获取指令
  20. if((USART2_RX_State & 0x80))
  21. {
  22. // 取得接收到的数据长度
  23. USART2_RX_State = USART2_RX_State & 0x3F;
  24. if( 'g' == USART2_RX_Buff[0]) // 收到获取温度指令:g\r\n
  25. {
  26. //将温度发回串口
  27. memset(tempStr, 0, sizeof(tempStr));
  28. int16ToChar(tempValue, tempStr);
  29. for(len=0; len < sizeof(tempStr); len++)
  30. {
  31. USART_SendData(USART2, tempStr[len]);
  32. while (USART_GetFlagStatus(USART2, USART_FLAG_TC) != SET); //等待发送完毕
  33. }
  34. }
  35. USART2_RX_State = 0;
  36. }
  37. } // end while
  38. }
  39. /****************************************************************************
  40. * Function Name : DS18B20_ReadTemp
  41. * Description : 读取温度值.
  42. * Input : None
  43. * Output : None
  44. * Return : temp:读取到的温度值,并保留两位小数点(百倍)。
  45. ****************************************************************************/
  46. int16_t DS18B20_ReadTemp(void)
  47. {
  48. int16_t tempValue = 0;
  49. uint16_t temp;
  50. DS18B20_Reset();
  51. DS18B20_WriteData(0xCC); //跳过ROM操作命令
  52. DS18B20_WriteData(0x44); //温度转换命令
  53. /* 如果不是连续读的话,这里加个延时。DS18B20转换温度需要时间 */
  54. // SYSTICK_Delay1ms(200);
  55. DS18B20_Reset();
  56. DS18B20_WriteData(0xCC); //跳过ROM操作命令
  57. DS18B20_WriteData(0xBE); //发送读取温度命令
  58. /* 读取16位温度值,先读低位 */
  59. temp = DS18B20_ReadData();
  60. tempValue = DS18B20_ReadData();
  61. tempValue <<= 8;
  62. tempValue |= temp;
  63. temp = tempValue;
  64. /* 温度处理,温度保留两位小数点 */
  65. if(tempValue < 0)
  66. {
  67. temp = temp - 1;
  68. temp = ~temp;
  69. temp = (float)temp * 6.25 + 0.5;
  70. return (-temp);
  71. }
  72. else
  73. {
  74. temp = (float)temp * 6.25+ 0.5;
  75. return temp;
  76. }
  77. }

7.5 上位机温度曲线显示功能实现

  1. void MainWindow::readData()
  2. {
  3. QByteArray data = serial->readAll();
  4. QString str(data);
  5. tempStr += str;
  6. QStringList strList;
  7. QRegExp rx("(\\+\\d+\\.\\d+)\\r\\n");
  8. int pos = rx.indexIn(tempStr);
  9. if( -1 < pos)
  10. {
  11. qDebug() << tempStr;
  12. tempStr.clear();
  13. strList.append(rx.cap(1));
  14. }
  15. else
  16. {
  17. return;
  18. }
  19. for(int i = 0; i < strList.size(); ++i)
  20. {
  21. QString item = strList.at(i);
  22. if( item.length() < 5) continue;
  23. float num = item.toFloat();
  24. qint64 current_time_total_ms = QDateTime::currentMSecsSinceEpoch();
  25. lineseries.append(current_time_total_ms + TIMER_INTVAL, num);
  26. }
  27. }

7.6 效果展示

       图7-6-1中保鲜膜包裹的即为DS18B20温度传感器(为测试水温,包裹保鲜膜保护传感器)。 

图7-6-1 基于STM32F103 MCU的硬件平台--DS18B20温度传感下位机搭建

       上位机收到下位机的持续温度值之后就可以绘制曲线了。图7-6-2中的曲线是使用Qt的QtCharts::QChart类实现。
       首先一小段平缓曲线是“室温”,接下来将DS18B20放入温水中,观察曲线进入上升阶段直至60.5度附近,然后将DS18B20从温水中取出,曲线进入缓慢下降阶段。向温水中掺入一定量开水,再将DS18B20放入热水中,观察发现曲线陡然快速上升,达到80.8度附近将DS18B20从热水中取出。最后放置在空气中,静待其降温!
       整个操作过程中完成温度曲线如图7-6-3所示。

注意:整个热水温度测量时长为10分钟,观察Gif图片效果需要点耐心。

图7-6-2 上位机显示温度传感器的温度变化(室温-温水-开水)

图7-6-3 温度传感器温度变化曲线

参考

[1] UART简介及与COM口的区别

[2] 串口通信的具体用途是什么

[3] 串口通讯介绍

[4] 串口通信简介

[5] DS18B20 Programmable Resolution 1-Wire® Digital Thermometer

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号