项目知识点梳理和总结

1. 硬件部分(开发板外围)

1.1 DS18B20——数字温度传感器(测量充电桩表面温度)(充电桩正常温度范围是-20~50°C

(1)传感器参数

        VDD:电源输入(3.0V - 5.5V)

        GND:地

        DQ:数字输入/输出(单总线(OneWire协议))

        工作电压:3.0V - 5.5V

         测量温度范围:-55°C到+125°C(适用充电桩内部温度测量)

         精度:-10°C到+85°C范围内为±0.5°C,其余范围内略有下降

         分辨率:可编程的9到12位分辨率,默认值为12位(精度为0.0625°C)

         数据位输入:一次传输两个字节十六个比特,前十二位是温度整数位,后四个字节是温度小数位,所以分辨率是0.0625°C

(2)选型原因

        1. 适配开发板的GPIO单总线传输、输入电压范围(3~5.5V)

        2. 测量精度范围是±0.5°C,适用

        3. 当初温度传感器在DS18B20与DHT11之间进行选型,相比DHT11测量精度更高,测量温度范围更大,且不需要测量湿度,节省数据传输开销

(3)涉及知识点与传感器工作原理

<1>单总线协议(OneWire协议)

        (1)定义

        单总线协议是一种用于单根数据线进行通信的串行通信协议,只需要一根数据线同时进行数据传输和设备控制

        (2)原理

        1. 主设备(2440)通过定时器(或延时函数),有时间间隔的控制数据线上的高低电平变化,来发送和接收数据

        从设备(DS18B20)通过接收主设备发送的电平信号将采集的数据通过数据线发送给主设备(2440)

        2. 复位序列:主控设备通过发出一段时间的低电平信号来初始化总线,通知设备开始通信准备

        3. 写入时序:主控设备通过在数据线上发送特定的时序脉冲来传输命令或数据给从设备

        4. 读取时序:主控设备发送命令后,从设备会在数据线上以时序脉冲形式回复数据

        5. 响应确认:从设备在接收到命令或数据后,通常会通过一定的时序脉冲回复确认信号

<2> 2440与DS18B20通信的实质:通过2440的GPIO引脚,按照单总线协议(OneWire),遵照DS18B20使用手册中的时序,完成2440对DS18B20的读写数据访问,最后将DS18B20寄存器中存储的数据传回应用程序

<3>2440与DS18B20的通信时序流程

        1. 使用杜邦线将传感器与2440连接起来,DS18B20的数位线接到2440上CON4寄存器的EINT3(12)GPIO引脚上

        2. 主控设备产生复位脉冲(通知从设备准备通信)

        数据线上空闲状态时,为高电平,要产生复位脉冲时,主控设备2440将数据线电平拉低至少480us,之后主控设备释放总线,总线变为高电平

        3. 从设备产生存在脉冲(确认从设备存在,准备通信)

        从设备(DS18B20)侦测到复位脉冲并等待15~60微妙之后,从设备发送存在脉冲,将总线电平拉低60~240us,之后释放总线,若之后总线变为高电平的空闲状态,则2440与DS18B20间已经准备好开始通信

        4. 之后主设备发送温度采集命令,从设备开始进行温度采集,随后从设备开始采集并发送数据,主设备接收到十六位的数据,采集结束。

1.2 MQ-7——(CO浓度采集)

        一氧化碳的浓度达到100 ppm(百万分之一)时就可能存在火灾爆炸的危险                

(1)传感器参数

        VCC:电源输入(2.5V - 5.0V)

        GND:地

        AOUT:模拟量输出(单总线(OneWire协议))

        DOUT:数字量输出

(2)选型原因

        1. 输入电压范围(2.5~5.0V),能够适配开发板

        2. 小巧轻便、‌易于集成到各种设备中的特点,‌使其成为多种应用场合下的理想选择。‌无论是家庭、‌工业还是便携式设备,‌MQ-7传感器都能提供快速、‌准确的一氧化碳浓度监测,‌保障人员安全和环境安全

(3)涉及知识点与传感器工作原理

<1>工作原理

        MQ-7一氧化碳传感器内部气敏电阻的电导率随着CO浓度的增加而增大,使得AOUT输出的电压值增大,将MQ-7输出的模拟电压值通过杜邦线接入S3c2440的芯片内部的片内TS-ADC上的其中一个通道,通过TS-ADC,通过ADC内部的比较器,将模拟电压采样量化为数字信号,通过配置ADC中断的方式,使用等待队列实现进程间的同步,即进程等待ADC转化时,使用等待队列,将进程挂起并放入等待队列(程序被阻塞),当ADC将模拟信号转化好之后,触发中断,之后在中断服务函数中将等待队列中的进程唤醒,程序继续向下运行,将ADC寄存器中保存的值从内核驱动程序中传到应用程序中。

<2> S3C2440处理器上搭载的ADC(模数转换器)叫做TS-ADC(Touch Screen Analog to Digital Converter)。这个ADC不仅用于一般的模拟信号采集,还特别设计用于触摸屏的输入检测。TS-ADC支持12位分辨率,并且可以处理多个模拟输入通道,非常适合嵌入式系统的应用

<3> 等待队列(同步和阻塞进程)

        (1)定义

        等待队列(Wait Queue)是操作系统内核中的一种同步机制,用于管理在等待特定事件发生时被阻塞的进程或线程。

        (2)作用

        等待队列在内核中非常常见,特别是在Linux内核中,常用于处理各种同步和阻塞操作。进程被放入等待队列后,CPU会被分配给其他可以运行的进程。进程被阻塞时,它不会继续执行任何指令,直到等待的事件发生并且进程从等待队列中被唤醒,。一旦等待的事件发生,进程会被移出等待队列,重新加入就绪队列,等待系统调度器再次将其调度到CPU上运行

        (3)如何使用

                1. 定义一个全局等待队列头变量和一个等待条件变量

                2. 初始化等待队列头变量

                3. 需要程序同步阻塞时调用等待函数,以中断的方式

                4. 在需要结束等待时,改变等待条件变量的值,唤醒程序

1.3 ACR220E三相电表——(测量充电桩电压、电流)

(1)电表参数信息

        1. 电压测量范围

        相电压:0~400V

        线电压:0~690V

        2. 电流测量范围

        额定电流范围:0~5A

        实际测量必须配置不同倍率的电流互感器(CT)来扩展测量范围

        3. ACR220E电表默认通过RS-485接口与上位机进行通信

(2)选型原因

        1. ACR220E电表通过RS-485接口进行通信,RS-485具有能够长距离传输和抗干扰能力强的优点,是工业中的首选

        2. 电表能够通过485转TTL与开发板通信,适配开发板

        3. ACR220E三相电表是一种高精度电力计量设备,在工业生产、商业建筑、公共设施中都有使用

(3)电表工作原理和涉及的知识点

<1> ACR220E三相电表

        对于本项目来说,电表带有RS-485通讯接口,A、B线,采用MODBUS-RTU协议,能够实现与2440通信。

<2> 三相电和单相电

        1. 单相电

        单相电是由两根导线(相线和零线)构成的电力供应方式,常用于住宅、办公室、小型商铺

   220V/120V
     (相线)
       |
    [负载]
       |
     (零线)

        2. 三相电

        三相电是由三根相线(通常标记为A、B、C)和一根零线构成的电力供应方式,常用于工业和大型商业场所,适用于高功率设备

    380V
    (A相) (B相) (C相)
     |      |      |
  [负载] [负载] [负载]
     |      |      |
   (零线)

<3> MODBUS协议

        1. 定义

        MODBUS协议是一种应用广泛的工业通信协议,用于可编程逻辑控制器(PLC)之间的通信。MODBUS协议通过各种类型的物理网络(如串行线路、以太网等)进行通信,支持主从模式,允许一个设备(主机)向多个设备(从机)发送指令,并接收它们的响应。MODBUS协议有几种常见的变体,主要包括MODBUS RTU、MODBUS ASCII和MODBUS TCP/IP

        2. 分类

        (1)MODBUS RTU(Remote Terminal Unit)

  • 使用二进制编码的数据。
  • 每个数据帧由起始标志、从地址、功能代码、数据、错误检查码(CRC)和结束标志组成。
  • 高效但不易调试。

        数据帧格式

| 起始标志 | 从地址 | 功能代码 | 数据 | CRC校验码 | 结束标志 |

        (2)MODBUS ASCII

  • 使用ASCII字符编码的数据。
  • 每个数据帧由冒号(:)作为起始标志,数据域,LRC(纵向冗余校验)和回车/换行作为结束标志组成。
  • 便于调试但效率较低。

        数据帧格式

: 起始标志
| 起始标志(:) | 从地址 | 功能代码 | 数据 | LRC校验码 | 结束标志(\r\n) |

        (3)MODBUS TCP/IP

  • 使用TCP/IP协议在以太网上传输MODBUS消息。
  • 每个数据帧包含一个固定的MODBUS应用协议头(MBAP),包括事务处理标识符、协议标识符、长度字段和单元标识符

        数据帧格式

| 事务处理标识符 | 协议标识符 | 长度 | 单元标识符 | 功能代码 | 数据 |

        3. 功能代码

        MODBUS协议支持多种功能代码,用于不同的操作,如读取、写入、诊断等。常用的功能代码包括

  • 读取线圈状态(0x01):读取从设备的离散输出(线圈)。
  • 读取离散输入(0x02):读取从设备的离散输入状态。
  • 读取保持寄存器(0x03):读取从设备的模拟输出(保持寄存器)。
  • 读取输入寄存器(0x04):读取从设备的模拟输入。
  • 写单个线圈(0x05):写入从设备的单个线圈。
  • 写单个保持寄存器(0x06):写入从设备的单个保持寄存器。
  • 写多个线圈(0x0F):写入从设备的多个线圈。
  • 写多个保持寄存器(0x10):写入从设备的多个保持寄存器

        4. 通信过程

        (1)请求/响应模型

        主机发送一个请求帧,从设备收到后进行处理,并返回一个响应帧,每个请求/响应都是独立的事物

        (2)广播模型

        主机发送广播消息(地址为0),所有从设备都接收并处理,但不返回响应

        5. 错误检测

        MODBUS协议提供了两种错误检测机制

        (1)RTU模式的CRC校验:使用循环冗余校验码(CRC)来检测传输中的数据错误

        (2)ASCII模式的LRC校验:使用纵向冗余校验码(LRC)来检测错误

        6. MODBUS中CRC校验原理

        (1)数据帧结构:MODBUS数据帧包含设备地址、功能码、数据和CRC校验码。CRC校验码用于检测数据在传输过程中是否发生错误

        (2)CRC计算

                MODBUS协议使用16位的CRC校验码,生成多项式为 ‘0xA001’

                数据帧中的每个字节与当前的CRC值进行异或运算,然后进行逐位处理,按照生成的多项式更新crc值

        (3)错误检测

                发送端根据要发送的数据帧(地址、功能、数据)计算CRC校验码,并将其附加到数据帧的末尾

                接收端接收到数据帧后,对接收到的数据帧中除开CRC校验码的部分,使用同样的方法计算CRC校验码        

                如果接收端计算的CRC校验码与接收到的CRC校验码值一致,说明数据未发生错误,否则,数据存在错误

        在本次项目中,当2440发送携带CRC校验码的数据帧给电表后,若发送数据帧有效,电表会将存储在电表寄存器中的电压值通过包含CRC校验码的数据帧格式发回2440,2440接收到数据帧后,除开末尾的两个字节CRC校验码,对数据帧进行CRC运算,即根据多项式,对数据帧的每一位进行异或处理,若最后计算出的CRC校验与电表发送数据帧中的CRC校验码相同,则数据传输正确无误

unsigned short calculate_crc(unsigned char *buf, int len)
{
    unsigned short crc = 0xFFFF;//初始值
	int pos = 0;
	int i = 0;

    for (pos = 0; pos < len; pos++) 
	{
        crc ^= (unsigned short)buf[pos];//与当前的字节进行异或

        for (i = 8; i != 0; i--) 
		{
            if ((crc & 0x0001) != 0) //如果最低位是1
			{
                crc >>= 1;    //右移一位
                crc ^= 0xA001;//异或生成多项式
            } 
			else 
			{
                crc >>= 1;    //最低位是0,仅右移一位
            }
        }
    }

    return crc;
}

        7. MODBUS应用场景

        (1)工业自动化

                用于连接和控制工业自动化设备,如PLC、传感器、等,确保设备的协同工作和数据传输

        (2)能源管理

                用于监测和控制电力设备,如变压器、电表等

        (3)交通控制系统

                用于监控和控制交通信号灯等设备,确保交通系统的顺畅运行和有效管理

<4> RS-485

        (1)定义

              RS-485是一种广泛应用于工业和通信系统中的串行通信标准,特点是传输的抗干扰能力强以及长距离传输。是串口通信和RS-232的改良版

        (2)特点

                1. 差分信号传输

                        从设备中接出A、B双绞线,传输的0或1根据两根导线的电压差来传输,RS-485信号的电压范围是 -7V 到 +12V之间

                        若A线电压减去B线电压范围在+(2 ~ 6)V,则传0(A>B)

                        若A线电压减去B线电压范围在-(2 ~ 6)V,则传1   (A<B)

                2. 多点通信

                        遵循主从应答模式,可以一对多进行通信,最多可同时通信32太设备

                3. 半双工通信

                        同一时间只能进行单向通信,要么发送,要么接收

                4. 抗电磁干扰能力强

                5. RS-485定义了物理层,在数据链路层及以上使用MODBUS协议

                6. 传输距离远

                        RS-485理论传输距离为1200m,但实际并不能达到1200m,会有信号衰减,产生误差

        波特率:一秒内能传输的高低电平个数,即一秒传输的数据位数

<5> RS485、RS232、串口通信之间的区别与联系

        (1)串口通信

        (2)RS-232

        (3)RS-485

1.4 IIC

1.5 单总线、IIC、SPI、UART、CAN、485、232、TTL区别和联系

1.6 framebuffer

(1)定义

        framebuffer(帧缓冲)是计算机显示系统中的一块内存区域,用于存储当前显示的图像数RGB颜色值数据。它是一个数据结构,通常由硬件显卡驱动程序管理,直接映射到屏幕上的每个像素。framebuffer中的数据决定了显示器上每个像素的颜色和亮度。同时,也是linux内核专门为显示提供的应用程序接口

(2)组成

        1. 内存区域:用于存储像素数据,每个像素数据包括颜色和透明度信息

        2. 图形驱动:管理和控制framebuffer的驱动程序,负责将framebuffer中的数据发送到显示设备

        3. 显示设备:如LCD显示屏等,用于显示framebuffer中的图像

(3)原理

        (1)帧缓冲区域

                内存中专门用于存储图像数据的区域

        (2)MMAP(建表)

                将帧缓冲区域映射到用户空间,用户在用户空间可通过内存访问的方式读写缓冲区,更新显示内容

        (3)MMU(查表)

                将内存的虚拟地址转化为物理地址

OCTL 是 "Input/Output Control" 的缩写,是操作系统中的一种机制,主要用于与设备驱动程序进行交互。在操作系统内核中,IOCTL 是一种系统调用,用于发送设备特定的命令或请求,以执行无法通过标准系统调用(如读写操作)完成的操作。

IOCTL 通常用于以下情况:

  1. 控制设备操作:可以通过 IOCTL 向设备发送命令,如启动、停止、重置设备等。
  2. 获取设备状态:可以通过 IOCTL 从设备获取特定信息,例如设备的状态、配置参数等。
  3. 设置设备参数:可以使用 IOCTL 设置设备的工作模式或参数,如更改波特率、设置通信模式等。

每个设备通常会定义一组专门的 IOCTL 命令,用户程序可以通过这些命令与设备进行细粒度的控制和通信。IOCTL 的具体使用方法通常依赖于设备驱动程序的实现和操作系统的规范。

在编程中,使用 IOCTL 需要通过系统调用 ioctl() 函数来实现,通常在 C 语言编写的程序中用得较多,尤其是在与硬件设备打交道时

        framebuffer控制LCD屏幕显示的原理

        字符设备驱动通过(kmalloc)开辟了显存,初始化了硬件(LCD控制器),MMAP建立虚拟地址到物理地址的映射页表,应用程序通过打开framebuffer字符设备驱动open(/dev/fb0),通过修改虚拟内存中的RGB数据颜色值,MMU来查表映射,能够修改硬件内存rom中的值,2440GPU使用LCD控制器和GPU,通过TTL、RGB排线完成对LCD屏幕像素点的绘制。完成了应用程序通过framebuffer技术操作LCD屏幕显示不同的图像功能

(4)特点

        1. 允许应用程序直接访问显示内存,提供对每个像素的精确控制

        2. 能够跨平台使用

(5)在linux操作系统的具体实现

        framebuffer通常由‘/dev/fb0’等设备文件表示,通过访问这些设备文件,可以进行直接的图像数据操作,例如使用mmap函数将framebuffer内存映射到用户空间,然后通过指针操作进行读写

(6)优点

        1. 高效:直接访问显存,避免了不必要的中间步骤

        2. 灵活:应用程序可以精确控制显示的每个像素

        3. 跨平台性强:几乎所有的操作系统和硬件平台都支持framebuffer

2. 软件部分

2.1 软件框架

        本项目软件框架基于线程邮箱结构,通过创建的一个链表和链表节点,将整个项目软件部分的各个功能模块连接起来,各个链表节点为一个功能模块,每个链表节点上又分别创建各自的队列来暂存其他模块传入的数据。整个软件部分的各个模块间通信通过线程邮箱来实现,实质是通过各线程被创建时产生的线程ID来寻找想要传输传输的数据的模块所在的线程。

2.2 软件工程管理工具Makefile

(1)在多文件操作中,在整个文件目录下,编写名为makefile(Makefile)的脚本文件

(2)语法规则

        要生成文件:所有依赖文件(中间以空格隔开)

                生成方式

(3)makefile中的变量

        $@  要生成的文件

        $^     所有依赖的文件

        $<     第一个依赖的文件

        

        =        直接赋值

        :=       覆盖赋值

        +=      在原来的基础上累加

        ?=        前面没有给变量赋值,则给变量赋等号后的值

                      变量已经有值,不会给变量赋新值

(4)执行步骤(编写好makefile之后)

        1.终端输入make
        2.make执行对应目录下的makefile文件
        3.生成makefile文件中第一个目标
        4.查找所有依赖的文件是否都存在
        5.如果存在则利用生成方式生成目标文件

(5)makefile代码

        <1>拆解版本

OBJ:=a.out          
//OBJ自定义变量    :=覆盖赋值   a.out要生成的文件

OBJS+=main.c mailbox.c mailbox_list.c mailbox_queue.c font_8x16.c framebuffer.c thread_fun.c tty.c  
//OBJS自定义变量  +=原来的基础上新+的文件,每个文件之间要用空格分隔开
 
CC=arm-linux-gcc
//直接给变量CC赋值

$(OBJ):$(OBJS)
//变量OBJ依赖于OBJS

	$(CC) $^ -o $@ -lm -lpthread
//$(arm-linux-gcc)生成方式  $^所有依赖的文件   $@要生成的文件

.PHONY:
//伪指令,区别默认生成的目标和下面要生成的目标

clean:
//不依赖任何文件

	rm $(OBJ) 

        <2> 完整版本   

OBJ:=a.out
OBJS+=main.c mailbox.c mailbox_list.c mailbox_queue.c font_8x16.c framebuffer.c thread_fun.c tty.c
CC=arm-linux-gcc

$(OBJ):$(OBJS)
    $(CC) $^ -o $@ -lm -lpthread
.PHONY:
clean:
    rm $(OBJ)

2.3 线程邮箱

(1)定义

        线程邮箱(Thread Mailbox)是一种用于线程间通信(Inter-Thread Communication, ITC)的机制,它提供了一个消息传递的结构,允许线程间通过发送和接收数据来进行同步和线程间的数据交换

(2)基本原理

        1. 消息队列

        线程邮箱通常依赖于消息队列,每个线程都有一个关联的邮箱,其他线程可以向这个邮箱发送消息。邮箱会将消息按照到达的顺序排队。

        2. 互斥保护

        为了防止并发操作导致多个线程同时操作同一数据,即资源竞争,线程邮箱实现通常会使用互斥锁(Mutex)或其他同步机制来保护对消息队列的访问

(3)应用场景

        1. 任务分配

        主线程将任务打包成消息发送到工作线程的邮箱中,工作线程从邮箱中读取任务并执行

        2. 事件通知

        线程邮箱可以用于事件驱动的编程模型中。某个线程监测到事件发生后,可以通过发送消息通知其他线程

        3. 数据传输

        在生产者-消费者模型中,生产者线程可以向消费者线程发送数据,生产者将数据作为消息发送到消费者邮箱,消费者读取并处理数据

        4. 同步协调

        一个线程完成某个步骤后,通过线程邮箱通知其他线程继续执行后续的步骤

(4)线程邮箱的优点

        对于多线程间的通信,线程邮箱通过多线程间遍历链表节点来传输数据,相对于创建多个全局变量来讲,线程邮箱中的互斥锁有效的避免了资源竞争以及创建多个全局变量的问题,将每个功能模块划分为不同链表节点中的线程,简化了代码结构,方便调用,使得函数变得模块化。

2.4 线程邮箱实现多线程通信和网络实现多进程通信的区别

        线程邮箱使用于需要高效、低延迟的线程间通信,但安全性低于多线程网络通信

        多进程网络通信适合需要强隔离性和跨机器通信的应用场景,安全性高于线程邮箱

2.5 单向无头链表——(一种线性存储结构)

(1)makefile

OBJ:=seqlist
OBJS+=SeqList.c
CC:=gcc 
 
$(OBJ):$(OBJS)
	$(CC) $^ -o $@
.PHONY:
clean:
	rm $(OBJ)
test:
	valgrind --tool=memcheck --leak-check=full ./$(OBJ)

(2)头文件

#ifndef _LINKLIST_H_
#define _LINKLIST_H_
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef int DataType;
 
typedef struct node
{
    DataType Data;
    struct node *pNext;
}LinkNode;
 
typedef struct list
{
    LinkNode *pHead;
    int cLen;
}LinkList;
 
extern LinkList *create_LinkList(void);
extern int is_empty_link(LinkList *pTmpList);
extern int push_head_link(LinkList *pTmpList, DataType Data);//头插、空链表也能使用
extern int push_tail_link(LinkList *pTmpList, DataType Data);//尾插、空链表也能使用
extern void link_for_each(LinkList *pTmpList);
extern int pop_head_link(LinkList *pTmpList);
extern int pop_tail_link(LinkList *pTmpList);
extern LinkNode *find_data(LinkList *pTmpList, DataType Data);
extern int modify_data(LinkList *pTmpList, DataType old_data, DataType new_data);
extern void destory_link(LinkList *pTmpList);
extern LinkNode *find_mid_node(LinkList *pTmpList);//找中间节点
extern LinkNode *find_last_k_node(LinkList *pList, int k);//寻找倒数第k个节点
extern int pop_data_link(LinkList *pTmpList, DataType Data);//删除数据为key的节点
extern void invert_link(LinkList *pTmpList);
extern void insert_sort_link(LinkList *pTmpList);
 
#endif

(3)主函数

#include "LinkList.h"
 
LinkList *create_LinkList(void)//创建空链表
{
    LinkList *pList = NULL;
 
    pList = malloc(sizeof(LinkList));
    if (NULL == pList)
    {
        perror("fail to malloc");
        return NULL;
    }
 
    pList->pHead = NULL;
    pList->cLen = 0;
 
    return pList;
}
 
int is_empty_link(LinkList *pTmpList)//判断链表是否为空链表
{
    if (NULL == pTmpList->pHead)
    {
        return 1;
    }
 
    return 0;
}
 
int push_head_link(LinkList *pTmpList, DataType Data)//头插、空链表也能使用
{
    LinkNode *pTmpNode = NULL;
 
    pTmpNode = malloc(sizeof(LinkNode));
    if (NULL == pTmpNode)
    {
        perror("fail to malloc");
        return -1;
    }
 
    pTmpNode->pNext = NULL;//插入节点初始化
    pTmpNode->Data = Data;
    
    pTmpNode->pNext = pTmpList->pHead;
    pTmpList->pHead = pTmpNode;
    pTmpList->cLen++;
 
    return 0;
}
 
int push_tail_link(LinkList *pTmpList, DataType Data)//尾插、空链表也能使用
{
    LinkNode *pTmpNode = NULL;
    LinkNode *pLastNode = NULL;
 
    pTmpNode = malloc(sizeof(LinkNode));
    if (NULL == pTmpNode)
    {
        perror("fail to malloc");
        return -1;
    }
 
    pTmpNode->pNext = NULL;
    pTmpNode->Data = Data;
    
    if (is_empty_link(pTmpList))
    {
        pTmpList->pHead = pTmpNode;
    }
    else 
    {
        pLastNode = pTmpList->pHead;
        while (pLastNode->pNext != NULL)
        {
            pLastNode = pLastNode->pNext;
        }
        pLastNode->pNext = pTmpNode;
    }
    pTmpList->cLen++;
 
    return 0;
}
 
void link_for_each(LinkList *pTmpList)//遍历输出链表
{
    LinkNode *pTmpNode = NULL;
 
    pTmpNode = pTmpList->pHead;
    while (pTmpNode != NULL)
    {
        printf("%d ", pTmpNode->Data);
        pTmpNode = pTmpNode->pNext;
    }
    putchar('\n');
}
 
int pop_head_link(LinkList *pTmpList)//头删
{
    if (is_empty_link(pTmpList))
    {
        return -1;
    }
 
    LinkNode *pTmpNode = NULL;
 
    pTmpNode = pTmpList->pHead;
    pTmpList->pHead = pTmpNode->pNext;
    free(pTmpNode);
    pTmpList->cLen--;
    
    return 0;
}
 
int pop_tail_link(LinkList *pTmpList)//尾删
{
    if (is_empty_link(pTmpList))
    {
        return -1;
    }
    if (1 == pTmpList->cLen)
    {
        pop_head_link(pTmpList);
    }
    else
    {
        LinkNode *pTmpNode = NULL;
 
        pTmpNode = pTmpList->pHead;
        while (pTmpNode->pNext->pNext != NULL)
        {
            pTmpNode = pTmpNode->pNext;
        }
        free(pTmpNode->pNext);
        pTmpNode->pNext = NULL;
        pTmpList->cLen--;
    }
    
    return 0;
}
 
LinkNode *find_data(LinkList *pTmpList, DataType Data)//在链表中寻找数据
{
    LinkNode *pTmpNode = NULL;
 
    pTmpNode = pTmpList->pHead;
 
    while (pTmpNode)
    {
        if (Data == pTmpNode->Data)
        {
            return pTmpNode;
        }
        pTmpNode = pTmpNode->pNext;
    }
 
    return NULL;
}
 
int modify_data(LinkList *pTmpList, DataType old_data, DataType new_data)//修改数据
{
    LinkNode *pTmpNode = NULL;
 
    pTmpNode = find_data(pTmpList, old_data);
    if (pTmpNode != NULL)
    {
        pTmpNode->Data = new_data;
        return 0;
    }
    else 
    {
        return -1;
    }
}
 
void destory_link(LinkList *pTmpList)//销毁链表
{
    while (!is_empty_link(pTmpList))
    {
        pop_head_link(pTmpList);
    }
    free(pTmpList);
}
 
LinkNode *find_mid_node(LinkList *pTmpList)//找中间节点
{
    LinkNode *pSlowNode = NULL;
    LinkNode *pFastNode = NULL;
 
    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;
 
    while (pFastNode != NULL)
    {
        pFastNode = pFastNode->pNext;
        if (NULL == pFastNode)
        {
            break;
        }
        pFastNode = pFastNode->pNext;//pFastNode 速度是 pSlowNode的两倍
        pSlowNode = pSlowNode->pNext;
    }
 
    return pSlowNode;
}
 
LinkNode *find_last_k_node(LinkList *pTmpList, int k)//寻找倒数第k个节点
{
    LinkNode *pFastNode = NULL;
    LinkNode *pSlowNode = NULL;
    int i = 0;
 
    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;
 
    for (i = 0; i < k; i++)//快慢指针相差k个位置
    {
        if (NULL == pFastNode)
        {
            return NULL;
        }
        pFastNode = pFastNode->pNext;
    }
 
    while (pFastNode != NULL)
    {
        pFastNode = pFastNode->pNext;
        pSlowNode = pSlowNode->pNext;
    }
 
    return pSlowNode;
}
 
int pop_data_link(LinkList *pTmpList, DataType Data)//删除数据为key的节点
{
    if (is_empty_link(pTmpList))
    {
        return 1;
    }
 
    LinkNode *pTmpNode = NULL;
    LinkNode *pPreNode = NULL;
 
    pTmpNode = pTmpList->pHead;
    while (pTmpNode != NULL)
    {
        if (Data == pTmpNode->Data)//找到数据
        {
            if (pTmpNode == pTmpList->pHead)//判断是否只有头结点
            {
                pTmpList->pHead = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pTmpList->pHead;
            }
            else
            {
                pPreNode->pNext = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pPreNode->pNext;
            }
            pTmpList->cLen--;
        }
        else
        {
            pPreNode = pTmpNode;
            pTmpNode = pTmpNode->pNext;
        }
    }
}
 
void invert_link(LinkList *pTmpList)//链表的倒置
{
    LinkNode *pTmpNode = NULL;
    LinkNode *pInsertNode = NULL;
 
    pTmpNode = pTmpList->pHead;
    pTmpList->pHead = NULL;
    
    while (pTmpNode != NULL)
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;
 
        pInsertNode->pNext = pTmpList->pHead;
        pTmpList->pHead = pInsertNode;
    }
}
 
