智能家居控制系统

news2024/11/18 7:31:58

🥁作者华丞臧.
📕​​​​专栏【项目经验】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 👉 LeetCode刷题网站


文章目录

  • 一、智能家居​
    • 1.1 项目介绍
    • 1.2 开发平台
      • 1.2.1 硬件
      • 1.2.2 软件
    • 1.3 项目功能
  • 二、Linux的知识点
    • 2.1 Linux
      • 2.1.1 LINUX 下有两种编译器
      • 2.1.2 LINUX文件操作
        • ​​LINUX下的文件系统
        • 交叉开发
      • 2.1.3 文件IO
        • 【打开文件】open
        • 【关闭文件】close
        • 【读文件】read
        • 【写文件】write
        • 【改变文件指针】lseek
  • 三、项目模块
    • 3.1 项目接口
      • 3.1.1 全局数据
    • 3.2 图片显示和字符显示
      • 3.2.1 像素点
      • 3.2.2 图片显示
        • 解析像素组
        • 初始化屏幕和映射
        • 关闭屏幕和解映射
      • 3.2.4 字符显示
    • 3.3 触摸屏
      • 3.3.1 获取触摸坐标
    • 3.4 音乐播放器
    • 3.6 传感器模块
      • 3.6.1 传感器初始化
      • 3.6.2 读取传感器数据
      • 3.6.3 LED灯
    • 3.7 界面切换
    • 3.8 模块合并
      • 3.8.1 线程
        • 创建一个新的线程
        • 获取当前进程id--pthread_self
        • 线程分离--pthread_detach
      • 3.8.2 模块合并
  • 四、功能测试
  • 五、项目源码


一、智能家居​

1.1 项目介绍

智能家居又称智能住宅,是以住宅为平台,利用先进的计算机、嵌入式系统和网络通讯技术,将家中各种设备,包括照明、环境控制系统、网络家电等通过家庭网络连接到一起,构建高效的住宅设施与家庭日程事务的管理系统。与普通的家居相比,智能家居既具有传统的居住功能,又提升了家居安全性、便利性、舒适性、艺术性,保证人们在任何一个有网络的地方就可以掌控家里的一切,因此成为当前人们家装所关注的热点。

1.2 开发平台

1.2.1 硬件

GEC6818 开发平台,核心板采用 10 层板工艺设计,确保稳定可靠,可以批量用于平板电脑,车机,学习机,POS 机,游戏机,行业监控等多种领域。该平台搭载三星 Cortex-A53 系列高性能八核处理器 S5P6818,最高主频高达 1.4GHz,可应用于嵌入式 Linux 和 Android 等操作系统的驱动、应用开发。开发板留有丰富的外设,支持千兆以太网、板载 LVDS 接口、MIPI 接口、USB 接口等。

1.2.2 软件

  • VS2019

本次项目我使用的是VS2019,用来编写代码。

  • VMware(ubantu)

Linux操作系统,用于编译代码并且生成可以在开发板上运行的可执行程序

  • secureCRT

把Linux编译生成的目标文件下载开发板上

secureCRT连接开发板的步骤如下:

  • 选择Serial协议;
    在这里插入图片描述
  • 配置串行端口数据;
    在这里插入图片描述
  • 点击完成。
    在这里插入图片描述
  • 点击连接 出现了绿色√那就说明连接成功了。
    在这里插入图片描述
  • 将可执行文件烧录到开发板上。
    在这里插入图片描述

1.3 项目功能

  1. 把基本的传感器的数据正确的显示在开发板上。
  2. 通过触控控制灯的亮灭。
  3. 通过触控控制音乐播放器。
  4. 显示传感器的测量数据,并通过光照强度来控制灯的亮灭。

二、Linux的知识点

2.1 Linux

虚拟机和ubantu

  • 虚拟机可以让我们在一台电脑中运行不同的操作系统 “双系统”;
  • ubantu是一个基于Linux内核的带图形化的一个Linux的操作系统,使用的是ubantu18.04;

2.1.1 LINUX 下有两种编译器

  1. 本地编译器: gcc
gcc xxx.c -> a.out 默认的可执行文件
gcc xxx.c -o xxx -> xxx是你命名的可执行文件
  1. 交叉编译器:arm-linux-gcc

在一个环境下编译生成适应于另外一个环境的可执行文件 linux -> arm

arm-linux-gcc xxx.c -o xxx -> xxx是你命名的可执行文件					

注意arm-linux-gcc生成的可执行文件只能在arm板上运行

如果我们的代码是在windows上面,但是编译是在Linux上,我们又如何将我们代码放到Linux下面去编译运行;我们虚拟机提供的一个功能:共享文件夹
虚拟文件夹的设置

  • 虚拟机设置 → 选项 → 共享文件夹→ 总是启用 → 添加到共享文件夹中

2.1.2 LINUX文件操作

​​LINUX下的文件系统

文件系统是一套用来管理文件的系统

文件系统的结构

linux系统下只有目录,通过目录来管理文件,所有的文件都是以根目录“/”开头,通过文件的路径可以找到对应的文件。

  • 绝对路径:以根目录开头的路径
  • 相对路径:不以根目录开头的路径
  • 当前路径名+相对路径名 = 绝对路径名

特殊目录
. 当前目录
.. 上一层目录
... 家目录

交叉开发

交叉开发的目的是让我们编写的代码能在目标板上跑起来。

2.1.3 文件IO

即对文件的输入和输出的操作。
Linux下应用程序设计的哲学:

  • 在Linux下,所有一切皆文件;
  • 所有一切包括设备.

【打开文件】open

打开文件所使用的的函数,其函数原型如下:

int open(const char *pathname, int flags);

  • pathname:你要打开的这个文件的设备路径
  • flags:打开文件的方式,三种方式:
    • O_RDONLY:read only 以只读方式打开
    • O_WRONLY:write only 以只写方式打开
    • O_RDWR: 以读写方式打开

返回值:

  • 成功:>0的整数,这个整数就是文件描述符;所有打开的文件都是通过文件描述符来引用的,文件描述符在Linux下,唯一表示一个打开的文件,后面对文件所有的操作都是文件描述符。
  • 失败:-1 错误码,文件不存在。

【关闭文件】close

关闭文件,其函数原型:

int close(int fd);

【读文件】read

从一个文件描述符里面去读,其函数原型如下:

size_t read(int fd, void *buf, size_t count);

  • fd:文件描述符
  • buf:用来保存从文件中读取到的内容
  • count:你想要从文件当中读取多少个字节

函数返回值:

  • 成功:返回我们实际读到的字节数
  • 失败:返回0,表示这个文件已经读到结尾了,没有内容可读。

【写文件】write

将我们的内容写到文件中去,其函数原型如下:

size_t write(int fd, const void *buf, size_t count);

  • fd:文件描述符
  • buf:保存我们要写入文件里面去的内容
  • count:你想要写入的字节数

函数返回值:

  • 写入成功:返回实际写入文件的字节数
  • 写入失败:返回 -1

【改变文件指针】lseek

用来移动你的光标,也就是你的文件的偏移量,其函数原型如下:

off_t lseek(int fd, off_t offset, int whence);

  • offset:文件的偏移量
  • whence:从文件的哪里开始偏移
    • SEEK_SET: 定位到文件的开头

      • 新光标的位置 = 文件开头+offset
      • 让光标处于开头
      • lseek(fd, 0, SEEK_SET);
    • SEEK_CUR: 定位当前的位置

      • 新光标的位置 = 当前的位置+offset
    • SEEK_END: 定位到文件末尾

      • 新光标的位置 = 末尾+offset

返回值:

  • 成功:返回的是文件开头到新光标的位置
  • 失败:返回-1

三、项目模块

在这里插入图片描述

3.1 项目接口

#ifndef _LCD_H_
#define _LCD_H_

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <pthread.h>
#include <termios.h> 
#include <netdb.h>
#include <string.h>
#include <errno.h>