void  insert_sort_link(LinkList *pTmpList)
{
    if (is_empty_link(pTmpList) || NULL == pTmpList->pHead->pNext)//空链表或者只有一个节点,不用排序
    {
        return ;
    }
 
    LinkNode *pTmpNode = NULL;//记录剩余节点的起始位置
    LinkNode *pInsertNode = NULL;//插入节点的位置
    LinkNode *p = NULL;//插入排序中遍历已经插入的节点的指针
 
    pTmpNode = pTmpList->pHead->pNext;//第一个节点不排序,从第二个节点开始排序
    pTmpList->pHead->pNext = NULL;//从原链表的第一个节点之后断开
 
    while (pTmpNode != NULL)//判断剩余未排序节点是否存在,最后一个节点参与操作
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;
 
        if (pInsertNode->Data <= pTmpList->pHead->Data)//判断要插入节点数据的大小是否小于第一个节点的数据
        {
            pInsertNode->pNext = pTmpList->pHead;//头插
            pTmpList->pHead = pInsertNode;
        }
        else //往后插
        {
            p = pTmpList->pHead;
            while (p->pNext != NULL && p->pNext->Data < pInsertNode->Data)
            {
                p = p->pNext;
            }
 
            pInsertNode->pNext = p->pNext;
            p->pNext = pInsertNode;
        }
    }
}
 
int main(void)
{
    LinkList *pList = NULL;
    LinkNode *pTmpNode = NULL;
    
    pList = create_LinkList();//创建
    if (NULL == pList)
    {
        return -1;
    }
#if 0
    push_head_link(pList, 3);//头插
    push_head_link(pList, 2);
    push_head_link(pList, 1);
    link_for_each(pList);
#endif
    push_tail_link(pList, 1);//尾插
    push_tail_link(pList, 2);
    push_tail_link(pList, 3);
    push_tail_link(pList, 4);
    push_tail_link(pList, 5);
    push_tail_link(pList, 6);
    push_tail_link(pList, 7);
    link_for_each(pList);
#if 0
    pop_head_link(pList);//头删
    link_for_each(pList);
 
    pop_tail_link(pList);//尾删
    link_for_each(pList);
#endif
    pTmpNode = find_data(pList, 4);//找数据
    if (NULL != pTmpNode)
    {
        printf("find node data = %d\n", pTmpNode->Data);
    }
    else
    {
        printf("not find this node\n");
    }
 
    modify_data(pList, 2, 20);//修改数据
    modify_data(pList, 3, 30);
    modify_data(pList, 4, 40);
    modify_data(pList, 5, 50);
    link_for_each(pList);
 
    pTmpNode = find_mid_node(pList);//找链表中间节点
    if (pTmpNode != NULL)
    {
        printf("Mid node data = %d\n", pTmpNode->Data);
    }
    link_for_each(pList);
 
    pTmpNode = find_last_k_node(pList, 5);//寻找链表倒数第k个节点
    if (pTmpNode != NULL)
    {
        printf("last node data = %d\n", pTmpNode->Data);
    }
 
    link_for_each(pList);
    pop_data_link(pList, 30);//删除链表数据
    link_for_each(pList);
 
    invert_link(pList);//链表倒置
    link_for_each(pList);
 
    insert_sort_link(pList);
    link_for_each(pList);
    
 
    destory_link(pList);//销毁链表
    
    return 0;
}