//图片显示
int display_bmp(int x0, int y0);

//初始化屏幕和映射
int init_lcd();

//关闭屏幕和解映射
int uninit_lcd();

//映射
void lcd_draw_point(int i, int j, int color);

//触摸屏
int get_xy();

//读取图片数据
int read_data();

//字模
void draw_word(int x0, int y0, int w, int h, int color, char s1[]);

//图片转换
void bmp_switch();

//led控制函数
void led_ctrl(char* led_id, int on_or_off);

//音乐
int music_play();

//数字显示
void lcd_number(int x0, int y0, double lf);

初始化gy39串口
void init_tty(int fd);

//测量
void gy39();
#endif

3.1.1 全局数据

#include "IHSys.h"

//
#define N 3
#define LED_D7	"/sys/kernel/gec_ctrl/led_d7"
#define LED_D8	"/sys/kernel/gec_ctrl/led_d8"
#define LED_D9	"/sys/kernel/gec_ctrl/led_d9"
#define LED_D10	"/sys/kernel/gec_ctrl/led_d10"
#define LED_ALL	"/sys/kernel/gec_ctrl/led_all"
#define BEEP 	"/sys/kernel/gec_ctrl/beep"

#define COM2	"/dev/ttySAC1"
#define COM3	"/dev/ttySAC2"
#define COM4	"/dev/ttySAC3"

int music_start = 0; //标记madplay是否占用

int lcd_fd;  //灯文件
int* plcd;  //像数文件
int bmp_fd; //图片文件
int led_fd; //显示屏文件

int width; //图片宽度
int height;//图片高度
int depth;//图片深度

char* p; //像素数组

int lack; //像数组每行缺少字节数
int total_bytes;//像素字节总数

int read_x = -1, read_y = -1; //x横y纵
int ret_x, ret_y;//x横y纵

int flag_cont_music = 1; //music状态
int music_count = 0;   //MP3下标

unsigned char rbuf1[9];  //获取光照数据
unsigned char rbuf2[15]; //获取温湿度大气压海拔数据
double LUX, T, P, HUM, H; //光照、温度、压强、湿度、海拔

int count = 2;		 //歌曲计数
int start_all = 1;   //进入主界面标记
int flag_light = 0;  //进入灯界面标记
int flag_mp3 = 0;    //进入音乐界面标记
int flag_dc = 0;     //进入测量面标记
int flag_on_off = 1; //灯的状态
int flag_light_sys = 1;  //灯驱动的状态

char* mp3[7] = { "/music/music_list/01.mp3","/music/music_list/02.mp3",
		"/music/music_list/03.mp3" ,"/music/music_list/04.mp3" ,
		"/music/music_list/05.mp3" ,"/music/music_list/06.mp3" ,
		"/music/music_list/07.mp3" }; //音乐所在路径(开发板上)