2.6 队列

(1)定义

        队列是一种线性数据结构的类型,可以用数组或链表等基础数据结构来实现,遵循先进先出FIFO(first in first out)的原则。最先入队的数据会最先被移出。

(2)结构(入队尾插、出队头删)

2.6 线程邮箱代码

(1)mailbox.h(线程邮箱结构框架)

#ifndef _MAILBOX_H_
#define _MAILBOX_H_

#include <pthread.h>

//上锁
#define ENTER_CRITICAL_AREA(mutex) do{pthread_mutex_lock(mutex);}while(0) 

//解锁
#define QUIT_CRITICAL_AREA(mutex) do{pthread_mutex_unlock(mutex);}while(0) 

typedef char DATATYPE[256];//定义了一种新的类型 DATATYPE,该类型表示一个大小为 256 的字符数组

/*void *(*th_fun)(void *)的含义是定义了一个名为th_fun
函数返回值为void *,函数形参为void *类型的函数指针
typedef为数据类型为void *()(void *)的函数指针起了一个类型别名th_fun*/
typedef void *(*th_fun)(void *);

//每个链表节点中单个队列节点所要存储的数据信息
typedef struct mail_information
{
    pthread_t id_of_sender;//发送线程的ID
    char name_of_sender[256];//发送线程的线程名
    pthread_t id_of_recver;//接收线程的ID
    char name_of_recver[256];//接收线程的线程名
    DATATYPE data;//本队列节点的数据
}MAIL_DATA;

//每个链表节点中的队列结构(单个节点)
typedef struct queue
{
    MAIL_DATA data;//队列中单个节点存储的数据
    struct queue *pnext;//指向下一个队列节点的指针
}queue_t;

//线程邮箱链表节点中的数据元素部分
typedef struct thread_node
{
    pthread_t tid;//线程ID
    char name[256];//线程名
    queue_t *mail_head;//队列头指针
    queue_t *mail_tail;//队列尾指针
    th_fun th;//线程函数
}LIST_DATA;

//线程邮箱链表节点
typedef struct LinkNode
{
    LIST_DATA elem;//节点元素结构体变量
    struct LinkNode *pnext;//指向下一个节点的地址
}MAILBOX;

//线程邮箱链表标签
typedef struct mail_box_system
{
    pthread_mutex_t mutex;//互斥锁
    MAILBOX *thread_list;//邮箱链表首节点地址
}MBS;

extern MAILBOX *end_list;
extern MBS *create_mail_box_system(void);
extern char *get_th_name(MBS *mbs);
extern int register_to_mail_system(MBS *mbs, char *name, th_fun th);
extern int send_msg(MBS *mbs, char *recvname, DATATYPE data);//入队数据
extern int recv_msg(MBS *mbs, char *sendname, DATATYPE data);//出队数据加入到当前线程的存储单元中
extern int wait_all_end(MBS *mbs);
extern int destory_mail_box_system(MBS *mbs);