char number[][24 * 48 / 8] = {
	{/*--  文字:  0  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x01,0xFF,0x80,0x03,0xC3,
0xC0,0x07,0x81,0xE0,0x0F,0x81,0xF0,0x0F,0x00,0xF0,0x1F,0x00,0xF8,0x1F,0x00,0xF8,
0x1E,0x00,0x78,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,
0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,
0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,
0x3E,0x00,0x78,0x1F,0x00,0xF8,0x1F,0x00,0xF8,0x0F,0x00,0xF0,0x0F,0x81,0xF0,0x07,
0x81,0xE0,0x03,0xC3,0xC0,0x01,0xFF,0x80,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},

 { /*--  文字:  1  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x1C,0x00,0x00,0x7C,
0x00,0x07,0xFC,0x00,0x07,0xFC,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,
0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,
0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,
0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,
0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,
0x3E,0x00,0x00,0x3E,0x00,0x07,0xFF,0xE0,0x07,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },


{ /*--  文字:  2  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x80,0x03,0xFF,0xC0,0x0F,0x83,
0xE0,0x0E,0x01,0xF0,0x1E,0x00,0xF8,0x1E,0x00,0xF8,0x3E,0x00,0x78,0x3E,0x00,0x78,
0x3F,0x00,0x78,0x3F,0x00,0x78,0x1F,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF0,0x00,
0x01,0xF0,0x00,0x01,0xE0,0x00,0x03,0xC0,0x00,0x07,0xC0,0x00,0x0F,0x80,0x00,0x0F,
0x00,0x00,0x1E,0x00,0x00,0x3C,0x00,0x00,0x78,0x00,0x00,0xF0,0x00,0x01,0xE0,0x00,
0x03,0xC0,0x00,0x07,0x80,0x1C,0x0F,0x00,0x1C,0x0E,0x00,0x38,0x1C,0x00,0x38,0x3C,
0x00,0x78,0x3F,0xFF,0xF8,0x3F,0xFF,0xF8,0x3F,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{/*--  文字:  3  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x00,0x07,0xFF,0x80,0x0F,0x07,
0xC0,0x1E,0x03,0xE0,0x1E,0x01,0xF0,0x1E,0x01,0xF0,0x1F,0x00,0xF0,0x1F,0x00,0xF0,
0x1E,0x00,0xF0,0x00,0x00,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,0xE0,0x00,
0x07,0xC0,0x00,0x1F,0x80,0x00,0xFE,0x00,0x00,0x7F,0x80,0x00,0x03,0xE0,0x00,0x01,
0xF0,0x00,0x00,0xF0,0x00,0x00,0xF8,0x00,0x00,0x78,0x00,0x00,0x7C,0x00,0x00,0x7C,
0x1E,0x00,0x7C,0x3F,0x00,0x7C,0x3F,0x00,0x78,0x3F,0x00,0xF8,0x3E,0x00,0xF8,0x1E,
0x01,0xF0,0x0F,0x03,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{/*--  文字:  4  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0x03,0xC0,0x00,0x07,
0xC0,0x00,0x0F,0xC0,0x00,0x0F,0xC0,0x00,0x1F,0xC0,0x00,0x1F,0xC0,0x00,0x3F,0xC0,
0x00,0x77,0xC0,0x00,0x77,0xC0,0x00,0xE7,0xC0,0x01,0xE7,0xC0,0x01,0xC7,0xC0,0x03,
0x87,0xC0,0x03,0x87,0xC0,0x07,0x07,0xC0,0x0E,0x07,0xC0,0x0E,0x07,0xC0,0x1C,0x07,
0xC0,0x3C,0x07,0xC0,0x38,0x07,0xC0,0x7F,0xFF,0xFC,0x7F,0xFF,0xFE,0x3F,0xFF,0xFC,
0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,
0x07,0xC0,0x00,0x07,0xC0,0x00,0x7F,0xFC,0x00,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{/*--  文字:  5  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF8,0x0F,0xFF,0xF8,0x0F,0xFF,
0xF8,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,
0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x3E,0x00,0x0F,0xFF,0xC0,0x0F,
0xFF,0xE0,0x0F,0x83,0xF0,0x1F,0x01,0xF0,0x1E,0x00,0xF8,0x0C,0x00,0xF8,0x00,0x00,
0x78,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x1E,0x00,0x7C,
0x3F,0x00,0x7C,0x3F,0x00,0x78,0x3F,0x00,0x78,0x3E,0x00,0xF8,0x1E,0x00,0xF0,0x1E,
0x01,0xF0,0x0F,0x03,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{/*--  文字:  6  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0xFF,0xE0,0x03,0xE1,
0xF0,0x03,0xC1,0xF8,0x07,0x81,0xF8,0x0F,0x01,0xF0,0x0F,0x00,0xE0,0x1E,0x00,0x00,
0x1E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x3F,0x80,0x3E,
0xFF,0xE0,0x3F,0xF3,0xF0,0x3F,0xC1,0xF8,0x3F,0x80,0xF8,0x3F,0x00,0x7C,0x3E,0x00,
0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,
0x3E,0x00,0x3C,0x3E,0x00,0x7C,0x1F,0x00,0x7C,0x1F,0x00,0x78,0x0F,0x80,0x78,0x0F,
0x80,0xF0,0x07,0xE1,0xE0,0x03,0xFF,0xC0,0x00,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{ /*--  文字:  7  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFC,0x1F,0xFF,0xFC,0x1F,0xFF,
0xF8,0x1F,0x00,0x78,0x1C,0x00,0x70,0x1C,0x00,0xF0,0x38,0x00,0xE0,0x38,0x01,0xE0,
0x00,0x01,0xC0,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x07,0x80,0x00,0x07,0x00,0x00,
0x0F,0x00,0x00,0x0E,0x00,0x00,0x1E,0x00,0x00,0x1E,0x00,0x00,0x1C,0x00,0x00,0x3C,
0x00,0x00,0x3C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x78,0x00,0x00,0x78,0x00,
0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,
0xFC,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},

{ /*--  文字:  8  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x80,0x07,0xFF,0xC0,0x0F,0x81,
0xE0,0x1F,0x00,0xF0,0x1E,0x00,0x78,0x3C,0x00,0x78,0x3C,0x00,0x78,0x3C,0x00,0x7C,
0x3C,0x00,0x7C,0x3E,0x00,0x78,0x1F,0x00,0x78,0x1F,0x80,0x70,0x0F,0xC0,0xF0,0x07,
0xF1,0xE0,0x03,0xFF,0xC0,0x01,0xFF,0x00,0x03,0xFF,0xC0,0x07,0x9F,0xE0,0x0F,0x07,
0xF0,0x1E,0x03,0xF0,0x3C,0x01,0xF8,0x3C,0x00,0xF8,0x3C,0x00,0x7C,0x7C,0x00,0x7C,
0x78,0x00,0x3C,0x78,0x00,0x3C,0x3C,0x00,0x3C,0x3C,0x00,0x78,0x3C,0x00,0x78,0x1E,
0x00,0xF0,0x0F,0x81,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{ /*--  文字:  9  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x00,0x07,0xFF,0x80,0x0F,0x83,
0xC0,0x1F,0x01,0xE0,0x1E,0x00,0xF0,0x3E,0x00,0xF0,0x3C,0x00,0x78,0x3C,0x00,0x78,
0x7C,0x00,0x78,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x3C,
0x00,0x7C,0x3E,0x00,0xFC,0x3E,0x00,0xFC,0x3E,0x01,0xFC,0x1F,0x07,0xFC,0x0F,0xFF,
0x7C,0x07,0xFE,0x7C,0x01,0xF8,0x7C,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,
0x00,0x00,0xF8,0x00,0x00,0xF0,0x0E,0x01,0xF0,0x1F,0x01,0xE0,0x1F,0x03,0xE0,0x1F,
0x07,0xC0,0x1F,0x8F,0x80,0x0F,0xFF,0x00,0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },

{ /*--  文字:  .  --*/
/*--  宋体36;  此字体下对应的点阵为:宽x高=24x48   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x1F,0x80,0x00,0x3F,
0xC0,0x00,0x3F,0xC0,0x00,0x1F,0x80,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
};

3.2 图片显示和字符显示

首先从智能家居系统图片显示开始,也就是说我们需要在屏幕上显示图片,并且能够进行图片切换的功能;在GEC6818开发板上配置了一块LCD屏幕,其分辨率为800*480,即表示屏幕上有480行每一行有800个像素点

3.2.1 像素点

像素点是可以显示某种颜色的点,在开发板上显示一个颜色就是给对应的像素点赋值,在LInux操作系统中一切皆文件,LCD屏幕在OS内核中的一个结构体指向的一个文件,这个文件描述了LCD的相关属性,因此对LCD屏的操作就可以转化为对文件的操作。让LCD屏显示某种颜色就是将该颜色对应的值写入到LCD屏对应的文件中。那么颜色如何描述的呢?

一个像素点的颜色由三种基色组成,这三种基色分别是红绿蓝,三种基色通过量化可以表示出不同的颜色,量化即不同程度的红绿蓝数量化;我们把一个像素点的颜色看做四个字节的大小的整型ARGB,其中A表示透明度(了解),R表示红色,G表示绿色,B表示蓝色,在这4个字节中,从低到高位分别代表B、G、R、A,一般使用的都是低三个字节,本项目中也没有使用到透明度。

总结:

  • 每一种基色占据1个字节:0~255。
  • 每一种颜色由四字节表示。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define RED		0XFF0000
#define GREEN 	0X00FF00
#define BULE	0X0000FF

int main()
{
	//打开屏幕
	int lcd_fd = open("/dev/fb0", O_RDWR);    //打开LCD屏幕文件
	if(-1 == lcd_fd)
	{
		perror("open lcd_fd failed: ");
		return -1;
	}
	
	unsigned int color;
	int i,j;
	//写颜色
	for(i = 0; i < 480; i++)
	{
		for(j = 0; j < 800; j++)
		{
			if(i >= 0 && i < 160)
			{
				color = RED;
				write(lcd_fd, &color, 4);  //写入颜色
			}
			else if(i >= 160 && i < 320)
			{
				color = GREEN;
				write(lcd_fd, &color, 4);  //写入颜色
			}
			else
			{
				color = BULE;
				write(lcd_fd, &color, 4);  //写入颜色
			}
		}
	}
	//关闭文件
	close(lcd_fd);
	return 0;
}

在ubantu上运行代码并将可执行文件下载到开发板上,运行结果如下:
在这里插入图片描述

3.2.2 图片显示

图片显示我选择使用bmp格式的图片,并且也是800*480的分辨率;如果图片不是bmp的可以使用电脑自带的画图将图片格式和分辨率改成合适的格式即可,步骤如下:
在这里插入图片描述

在这里插入图片描述

bmp图片的优势:bmp是位图文件,是一种无压缩的图片文件格式无压缩;在其中保存了每个像素点ARGB颜色的分量,可以直接读取图片中的像素点。

得到bmp格式的图片只是第一步,接下来需要将图片的像素点读取并且保存在内存中并且写入到LCD屏内核文件中;首先来了解bmp图片文件中的内容,如下图:
在这里插入图片描述
bmp格式的文件分为4部分,其中最值得我们关注的DIB头和像数组;在DIB头中我们可以读取图片的高度和宽度即分辨率,而在像数组中我们可以读取图片的每一个像素点并且将其保存下来;调色板是一种采用索引的压缩算法,目的是为了节省存储空间,只有图片的颜色小于256或等于色的时候才采用,对于像素深度高于16位的图像,不使用调色板,对于24位和32位色深的bmp图片是不需要调色板的

//通过如下的代码可以读取bmp图片的宽度、高度、色深
//读取图片数据
//位图宽度:每一行所占的像素点的个数
int read_data(int i)
{
	bmp_fd = 0;
	char pch3[8] = { 0 };
	sprintf(pch3, "/bmp/p%d.bmp", i);
	printf("%s\n", pch3);

	bmp_fd = open(pch3, O_RDWR);
	if (-1 == bmp_fd)
	{
		perror("open bmp failed:");
		return 0;
	}
	//宽度
	width = 0;
	lseek(bmp_fd, 0x12, SEEK_SET);
	int ret = read(bmp_fd, &width, 4);
	if (-1 == ret)
	{
		printf("read width error.\n");
		return -1;
	}
	printf("width = %d\n", width);
	//但是我们读取到的width有可能是一个负值
	//width > 0:从左到右来保存每一个像素点
	//width < 0:从右往左来保存每一个像素点
	
	//位图高度:这张图片有多少行的像素点
	height = 0;
	lseek(bmp_fd, 0x16, SEEK_SET);
	ret = read(bmp_fd, &height, 4);
	if (-1 == ret)
	{
		printf("read height error.\n");
		return -1;
	}
	printf("height = %d\n", height);
	//height > 0:从下到上保存像素点
	//height < 0 :从上到下保存像素点
	
	//位图的深度(色深):每个像素点所占的位数
	depth = 0;
	lseek(bmp_fd, 0x1c, SEEK_SET);
	ret = read(bmp_fd, &depth, 2);
	if (-1 == ret)
	{
		printf("read depth error.\n");
		return -1;
	}
	printf("depth = %d\n", depth);
	//如果depth == 32,4个字节,则是ARGB
	//如果depth == 24,3个字节,则是RGB,A取默认值0

	if (abs(width) * (depth / 8) % 4)
	{
		lack = 4 - abs(width) * (depth / 8) % 4;
	}

	int line_bytes = lack + abs(width) * (depth / 8);
	total_bytes = line_bytes * abs(height); //图片总字节数

	p = (char*)realloc(p, total_bytes);
	if (p == NULL)
	{
		perror("realloc fail");
		return 0;
	}

	//读像素组
	lseek(bmp_fd, 0x36, SEEK_SET);
	int res = read(bmp_fd, p, total_bytes);
	if (-1 == res)
	{
		perror("read fail");
		return 0;
	}
	close(bmp_fd);
	return 1;
}

内存分配单位是4字节,位图中每行像素数据是连续的,下一行和上一行不能共1个分配单元(4字节)。每行像素数据长度必须是4字节的倍数, 字节数 % 4 不等于0时, 后续字节用0补齐。因此对于每一行的像素我们都需要计算其是否是4的整数,而每一行像素都是一样的所以只需要计算一行的却少数即可。

  • 一行像素点的个数:abs(width)
  • 每一个像素点占多少个字节:depth/8
  • 一行有多少个字节:abs(width)*(depth/8)
  • 为了保证每一行的字节数就是4的倍数,我们就需要在末尾添上几个空白的字节。
  • 每一行需要多少个字节:
int lack = 0;
if(abs(width)*(depth/8)%4)
{
	lack = 4 - abs(width)*(depth/8)%4;
}
  • 一行总字节数:line_bytes = lack + abs(width)*(depth/8);
  • 多少行:bs(height)
  • 整个像素数组的大小:total_bytes = line_bytes * abs(height)
  • 定义一个同样大小的数组去保存这个图片的所有像素点。

解析像素组

计算机操作系统字节序有大小端之分:

  • 大端模式:存储器的低地址,存放的是数据的高字节。
  • 小端模式:存储器的低地址,存放的是数据的低字节。
  • 图片的本质是磁盘上的一个文件,文件中保存了图片的属性和数据,而bmp图片的像素点一般是24位大小或者32位大小,所以像素点有大小端之分。

在这里插入图片描述

bmp图片的像素点一般都是24位,也有32位的,如果是32位的像素点A就有相应的值,而如果是24位的就给A设置为0;以32位来说,我们可以用一个整型来保存一个像素点的大小,然后通过按位或来设置ARGB并将其映射到LCD屏上。

在这里插入图片描述

//图片显示
int display_bmp(int x0, int y0)  //x0和y0表示映射的起始位置
{
	unsigned char a, r, g, b;
	int x, y;//x行,y列
	int i = 0;
	int color = 0;

	if (abs(width) * (depth / 8) % 4)
	{
		lack = 4 - abs(width) * (depth / 8) % 4;
	}

	for (y = 0; y < abs(height); y++)
	{
		for (x = 0; x < abs(width); x++)
		{
			b = p[i++];
			g = p[i++];
			r = p[i++];
			if (depth == 32)
			{
				a = p[i++]; //像素为4字节
			}
			else if (depth == 24)
			{
				a = 0; //像素为3字节
			}

			color = ((a << 24) | (r << 16) | (g << 8) | b);  //小端
			lcd_draw_point(width > 0 ? x + x0 : abs(width) - 1 - x + x0, height > 0 ? abs(height) - 1 - y + y0 : y + y0, color);
		}
		i += lack;
	}
}

//映射
void lcd_draw_point(int i, int j, int color)
{
	if (i >= 0 && i < 800 && j >= 0 && j < 480)
	{
		*(plcd + j * 800 + i) = color; //plcd从0开始映射,每个点都可以通过(plcd+偏移量)来找到
	}
}

初始化屏幕和映射

不同的显示屏硬件,数据线以及时序及硬件操作是不一样的;从应用开发的角度,所有的显示屏只有一个作用帮我们在正确的地方来显示颜色,显示图片。在Linux下,LCD屏幕就是一个文件,OS通过数据结构来管理该文件,那么当OS需要使用LCD资源时,该文件会被加载到内存中因此我们可以把LCD屏幕看做内存中的一片空间,所以只要我们可以将bmp图片的像数组映射到LCD在内核中的空间就可以在LCD屏上显示图片了。

//mmap一种内存映射文件的方法
//mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。
//mmap在用户空间映射调用系统中作用很大。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
//addr:如果addr为NULL,则内核选择创建映射的地址;这是创建新地图最方便的方法平。
//如果addr不为NULL,那么内核会把它作为映射位置的提示;在Linux上,映射将在附近的页面边界。新映射的地址作为调用的结果返回。
//length:映射空间总大小;
//prot:描述了映射所需的内存保护
//flags:确定映射的更新对映射同一区域的其他进程是否可见,以及更新是否可见传递到底层文件。
//返回值:当映射成功后,会返回一个指向文件描述符所对应文件的指针
//初始化屏幕和映射
//lcd_fd和plcd是全局变量
int init_lcd()
{
	//打开屏幕
	lcd_fd = open("/dev/fb0", O_RDWR);  //"/dev/fb0"是开发板上屏幕文件的路径
	if (-1 == lcd_fd)
	{
		perror("open fail");
		return 0;
	}

	//映射
	plcd = mmap(NULL, 480 * 800 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
	if (NULL == plcd)
	{
		perror("mmap fail");
		return 0;
	}

	return 1;
}

关闭屏幕和解映射

//解映射
int munmap(void *addr, size_t length);

//关闭屏幕和解映射
int uninit_lcd()
{
	free(p);
	p = NULL;
	int n = munmap(plcd, 480 * 800 * 4);
	close(lcd_fd);

	return 1;
}

图片显示结果如下:
在这里插入图片描述

3.2.4 字符显示

图片显示和字符显示较为相似,在LCD屏幕上我们可以将字符看做一个一个的像素点组成的,因此我们只需要这个字符的像素点按照其位置一一映射即可实现字符显示,原理就是把屏幕上相对应的像素点点亮。我们可以通过取模软件把字符按照一定的规律变成了16进制的数。
软件链接👉取模软件 ,提取码为:r4ci

取模软件操作如下:

  • 配置取模方式,取消勾选直接倒序和保留;
    在这里插入图片描述
  • 选择字模的字体、字形以及字体大小;
    在这里插入图片描述
  • 取模。
    在这里插入图片描述

在该项目中,除了传感器模块测量的数据需要动态更新外,其他的字符都是静态的,因此对于静态的字符都采用图片的方式显示即可,对于动态的字符使用字模的方式显示。为了很好的表示传感器测量的数据需要保留两位有效数据,因此还需要对.进行取模,将0~9.取模转换的数组存放在一个二维字符数组方便使用。

//字模
void draw_word(int x0, int y0, int w, int h, int color, char s[])
{
	// 遍历数组的每个元素(1个字节 代表每行的8个像素素点的显示信息)
	int i, j;
	int arrary_num = w / 8 * h;  

	for (i = 0; i < arrary_num; i++)
	{
		// 每一个数组元素的每一位代表了一个像素点 从高到低
		// 遍历它的每一位
		for (j = 7; j >= 0; j--)
		{
			/*
			判断该位是不是1
			s[i]>>j & 1 是否 >0
			1000 0000 >> 7 =>0000 0001
			0000 0001
		&	0000 0001
			0000 0001 >0
			*/
			if (*(s + i) >> j & 1)
			{
				//在屏幕对应的位置显示即可
				lcd_draw_point(i % (w / 8) * 8 + 7 - j + y0, i / (w / 8) + x0, color);
			}
		}
	}
}