#endif

(2)mailbox.c(线程邮箱节点的所有操作)

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "mailbox.h"
#include "mailbox_queue.h"
#include "mailbox_list.h"

MBS *create_mail_box_system(void)//创建邮箱链表标签头
{
    int ret = 0;

    //创建链表标签
    MBS *ptmp = malloc(sizeof(MBS));
    if (NULL == ptmp)
    {
        perror("fail to create_mail_box_system");
        return NULL;
    }
    //初始化互斥锁
    ret = pthread_mutex_init(&ptmp->mutex, NULL);
    if (0 != ret)
    {
        perror("fail to pthread_mutex_init");
        return NULL;
    }

    //创建链表的第一个节点
    ptmp->thread_list = malloc(sizeof(MAILBOX));
    ptmp->thread_list->pnext = NULL;
 
    printf("mail box create successfully!\n");

    return ptmp;//返回链表标签结构体地址
}

//注册新的邮箱(链表节点),向节点中加入相关信息
                          //邮箱标签   邮箱节点名  该节点线程函数的名
int register_to_mail_system(MBS *mbs, char *name, th_fun th)
{
    MAILBOX *ptmp = malloc(sizeof(MAILBOX));//创建新的邮箱链表节点
    if (NULL == ptmp)
    {
        perror("fail to register!\n");
        return -1;
    }
    strcpy(ptmp->elem.name, name);//邮箱节点名
    ptmp->elem.th = th;//节点中线程函数的名称

    init_que(ptmp);//初始化邮箱(链表节点)的队列头
    list_add(mbs->thread_list, ptmp);//将注册的邮箱节点加入链表(头插)

    //创建邮箱节点的线程函数,实现各个节点模块的功能
    int ret = pthread_create(&ptmp->elem.tid, NULL, th, mbs);
    if (0 != ret)
    {
        perror("fail to pthread_create!\n");
        return -1;
    }

    printf("register mail system |%s| ok !\n", ptmp->elem.name);

    return 0;
}


//将本线程中数据发送给名为recvname的线程,将数据入队到对应线程的队列中
int send_msg(MBS *mbs, char *recvname, DATATYPE data)
{
    MAIL_DATA *ptmp = malloc(sizeof(MAIL_DATA));
    strcpy(ptmp->data, data);
    ptmp->id_of_sender = pthread_self();//获取发送端线程ID

    //遍历链表寻找指定节点名所在链表节点的首地址
    MAILBOX *find = list_for_each(mbs->thread_list, recvname);
    if (NULL == find)
    {
        printf("can't find recv mailbox\n");
    }

    char *name = get_th_name(mbs);//查找发送方的链表节点的名字
    strcpy(ptmp->name_of_sender, name);
    strcpy(ptmp->name_of_recver, recvname);

    //入队上锁防止此时被资源竞争
    ENTER_CRITICAL_AREA(&mbs->mutex);
    in_queue(find, ptmp);//入队尾插
    QUIT_CRITICAL_AREA(&mbs->mutex);
    printf("send_msg success!\n");

    return 0;
}

//出队数列头部的数据(头删)
int recv_msg(MBS *mbs, char *sendname, DATATYPE data)
{
    MAIL_DATA *ptmp = malloc(sizeof(MAIL_DATA));
    pthread_t tid = pthread_self();
    MAILBOX *find = mbs->thread_list->pnext;

    while (find != NULL)//根据本线程函数的线程ID遍历邮箱链表
    {
        if (tid == find->elem.tid)
        {
            break;
        }
        find = find->pnext;
    }
    
    //在邮箱节点寻找线程函数对应的ID
    if (find != NULL && tid == find->elem.tid)
    {

        if (find->elem.mail_head != find->elem.mail_tail)//如果队列非空
        {
            ENTER_CRITICAL_AREA(&mbs->mutex);
            out_queue(find, ptmp);//出队头删
            QUIT_CRITICAL_AREA(&mbs->mutex);
        }
        else
        {
            printf("%s queue is empty!\n", find->elem.name)//队列为空,打印空的结果,结束程序
            return -1;
        }
    }

    strcpy(sendname, ptmp->name_of_sender);//将队列中的发送节点名拷贝出来
    strcpy(data, ptmp->data);//将队列中即将出队的数据拷贝出来

    free(ptmp);
    
    return 0;
}

MAILBOX *end_list = NULL;//链表的尾节点

char *get_th_name(MBS *mbs)
{
    pthread_t tid = pthread_self();
    MAILBOX *find = mbs->thread_list;
    MAILBOX *end = end_list;

    while (find != end)
    {
        if (tid == find->elem.tid)
        {
            break;
        }
        find = find->pnext;
    }

    if (tid == find->elem.tid)
    {
        return find->elem.name;
    }
    else
    {
        return NULL;
    }
}

//销毁线程
int wait_all_end(MBS *mbs)
{
    MAILBOX *find = mbs->thread_list->pnext;

    while (find->pnext != NULL)
    {
        pthread_join(find->elem.tid, NULL);
        find = find->pnext;
    }
    pthread_join(find->elem.tid, NULL);

    return 0;
}

//销毁邮箱链表
int destory_mail_box_system(MBS *mbs)
{
    pthread_mutex_destroy(&mbs->mutex);
    
    MAILBOX *ptmp = NULL;
    MAILBOX *find = mbs->thread_list;
    while (find != NULL)
    {
        ptmp = find;
        find = find->pnext;
        free(ptmp);
    }
    free(find);

    return 0;
}

(3)mailbox_list.c(对线程邮箱节点的操作)

#include <stdio.h>
#include <string.h>
#include "mailbox.h"

//邮箱节点插入邮箱链表中(头插)
void list_add(MAILBOX *head, MAILBOX *info)
{
    info->pnext = head->pnext;
    head->pnext = info;
}

//遍历邮箱链表,寻找指定节点名的节点地址
MAILBOX *list_for_each(MAILBOX *head, char *name)
{
    MAILBOX *ptmp = NULL;
    ptmp = head;

    while (ptmp != NULL)
    {
        if (0 == strncmp(ptmp->elem.name, name, strlen(name)))
        {
            return ptmp;
        }
        ptmp = ptmp->pnext;
    }

    return NULL;
}

(4)mailbox_list.h

#ifndef _MAILBOX_LIST_H_
#define _MAILBOX_LIST_H_

extern void list_add(MAILBOX *head, MAILBOX *info);
extern MAILBOX *list_for_each(MAILBOX *head, char *name);

#endif

(5)mailbox_queue.c(对邮箱节点中队列的操作)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mailbox.h"

//初始化队列         对应邮箱节点的地址
int init_que(MAILBOX *list_head)
{
    queue_t *ptmp = malloc(sizeof(queue_t));
    if (NULL == ptmp)
    {
        perror("fail to init_que malloc");
    }
    ptmp->pnext = NULL;

    list_head->elem.mail_head = ptmp;
    list_head->elem.mail_tail = ptmp;

    return 0;
}

//入队数据(向要发送数据的邮箱节点的队列中尾插数据)
int in_queue(MAILBOX *list_head, MAIL_DATA *data)
{
    queue_t *ptmp = malloc(sizeof(queue_t));
    memcpy(&ptmp->data, data, sizeof(MAIL_DATA));
    ptmp->pnext = NULL;
    
    list_head->elem.mail_tail->pnext = ptmp;
    list_head->elem.mail_tail = list_head->elem.mail_tail->pnext;

    return 0;
}

//出队数据加入到当前线程的存储单元中
int out_queue(MAILBOX *list_head, MAIL_DATA *data)
{
    if (list_head->elem.mail_head == list_head->elem.mail_tail)//空队列
    {
        printf("queue is empty.\n");
        return -1;
    }

    if (list_head->elem.mail_head->pnext == list_head->elem.mail_tail)//队列中只有一个节点
    {
        list_head->elem.mail_tail = list_head->elem.mail_head;
    }

    queue_t *del = list_head->elem.mail_head->pnext;
    list_head->elem.mail_head->pnext = del->pnext;

    *data = del->data;
    
    free(del);

    return 0;
}

(6)mailbox_queue.h

#ifndef _MAILBOX_QUEUE_H_
#define _MAILBOX_QUEUE_H_

extern int init_que(MAILBOX *list_head);
extern int in_queue(MAILBOX *list_head, MAIL_DATA *data);
extern int out_queue(MAILBOX *list_head, MAIL_DATA *data);

#endif

(7)thread_fun.c(包含所有线程函数的处理,即各个邮箱节点模块功能的实现)

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "thread_fun.h"
#include "mailbox.h"
#include "mailbox_queue.h"
#include "mailbox_list.h"
#include "framebuffer.h"
#include "font.h"
#include "tty.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <time.h>

#define vol_warn_num 700

//CRC处理函数
unsigned short calculate_crc(unsigned char *buf, int len)
{
    unsigned short crc = 0xFFFF;
	int pos = 0;
	int i = 0;

    for (pos = 0; pos < len; pos++) 
	{
        crc ^= (unsigned short)buf[pos];
        for (i = 8; i != 0; i--) 
		{
            if ((crc & 0x0001) != 0) 
			{
                crc >>= 1;
                crc ^= 0xA001;
            } 
			else 
			{
                crc >>= 1;
            }
        }
    }

    return crc;
}

#if 1
//数据采集
void *data_collect_th(void *arg)
{
    printf("this is the data_collect_th\n");
#if 1
	MBS *mbs = arg;
	unsigned char voltage_cmd[8] = {0x01, 0x03, 0x00, 0x2B, 0x00, 0x03};
	ssize_t ret = 0;
	unsigned short crc = 0;
	unsigned char recv_buf[11] = {0};
	int i = 0;

	//串行通信中,非阻塞可以在发送指令后执行其他命令,不必阻塞等待设备的到来
	int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NDELAY);//以读写、非阻塞且不作为控制终端的方式打开了UART设备
	if (fd < 0)
	{
		puts("fail to open!\n");
	}
	set_port(fd, 9600, 8, 1, 'N');//波特率9600、八个数据位、一位停止位、无奇偶校验

	while (1)
	{
		crc = calculate_crc(voltage_cmd, 6);//计算crc校验
		voltage_cmd[6] = crc & 0xff;//获取 crc 值的低8位_前八位为0,后八位为1,与0得0,与1不变
		voltage_cmd[7] = (crc >> 8) & 0xff;//获取crc高八位

		ret = write(fd, voltage_cmd, 8);
		if (-1 == ret)
		{
			perror("fail to write!\n");
		}
		else
		{
			printf("write success! ret = %d\n", ret);
		}

		sleep(1);

		ret = read(fd, recv_buf, sizeof(recv_buf));
		if (-1 == ret)
		{
			perror("fail to read!\n");
		}
		else
		{
			printf("read success! ret = %d\n", ret);
		}

		crc = calculate_crc(recv_buf, 9);

		//将第九个字节变为低八位,讲第十个字节变为高八位
		if (crc == (recv_buf[9] | (recv_buf[10] << 8)))
		{
			printf("recv success!");

			for (i = 0; i < 11; ++i)
			{
				printf("%x ", recv_buf[i]);
			}
			putchar('\n');
		}
		else
		{
			perror("fail to recv\n");
		}
	}

#endif
#if 0
	MBS *mbs = arg;
	unsigned char voltage_cmd[8] = {0x01, 0x03, 0x00, 0x25, 0x00, 0x09};
	ssize_t ret = 0;
	unsigned short crc = 0;
	unsigned char recv_buf[23] = {0};
	int i = 0;

	//串行通信中,非阻塞可以在发送指令后执行其他命令,不必阻塞等待设备的到来
	int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NDELAY);//以读写、非阻塞且不作为控制终端的方式打开了UART设备
	if (fd < 0)
	{
		puts("fail to open!\n");
	}
	set_port(fd, 9600, 8, 1, 'N');//波特率9600、八个数据位、一位停止位、无奇偶校验

	while (1)
	{
		crc = calculate_crc(voltage_cmd, 6);//计算crc校验
		voltage_cmd[6] = crc & 0xff;//获取 crc 值的低8位_前八位为0,后八位为1,与0得0,与1不变
		voltage_cmd[7] = (crc >> 8) & 0xff;//获取crc高八位

		ret = write(fd, voltage_cmd, 8);
		if (-1 == ret)
		{
			perror("fail to write!\n");
		}
		else
		{
			printf("write success! ret = %d\n", ret);
		}

		sleep(1);

		ret = read(fd, recv_buf, sizeof(recv_buf));
		if (-1 == ret)
		{
			perror("fail to read!\n");
		}
		else
		{
			printf("read success! ret = %d\n", ret);
		}

		crc = calculate_crc(recv_buf, 21);

		//将第九个字节变为低八位,讲第十个字节变为高八位
		if (crc == (recv_buf[21] | (recv_buf[22] << 8)))
		{
			printf("recv success!");

			for (i = 0; i < 23; ++i)
			{
				printf("%x ", recv_buf[i]);
			}
			putchar('\n');
		}
		else
		{
			perror("fail to recv\n");
		}
	}

#endif
#if 0
	MBS *mbs = arg;

    while (1)
    {
        printf("this is the data_collect_th\n");

        send_msg(mbs, "warn", "800");
        send_msg(mbs, "show", "800");
        send_msg(mbs, "show", "66");
        send_msg(mbs, "show", "25");
		sleep(3);
        send_msg(mbs, "warn", "600");
        send_msg(mbs, "show", "600");
        send_msg(mbs, "show", "55");
        send_msg(mbs, "show", "29");
		sleep(3);
        send_msg(mbs, "warn", "677");
        send_msg(mbs, "show", "677");
        send_msg(mbs, "show", "44");
        send_msg(mbs, "show", "27");
        sleep(6);
    }
#endif
}
#endif

#if 0
//数据采集ds18b20
void *data_collect_th(void *arg)
{
    printf("this is the data_collect_th\n");

	MBS *mbs = arg;
	unsigned short temp = 0;
	char temp_cc[256] = {0};
	float temp_c = 0;

	int fd = open("/dev/ds18b20", O_RDWR);
	if (fd < 0)
	{
		puts("fail to open!\n");
	}

	while (1)
	{
		read(fd, &temp, sizeof(temp));
		temp_c = temp * 0.0625;
		sprintf(temp_cc, "%.2f", temp_c);
		printf("temp %f\n", temp_c);
		printf("temp %x\n", temp);
        send_msg(mbs, "show", temp_cc);
		sleep(2);
	}
}
#endif