//数字显示
void lcd_number(int x0, int y0, double lf)  //(x0,y0)表示起始坐标
{
	char* number_string = (char*)calloc(sizeof(char), 50);
	if (NULL == number_string)
	{
		perror("malloc fail");
		return;
	}
	sprintf(number_string, "%.2lf", lf);
	printf("number_string = %s\n", number_string);
	
	char* cur = number_string;
	int n = 0;   //每个字模对应的下标
	int displayce = 10;  //偏移量
	while (*cur)
	{
		if (*cur != '.')
		{
			n = *cur - '0';
		}
		else
		{
			n = 10;
		}

		displayce += 20;
		draw_word(y0, x0 + displayce, 24, 48, 0x050505, number[n]);
		cur++;
	}

	free(number_string);
	number_string = NULL;
}

3.3 触摸屏

3.3.1 获取触摸坐标

触摸屏在Linux下同样是一个文件,其在开发板的路径为:/dev/input/event0,与打开屏幕是相似的。重要的是,Linux是如何描述触摸事件的,怎么保存对应的数据?

操作系统管理触摸事件一定是先描述再组织,因此首先是用一个结构体来描述输入事件,该结构体定义在<linux/input.h>头文件当中。
输入事件的结构体:input_event

struct input_event
{
	struct timeval time;//该输入事件发生的事件,可以精确到微秒	
	
	_u16 type;//事件的类型。如下:
	_u16 code;//要跟据type的不同,code表示的含义也不同
	_u16 value;//要根据type,code的痛而有不同的含义
}

说明:

  • type
    • type = EV_KEY 表示这是一个按键事件。
    • type = EV_REL 表示这是一个鼠标事件。
    • type = EV_ABS 表示这是一个触摸屏事件。
    • type = EV_SYN 事件的分割标志。
  • code:要跟据type的不同,code表示的含义也不同,如:type = EV_ABS
    • code = BTN_TOUCH 表示触摸点击。
    • code = ABS_X 表示的是x轴的坐标。
    • code = ABS_Y 表示的是y轴的坐标。
    • code = ABS_PRESSURE 表示的是给屏幕压力。
  • value:要根据type,code的值而有不同的含义。
    • type = EV_ABS,且 code = ABS_X,value 表示的是x轴的坐标。
    • type = EV_ABS,且code = ABS_Y,value 表示的是y轴的坐标。
    • type = EV_KEY,且code = BTN_TOUCH,value = 1 表示手指按下,value = 0 表示手指松开。

注意:6818开发板现在触摸屏的坐标分成两种 800480、1024600,如果板子是1024*600的话,那就要进行等比例缩小。

//触摸屏
int get_xy()
{
	read_x = -1; //初始化
	read_y = -1; //初始化

	struct input_event ts;
	int touch_fd = open("/dev/input/event0", O_RDONLY);

	while (1)
	{
		//sleep(1);
		read(touch_fd, &ts, sizeof(struct input_event));

		if (ts.type == EV_KEY && ts.code == BTN_TOUCH)
		{
			if (ts.value == 1)
			{
				printf("press\n");
			}
			else
			{
				printf("release\n");
			}
		}

		if (ts.type == EV_ABS)
		{
			if (ts.code == ABS_X)
			{
				read_x = ts.value * 800 / 1024;
				ret_x = read_x;
			}

			if (ts.code == ABS_Y)
			{
				read_y = ts.value * 480 / 600;
				ret_y = read_y;
				break;
			}
		}
	}

	close(touch_fd);
}

3.4 音乐播放器

首先来认识一个Linux上的软件–madplay,madplay是一个Linux下的音乐播放器;如果你想要在开发板上播放你的音乐,首先开发板上需要有对应MP3的音乐,所以需要拷贝音乐文件到开发板上路径自己选择。