#if 0

//数据采集MQ-7(CO)
void *data_collect_th(void *arg)
{
    printf("this is the data_collect_th\n");

	MBS *mbs = arg;
	unsigned int co = 1;
	unsigned int coo = 0;
	char co_show[256] = {0};
	int i = 0;

	int fd = open("/dev/mq7_adc", O_RDWR);
	if (fd < 0)
	{
		puts("fail to open!\n");
	}

	while (1)
	{
		read(fd, &co, sizeof(co));
		printf("co %d\n", co);
		sleep(1);
	}
}
#endif

#if 0

//数据采集电压、电流
void *data_collect_th(void *arg)
{
    printf("this is the data_collect_th\n");

	MBS *mbs = arg;
	unsigned char voltage_cmd[] = {0x01, 0x03, 0x00, 0x28, 0x00, 0x03};
	unsigned char current_cmd[] = {0x01, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	ssize_t ret = 0;
	unsigned short crc = 0;
	unsigned char recv_buf[11];
	
	int fd = open("/dev/ttySAC0", O_RDWR | O_NOCTTY | O_NDELAY);//以读写、非阻塞且不作为控制终端的方式打开了UART设备
	if (fd < 0)
	{
		puts("fail to open!\n");
	}
	set_port(fd, 9600, 8, 1, 'N');//波特率9600、八个数据位、一位停止位、无奇偶校验

	while (1)
	{
		crc = calculate_crc(voltage_cmd, 6);
		
		voltage_cmd[6] = crc & 0xff;
		voltage_cmd[7] = (crc >> 8) & 0xff;//交换CRC
		ret = write(fd, voltage_cmd, strlen(voltage_cmd));
		sleep(1);
		ret = read(fd, recv_buf, sizeof(recv_buf));
		crc = calculate_crc(recv_buf, 9);

		if (crc == (recv_buf[9] | (recv_buf[10] << 8)))
		{
			printf("recv success!");

			int i = 0;

			for (i = 0; i < 11; ++i)
			{
				printf("%d ", recv_buf[i]);
			}
			putchar('\n');
		}
		else
		{
			perror("fail to recv\n");
		}
	}
}
#endif

//数据显示
void *show_th(void *arg)
{
	printf("this is the show th\n");

	MBS *mbs = arg;
	char sendname[256];
	DATATYPE data;

	init_fb("/dev/fb0");
	draw_bmp(0, 0, "./res/1.bmp", 240, 320);

    while (1)
    {
#if 0
		recv_msg(mbs, sendname, data);
		draw_string(115, 155, data, 0x0, 0x00ffffff);
		bzero(sendname, sizeof(sendname));
		bzero(data, sizeof(data));
		sleep(1);

		recv_msg(mbs, sendname, data);
		draw_string(115, 195, data, 0x0, 0x00ffffff);
		bzero(sendname, sizeof(sendname));
		bzero(data, sizeof(data));
		sleep(1);
#endif
		recv_msg(mbs, sendname, data);
		draw_string(115, 235, data, 0x0, 0x00ffffff);
		bzero(sendname, sizeof(sendname));
		bzero(data, sizeof(data));

        sleep(1);
    }
}

//接收来自服务端的信息
void *th1(void *arg)
{
	int confd = *(int *)arg;
	ssize_t ret_recv = 0;

	while (1)
	{
		char buf[256] = {0};

		ret_recv = recv(confd, buf, sizeof(buf), 0);
		if (-1 == ret_recv)
        {
            perror("recv error!\n");
        }

        printf("from recv: %s\n", buf);
	}
}

//向服务端发送信息
void *th2(void *arg)
{
	int confd = *(int *)arg;
	ssize_t ret_send = 0;

	while (1)
	{
		printf("to recv:");
		char buf[256] = {0};

		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = '\0';

		ret_send = send(confd, buf, strlen(buf), 0);		
        if (-1 == ret_send)
        {
            perror("send error!\n");
        }
	}
}

//网络模块
void *sock_th(void *arg)
{
    printf("this is the sock th\n");

	MBS *mbs = arg;

	int confd = 0;
	int ret_connect = 0;
	struct sockaddr_in recvaddr;
	struct sockaddr_in sendaddr;
	socklen_t addrlen = 0;
	ssize_t ret_send = 0;
	ssize_t ret_recv = 0;
	pthread_t tid1;
	pthread_t tid2;
	char buf[256] = {0};

	confd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == confd)
	{
		perror("socket error!\n");
	}

	bzero(&recvaddr, sizeof(recvaddr));
	bzero(&sendaddr, sizeof(sendaddr));

	recvaddr.sin_family = AF_INET;
	recvaddr.sin_port = htons(50000);
	recvaddr.sin_addr.s_addr = inet_addr("192.168.1.100");

	ret_connect = connect(confd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
	if (-1 == ret_connect)
    {
        perror("connect error!\n");
    }

#if 0
    while(1)
    {
		char buf[256] = {"supercarrydoinb!"};

		ret_send = send(confd, buf, strlen(buf), 0);
		if (-1 == ret_connect)
        {
            perror("send error!\n");
        }

		ret_recv = recv(confd, buf, sizeof(buf), 0);
		if (ret_recv <= 0)
		{
			break;
		}

		printf("%s", buf);
		sleep(1);
    }
#endif

	pthread_create(&tid1, NULL, th1, &confd);
	pthread_create(&tid2, NULL, th2, &confd);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	close(confd);
 
    return NULL;
}

//报警模块
void *warn_th(void *arg)
{
    printf("this is the sock th\n");

	MBS *mbs = arg;
	char sendname[256];
	DATATYPE data;
	int vol_num = 0;
	char num = 0;

	while (1)
	{
		int fd = open("/dev/buzzer", O_RDWR);
		if (fd < 0)
		{
			puts("error!\n");
		}

		recv_msg(mbs, sendname, data);
		vol_num = atoi(data);
		
		if (vol_num > vol_warn_num)
		{
			num = 1;
		}
		else
		{
			num = 0;
		}
		
		write(fd, &num, sizeof(num));
		
		close(fd);
		bzero(sendname, sizeof(sendname));
		bzero(data, sizeof(data));
	}
}

(8)thread_fun.h

#ifndef _THREAD_FUN_H_
#define _THREAD_FUN_H_

extern unsigned short calculate_crc(unsigned char *buf, int len);
extern void *data_collect_th(void *arg);
extern void *show_th(void *arg);
extern void *sock_th(void *arg);
extern void *warn_th(void *arg);

#endif

(9)main.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "thread_fun.h"
#include "mailbox.h"
#include "mailbox_queue.h"
#include "mailbox_list.h"
#include "framebuffer.h"
#include "font.h"
#include "tty.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <time.h>

int main(void)
{ 
	MBS *mbs = create_mail_box_system();//创建线程邮箱

//	register_to_mail_system(mbs, "show", show_th);
//	register_to_mail_system(mbs, "sock", sock_th);//sqtt
	register_to_mail_system(mbs, "data", data_collect_th);
//	register_to_mail_system(mbs, "warn", warn_th);

	wait_all_end(mbs);//销毁线程
	destory_mail_box_system(mbs);//销毁线程邮箱链表
	deinit_fb();//销毁framebuffer

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值