播放方式:

  • 播放某一首歌一次:madplay /lixiang/music/1.mp3
  • 播放某一首歌(单曲循环):madplay /lixiang/music/1.mp3 -r
  • 播放我现在这个目录下的全部MP3文件一次:madplay /lixiang/music/*.mp3
  • 播放我现在这个目录下的全部MP3文件(列表循环):madplay /lixiang/music/*.mp3 -r
  • 播放我现在这个目录下的全部MP3文件(随机播放):madplay /lixiang/music/*.mp3 -z

程序控制madplay播放,暂停播放,恢复播放,停止播放,关闭播放器:

  • 后台播放目录下所有的.mp3文件:system(“madplay /lixiang/music/*.mp3 &”)
  • 列表循环播放:system(“madplay /lixiang/music/*.mp3 -r &”)
  • 暂停播放:system(“kill -STOP madplay &”)
  • 恢复播放:system(“kill -CONT madplay &”)
  • 停止播放:system("kill -9 madplay ")
  • 注意:&为后台播放,如果不加&则为前台播放,将无法对其进行暂停,恢复操作

对于一个音乐播放器需要有暂停、下一首、上一首等功能,首先在音乐界面需要有这三个按钮,而通过触摸屏我们可以得到按钮在图片上的位置,所以根据在触摸屏按下的不同位置我们可以进行暂停、下一首、上一首等功能,如下图:
在这里插入图片描述

//system 执行一个shell命令
#include <stdlib.h>
int system(const char *command);
//音乐
int music_play()
{
	int input = 0;
	char cmd[250] = { 0 }; //存放指令

	//上一首 下一首 暂停播放 继续播放 停止播放
	if (flag_mp3 == 1)  //进入音乐界面
	{
		if (read_x >= 220 && read_x <= 260 && read_y >= 380 && read_y <= 440)  // 上一首
		{
			if (music_start == 1)  
			{
				system("killall -9 madplay ");//停止当前正在播放的音乐
			}
			printf("music_start = %d\n", music_start);

			if (music_count == 0)
			{
				music_count = 6;
			}
			else
			{
				music_count--;
			}
			printf("music_count = %d\n", music_count);
			read_data(7);   //读取图片像素组
			display_bmp(0, 0); //显示图片

			sprintf(cmd, "madplay %s -r &", mp3[music_count]);
			music_start = 1;  //标记音乐开始播放
			system(cmd);   //播放音乐
			flag_cont_music = 1;  //音乐正在播放
		}
		else if (read_x >= 540 && read_x <= 580 && read_y >= 380 && read_y <= 430)  //下一首
		{
			if (music_start == 1)  
			{
				system("killall -9 madplay ");//停止当前正在播放的音乐
			}
			printf("music_start = %d\n", music_start);

			if (music_count == 6)
			{
				music_count = 0;
			}
			else
			{
				music_count++;
			}
			printf("music_count = %d\n", music_count);
			read_data(7);
			display_bmp(0, 0);

			sprintf(cmd, "madplay %s -r &", mp3[music_count]);
			music_start = 1;
			system(cmd);
			flag_cont_music = 1;
		}
		else if (read_x >= 350 && read_x <= 450 && read_y >= 370 && read_y <= 480)  //暂停
		{
			if (music_start == 0)
			{
				read_data(7);
				display_bmp(0, 0);
				sprintf(cmd, "madplay %s -r &", mp3[music_count]);
				system(cmd);
				music_start = 1;
			}
			else if (music_start == 1 && flag_cont_music == 1)
			{
				read_data(3);
				display_bmp(0, 0);
				system("killall -STOP madplay &");
				flag_cont_music = -flag_cont_music;
			}
			else if (music_start == 1 && flag_cont_music == -1)
			{
				read_data(7);
				display_bmp(0, 0);
				system("killall -CONT madplay &");
				flag_cont_music = -flag_cont_music;
			}
		}
	}
}

3.6 传感器模块

GY-39 是一款低成本,气压,温湿度,光强度传感器模块。工作电压 3-5v,功耗小,安装方便。其工作原理是, MCU 收集各种传感器数据,统一处理,直接输出计算后的结果,此模块,有两种方式读取数据,即串口 UART( TTL 电平)或者 IIC( 2 线)。串口的波特率有 9600bps 与 115200bps,可配置,有连续,询问输出两种方式,可掉电保存设置。可适应不同的工作环境,与单片机及电脑连接。模块另外可以设置单独传感器芯片工作模式,作为简单传感器模块, MCU 不参与数据处理工作。提供 arduino, 51, stm32 单片机通讯程序,不提供原理图及内部单片机源码。此 GY39 模块另外赠送安卓手机软件 app 查看数据,且支持 wifi 局域内网连接,手机及电脑同时显示数据。

在这里插入图片描述

gy39模块
1.接线方式:
	TTL电平	3.3v~5v:1 0v:0
	VCC接电源 3.3v~5v
	GND接地
	CT	发送数据线
	DR	接收数据线
	
	串口通信:全双工,串行的通信协议
	ARM(6818)		GY39
	VCC-------------VCC
	GND-------------GND
	TX<------------->DR
	RX<------------->CT

3.6.1 传感器初始化

//gy39传感器的初始化函数
void init_tty(int fd)
{
	//声明设置串口的结构体
	struct termios termios_new;
	//先清空该结构体
	bzero(&termios_new, sizeof(termios_new));
	//	cfmakeraw()设置终端属性,就是设置termios结构中的各个参数。
	cfmakeraw(&termios_new);
	//设置波特率
	//termios_new.c_cflag=(B9600);
	cfsetispeed(&termios_new, B9600);
	cfsetospeed(&termios_new, B9600);
	//CLOCAL和CREAD分别用于本地连接和接受使能,因此,首先要通过位掩码的方式激活这两个选项。    
	termios_new.c_cflag |= CLOCAL | CREAD;
	//通过掩码设置数据位为8位
	termios_new.c_cflag &= ~CSIZE;
	termios_new.c_cflag |= CS8;
	//设置无奇偶校验
	termios_new.c_cflag &= ~PARENB;
	//一位停止位
	termios_new.c_cflag &= ~CSTOPB;
	tcflush(fd, TCIFLUSH);
	// 可设置接收字符和等待时间,无特殊要求可以将其设置为0
	termios_new.c_cc[VTIME] = 10;
	termios_new.c_cc[VMIN] = 1;
	// 用于清空输入/输出缓冲区
	tcflush(fd, TCIFLUSH);
	//完成配置后,可以使用以下函数激活串口设置
	if (tcsetattr(fd, TCSANOW, &termios_new))
		printf("Setting the serial1 failed!\n");

}

3.6.2 读取传感器数据

GY39有两种工作模式:

  1. 只获取光照强度
  2. 只获取温湿度大气压强海拔

模块输出每帧包含8-13个字节

  • 只输出光照强度9个字节
  • 只输出温湿度压强海拔13个字节

更加详细的gy39使用方法参考下面的资料,其中详细介绍了gy39传感器的属性,以及如何使用命令字节切换传感器模式、如何读取GY39的测量数据数据。
链接👉:GY39传感器模块,提取码:1234。

数据的显示很简单,得到的数据应该是浮点型的数据,将这个浮点数的每一位都取出来转换成字模的形式打印出来即可,打印时需要注意每一位数字都需要加上一个偏移量,不然就会打印在同一个屏幕位置上。
在这里插入图片描述

void gy39()
{
	//打开串口
	int gy39_fd = open("/dev/ttySAC1", O_RDWR);
	//初始化串口
	init_tty(gy39_fd);
	int tmp = 0;

	unsigned char cmd1[3] = { 0xa5, 0x81, 0x26 }; // 命令字节,获取光照强度命令
	//write(fd, cmd, 3);
	//1000 0010 = > 0x82
	unsigned char cmd2[3] = { 0xa5, 0x82, 0x27 }; // 命令字节,获取温湿度大气压强海拔
	//1000 0011 = 》0x83
	unsigned char cmd3[3] = { 0xa5, 0x83, 0x28 }; // 命令字节,获取温湿度大气压强海拔光照强度

	int ret = write(gy39_fd, cmd1, 3);
	if (ret != 3)
	{
		perror("write fail");
		//sleep(1);
	}


	ret = read(gy39_fd, rbuf1, 9);
	if (ret != 9)
	{
		perror("rbuf1 read fail");
		return;
	}

	//获取光照强度
	if (rbuf1[0] == 0x5a && rbuf1[1] == 0x5a && rbuf1[2] == 0x15)  //rbuf1[2] == 0x15  表示只获取光照强度
	{
		tmp = rbuf1[4] << 24 | rbuf1[5] << 16 | rbuf1[6] << 8 | rbuf1[7];
		LUX = tmp / 100.0;
		printf("LUX = %.2lf\n", LUX);
	}

	//光照强度大于100,熄灭灯
	if (LUX > 100)
	{
		led_ctrl(LED_ALL, 0);
		if (flag_light == 1)
		{
			read_data(4);
			display_bmp(0, 0);
			flag_on_off = 1;
		}
		else if (flag_light == 0)
		{
			flag_on_off = 1;
		}
	}

	//切换模式 获取温湿度大气压强海拔
	ret = write(gy39_fd, cmd2, 3);
	if (ret != 3)
	{
		perror("write fail");
		//sleep(1);
	}

	ret = read(gy39_fd, rbuf2, 15);
	if (ret != 15)
	{
		perror("rbuf2 read fail");
		//return;
	}

	//获取温度
	if (rbuf2[0] == 0x5a && rbuf2[1] == 0x5a && rbuf2[2] == 0x45)//rbuf2[2] == 0x45  表示只获取温湿度大气压强海拔
	{
		//温度
		tmp = rbuf2[4] << 8 | rbuf2[5];
		T = tmp / 100.0;
		printf("T = %.2lf\n", T);

		//气压
		tmp = rbuf2[6] << 24 | rbuf2[7] << 16 | rbuf2[8] << 8 | rbuf2[9];
		P = tmp / 100.0;
		printf("P = %.2lf\n", P);

		//湿度
		tmp = rbuf2[10] << 8 | rbuf2[11];
		HUM = tmp / 100.0;
		printf("HUM = %.2lf\n", HUM);

		//海拔
		tmp = rbuf2[12] << 8 | rbuf2[13];
		H = tmp;
		printf("H = %.2lf\n", H);
	}
	printf("\n");

	if (flag_dc == 1)
	{
		read_data(6);
		display_bmp(0, 0);
		lcd_number(150, 350, LUX); //光照强度
		lcd_number(560, 70, T);    //温度
		lcd_number(140, 210, P);   //压强
		lcd_number(150, 70, HUM);  //湿度
		lcd_number(560, 215, H);   //海拔
		sleep(1);
	}
	close(gy39_fd);
	return;
}

3.6.3 LED灯

控制LED灯同样是打开一个开发板上LED灯的文件,将1(高电平)或者0(低电平)写入即可操作灯的亮灭。

  • 写1,亮。
  • 写0 ,灭。

注意:加载led的内核驱动把kobject_led.ko下载到开发板上,然后输入命令:(kobject_led.ko文件👉LED驱动文件,提取码:1234)

//开发板上以命令行模式输入,每次开启开发板都需要设置
insmod kobject_led.ko

//system 执行一个shell命令
system("insmod kobject_led.ko");  
//led控制函数
#define LED_D7	"/sys/kernel/gec/ctrl/led_d7"
#define LED_D8	"/sys/kernel/gec/ctrl/led_d8"
#define LED_D9	"/sys/kernel/gec/ctrl/led_d9"
#define LED_D10	"/sys/kernel/gec/ctrl/led_d10"
#define LED_ALL	"/sys/kernel/gec/ctrl/led_all"
#define BEEP 	"/sys/kernel/gec/ctrl/beep"

void led_ctrl(char* led_id, int on_or_off)
{
	printf("%d\n", on_or_off);
	led_fd = open(led_id, O_RDWR);

	write(led_fd, &on_or_off, 4);

	close(led_fd);
}

3.7 界面切换

智能家居系统包含灯光控制、音乐播放、环境测量三个功能,系统启动后首先是一个启动界面,在屏幕上任意点击后进入系统主界面,主界面给用户提供三个按钮,分别是灯光控制、音乐播放、查看环境测量数据;用户点击对应的功能按钮进入对应的功能界面,每个界面中触摸屏的点击作用是不相同的,如:在灯光控制界面点击灯就是控制灯的亮灭,而在音乐界面点击的作用就是播放、上一首、下一首等;所以不同的界面必须有不同的标记,在该项目中我使用的是全局变量来标记触摸屏当前所在的界面。
在这里插入图片描述

//
int start_all = 1;   //进入主界面标记
int flag_light = 0;  //进入灯界面标记
int flag_mp3 = 0;    //进入音乐界面标记
int flag_dc = 0;     //进入测量面标记
int flag_on_off = 1; //灯的状态
int flag_light_sys = 1;  //灯驱动的状态
int flag_cont_music = 1;//music状态
int music_count = 0;//MP3下标

void bmp_switch()
{
	if (read_x != -1 && read_y != -1 && start_all == 1)  //进入主界面
	{
		start_all = 0;
		read_data(2);
		display_bmp(0, 0);
	}
	else if (start_all == 0)  //
	{
		//mp3
		if (read_x >= 300 && read_x <= 450 && read_y >= 170 && read_y <= 310
			&& start_all == 0 && flag_light != 1 && flag_mp3 == 0 && flag_dc != 1)
		{
			flag_mp3 = 1;
			if (music_start == 1 && flag_cont_music == 1)
			{
				read_data(7);
				display_bmp(0, 0);
			}
			else
			{
				read_data(3);
				display_bmp(0, 0);
			}

		}


		// mp3返回主界面,关闭后台播放
		if (read_x >= 760 && read_x <= 800 && read_y >= 0 && read_y <= 60
			&& start_all == 0 && flag_light != 1 && flag_mp3 == 1 && flag_dc != 1)
		{
			system("killall -9 madplay ");
			flag_mp3 = 0;
			flag_cont_music = -1;
			music_start = 0;
			read_data(2);
			display_bmp(0, 0);
		}

		//灯
		if (read_x >= 60 && read_x <= 200 && read_y >= 170 && read_y <= 310
			&& start_all == 0 && flag_light == 0 && flag_mp3 != 1 && flag_dc != 1)
		{
			if (flag_light_sys == 1)
			{
				system("insmod kobject_led.ko");
				flag_light_sys = -flag_light_sys;
			}

			flag_light = 1;
			if (flag_on_off == -1)
			{
				read_data(5);
				display_bmp(0, 0);
			}
			else
			{
				read_data(4);
				display_bmp(0, 0);
			}

		}

		if (read_x >= 300 && read_x <= 450 && read_y >= 170 && read_y <= 310
			&& start_all == 0 && flag_light == 1 && flag_mp3 != 1 && flag_dc != 1)
		{

			//printf("%d\n",flag_on_off);
			if (flag_on_off == 1)
			{
				read_data(5);
				display_bmp(0, 0);
				led_ctrl(LED_ALL, 1);
			}
			else if (flag_on_off == -1)
			{
				read_data(4);
				display_bmp(0, 0);
				led_ctrl(LED_ALL, 0);
			}
			flag_on_off = -flag_on_off;
		}

		//温湿度
		if (read_x >= 550 && read_x <= 700 && read_y >= 170 && read_y <= 310
			&& start_all == 0 && flag_light != 1 && flag_mp3 != 1 && flag_dc == 0)
		{
			flag_dc = 1;
			read_data(6);
			display_bmp(0, 0);
		}

		// 返回主界面,功能模块后台运行
		if (read_x >= 0 && read_x <= 40 && read_y >= 0 && read_y <= 60)
		{
			if (start_all == 0 && flag_light != 1 && flag_mp3 == 1 && flag_dc != 1)
			{
				flag_mp3 = 0;
				read_data(2);
				display_bmp(0, 0);
			}
			else if (start_all == 0 && flag_light == 1 && flag_mp3 != 1 && flag_dc != 1)
			{
				flag_light = 0;
				read_data(2);
				display_bmp(0, 0);
			}
			else if (start_all == 0 && flag_light != 1 && flag_mp3 != 1 && flag_dc == 1)
			{
				flag_dc = 0;
				read_data(2);
				display_bmp(0, 0);
			}
		}
	}
}

3.8 模块合并

3.8.1 线程

  • Linux认为:没有进程,没有线程;在概念上区分,只有一个叫做执行流
  • 在一个程序里的一个执行路线就叫线程(thread),更准确的定义是:线程是一个进程内部的控制序列;一切进程至少都有一个执行线程。
  • 线程在进程内部运行,本质是在进程地址空间内运行;在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化,透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

线程是真正的基本调度单位,线程是在进程中的执行流,在Linux当中线程是在进程的地址空间内运行的,地址空间是进程看待资源的统一的视角,多个执行流将进程的资源划分。

重新理解进程,在学习Linux中进程概念时,我们理解进程为内核数据结构加上进程对应的代码和数据;那么在学习了线程后,从内核视角来看:
进程是承担分配系统资源的基本实体(进程的基座属性),即向系统申请资源的基本单元。

  • 单执行流进程: 内部只有一个执行流的进程;
  • 多执行流进程:内部有多个执行流的进程;

线程总结

  1. 在进程内部运行的执行流
  2. 线程比进程粒度更细,调度成本更低;
  3. 线程是CPU调度的基本单元;
  4. 进程内部的线程共享主线程的地址空间,每个进程可以占有进程的一部分资源,执行进程的一部分代码;

创建一个新的线程

功能:创建一个新的线程
原型
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
 					void *(*start_routine)(void*), void *arg);
参数
	thread:返回线程ID
	attr:设置线程的属性,attr为NULL表示使用默认属性
	start_routine:是个函数地址,线程启动后要执行的函数
	arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

获取当前进程id–pthread_self

  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID
  • 不是一回事。
  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:
pthread_t pthread_self(void);

线程分离–pthread_detach

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
  • 新线程分离,但是主线程先退出,那么该进程就会退出对应的所有线程也就会全部退出;一般我们分离线程,对应的main thread一般不要退出,常驻内存。
int pthread_detach(pthread_t thread);

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离;

pthread_detach(pthread_self());

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

3.8.2 模块合并

智能家居系统包括图片显示、触摸屏、LED灯、音乐播放、传感器测量等模块,其中的一些模块是相互关联的,点击屏幕不仅仅需要进行图片切换还需要操作某些硬件来完成相应的任务,如:进入LED灯的界面可以控制灯的开关,进入音乐界面可以播放、暂停、切换音乐;也就是说在触摸屏幕的同时需要进行其他的操作,而线程能够增加多个执行流,很好地实现模块的合并。

#include "IHSys.h"

//获取坐标
void* get_user_touch()
{
	pthread_detach(pthread_self()); //线程分离
	while (1)
	{
		get_xy();
		//printf("x = %d y = %d\n", ret_x, ret_y);
	}
}

//测量
void* get_gy39()
{
	pthread_detach(pthread_self()); //线程分离
	while (1)
	{
		gy39();
	}
}

//音乐
void* get_music()
{
	pthread_detach(pthread_self()); //线程分离
	while (1)
	{
		music_play();
	}
}

//判断触摸点
void* get_bmp_switch()
{
	pthread_detach(pthread_self()); //线程分离
	while (1)
	{
		bmp_switch();
	}
}

int main()
{

	//初始化屏幕和映射
	init_lcd();

	//读取图片数据
	read_data(1); //系统启动界面

	display_bmp(0, 0);

	pthread_t id[4];
	pthread_create(&id[0], NULL, get_user_touch, (void*)NULL);
	pthread_create(&id[1], NULL, get_gy39, (void*)NULL);
	pthread_create(&id[2], NULL, get_music, (void*)NULL);
	pthread_create(&id[3], NULL, get_bmp_switch, (void*)NULL);

	while (1)
	{
		;
	}

	//关闭屏幕和解映射
	uninit_lcd();
	return 0;
}

在这里插入图片描述

四、功能测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

视频观看 👉智能家居系统

五、项目源码

Gitee:智能家居系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/380743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MIGO 物料过账 创建物料凭证 BAPI_GOODSMVT_CREATE

文章目录1.前台操作2.需求分析2.1调用方式2.2分为两大概括:2.3业务逻辑细节图3.BAPI_GOODSMVT_CREATE4.RFC接口代码5.总结1.前台操作 SAP CO01(创建生产订单)/MIGO(发货投料)前台操作 这里面有migo的前台操作,首先了解前台操作后再去写RFC接口是比较容易理解的.!! 2.需求分析…

opencv读入图片注意事项

来源&#xff1a;投稿 作者&#xff1a;蓬蓬奇 编辑&#xff1a;学姐 深度学习数据预处理中常用opencv读入图片&#xff0c;一般在__getitem__函数中调用。本文主要介绍opencv读取图片的一些细节以及注意事项。本文中使用的图片见第6节“opencv测试使用的图片”。 1.如何通过o…

计算机底层:储存器的性能指标(CPU和内存等硬件的性能以及 对比标准)

计算机底层&#xff1a;储存器的性能指标(CPU和内存等硬件的性能以及 对比标准) 内存&#xff1a; MAR是存放地址的寄存器&#xff1b;MDR是存放数据的寄存器。 MAR是存放地址的寄存器&#xff0c;那么其中的二进制位一定是不能重复的&#xff0c;试想&#xff0c;如果有有两个…

《爆肝整理》保姆级系列教程python接口自动化测试框架(二十六)--批量执行用例 discover(详解)

简介  我们在写用例的时候&#xff0c;单个脚本的用例好执行&#xff0c;那么多个脚本的时候&#xff0c;如何批量执行呢&#xff1f;这时候就需要用到 unittest 里面的 discover 方法来加载用例了。加载用例后&#xff0c;用 unittest 里面的 TextTestRunner 这里类的 run 方…

类和对象 - 上

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 面向过程与面向对象 面向过程的解决方法 面向对象的解决方法 面向对象的优势 类的引入 早期C类的实现 class定义类 class定义规则 类成员的两种定义方式 类的访问限定符及封装 访…

在数字优先的世界中打击知识产权盗窃

在当今数据驱动的世界中&#xff0c;全球许多组织所面临的期望和需求正在达到前所未有的水平。 为了迎接挑战&#xff0c;数据驱动的方法是必要的&#xff0c;需要有效的数字化转型来提高运营效率、简化流程并从遗留技术中获得更多收益。 但是&#xff0c;虽然数字优先方法可…

Spring Boot+Vue前后端分离项目练习01之网盘项目的搭建

1.前期设计 一般看来&#xff0c;网盘系统主要实体包括&#xff1a;用户、存储信息、用户文件、文件、回收文件等基本功能点。 各实体对应的表结构如下所示&#xff1a; 表名&#xff1a;user&#xff08;用户表&#xff09; 字段名属性说明userIdbigint主键usernamevarcha…

数据结构与算法系列之顺序表的实现

这里写目录标题顺序表的优缺点&#xff1a;注意事项test.c&#xff08;动态顺序表&#xff09;SeqList.hSeqList.c各接口函数功能详解void SLInit(SL* ps);//定义void SLDestory(SL* ps);void SLPrint(SL* ps);void SLPushBack(SL* ps ,SLDataType * x );void SLPopBack(SL* ps…

[busybox] busybox生成一个最精简rootfs(下)

书接上回&#xff1a;[busybox] busybox生成一个最精简rootfs(上) 本篇介绍几个rootfs中用到的“不是那么重要的”几个文件。 9 /etc/shadow 和 /etc/passwd 曾经&#xff0c;/etc/passwd 文件用于存储独立 Linux 系统中的所有登录信息。 后来&#xff0c;由于以下原因&…

算法训练营 day63 单调栈 下一个更大元素II 接雨水

算法训练营 day63 单调栈 下一个更大元素II 接雨水 下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的…

2023软件测试工程师涨薪攻略,3年如何达到30K

1.软件测试如何实现涨薪 首先涨薪并不是从8000涨到9000这种涨薪&#xff0c;而是从8000涨到15K加到25K的涨薪。基本上三年之内就可以实现。 如果我们只是普通的有应届毕业生或者是普通本科那我们就只能从小公司开始慢慢往上走。 有些同学想去做测试&#xff0c;是希望能够日…

Vue3 关于 provide、inject 的用法

前言&#xff1a; 在前端项目中牵扯的最多的莫过于组件之间的传值了&#xff0c;除了最最常用的 props 和 emit&#xff0c;其实在 Vue 中还额外提供了另外几种方法。今天分享一种组件之间通信的方法。 一. 场景再现 先别着急考虑标题这个 api 的含义。在这里我先动手写一个比较…

如何使用Codecepticon对C#、VBA宏和PowerShell源代码进行混淆处理

关于Codecepticon Codecepticon是一款功能强大的代码混淆处理工具&#xff0c;该工具专为红队和紫队渗透测试安全活动而开发&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松对C#、VBA5/VBA6&#xff08;宏&#xff09;和PowerShell源代码进行混淆处理。Codecep…

【Datawhale图机器学习】图神经网络

图神经网络 GNN是一种连接模型&#xff0c;通过网络中节点之间的信息传递的方式来获取图中的依存关系&#xff0c;GNN通过从节点任意深度的邻居来更新该节点状态&#xff0c;这个状态能够表示状态信息。第一次在论文 The graph neural network model 中提出 与传统NN的区别&a…

UnsupportedOperationException深层探究

在调试程序的时候发现一个奇怪的问题&#xff0c;代码都是写过的很常见的代码&#xff0c;但是看日志会报错&#xff0c;报错信息如下&#xff1a;Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractList.set(AbstractList.j…

mysql last lesson

1:创建用户 create user zhang identified by 12345678;2&#xff1a;给用户授权&#xff0c;撤销授权&#xff0c; grant.......to revoke ....... 3:将数据库中的数据导出 C:\Windows\system32>mysqldump bjpowernode>C:\bjpowernode.sql -uroot -p12345678 4&#…

nginx-ingress部署+跨命名空间转发

nginx-ingress部署一、环境信息二、k8s环境搭建三、ingress环境搭建3.1 deploy.yaml文件3.2 service-nodeport.yaml文件四、按照业务建立service及ingress4.1 业务信息4.2 建立service4.3 创建ingress五、验证结果一、环境信息 k8s集群版本&#xff1a;1.23.6ingress版本&…

MySQL高级三

目录 三、MySQL高级03 3.1 MyCat 3.1.1 MyCat简介 3.1.2 中间件的作用 3.2 安装MyCat 3.3 主从复制 3.3.1 主从复制的原理 3.3.2 主从复制的好处 3.3.3 配置主从复制 三、MySQL高级03 如果虚拟机的磁盘已满&#xff0c;可以对磁盘进行重新分配 参考&#xff1a;虚拟…

将IDEA的项目托管到gitee

目录1. 在gitee上创建仓库2. 本地创建仓库目录3. 将项目添加到缓冲区4. 将缓冲区的项目添加到本地仓库5. 将本地仓库的项目上传到gitee6. 遇到的问题6.1 问题描述6.2 解决方法7. 相关图示与补充8. 相关参考1. 在gitee上创建仓库 2. 本地创建仓库目录 在IDEA中选择创建 Git 仓…

命令行和Python交互模式

命令行和Python交互模式的区别 命令行模式mac打开终端 在命令行模式下&#xff0c;可以执行python进入Python交互式环境&#xff0c;也可以执行python hello.py运行一个.py文件。 执行一个.py文件只能在命令行模式执行 Python交互模式键入python3即可&#xff0c;交互模式下可…