本文主要是围绕数码管的显示、ADC温度采集、按键操作展开,温度的正常阈值保存在eeprom中,当温度的值超过
设定的阈值后,数码管闪烁,蜂鸣器报警。按键可以调节温度的上、下限。
其中,板载的按键可以加热发热电阻进行场景的测试。
注:涉及到驱动文件imx_key.ko lradc.ko可以自己编写或者在我的资源中下载。在编译过程中,对数函数的编译加上 -lm ,多线程的编译加上 -pthread
程序非最终版。中间可以增加互斥锁和线程控制,确保安全稳定。
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include "lradc.h"
#define LRADC_DEV "/dev/magic-adc" /* adc设备文件名*/
#define CMD_VOLTAGE IMX28_ADC_CH0 /* 通道0读取命令*/
#define CMD_TEMPTURE IMX28_ADC_CH1 /* 通道1通读读取命令*/
#define R33 2000
#define V_ADC 3.3
//#define T1 (273.15 + 25)
#define R1 27600
#define B 3435*1000
#define I2C_SLAVE 0x0703
#define I2C_ADDR 0xA0 /* EEPROM的从机地址*/
#define I2C_DEV_NAME "/dev/i2c-1" /* I2C总线设备文件*/
#define DATA_ADDR 0x00 /* 保存温度的起始地址*/
#define SPI_DEVICE "/dev/spidev1.0"
#define GPIO_DEVICE "/sys/class/gpio/gpio117/value" // gpio3.21的属性文件
#define ADC_DEV "/dev/magic-adc"
#define ADC_MODULE_PATH "/root/lradc.ko"/* ADC驱动模块的默认路径*/
#define DIGITRON_ON 1 /* 使能数码管显示*/
#define DIGITRON_OFF 0 /* 禁能数码管显示*/
#define DEV_NAME "/dev/five_buttons"
#define KEY_MODULE_PATH "/root/imx_key.ko"
#define BEEP_DEV "/sys/class/leds/beep/brightness" /* 蜂鸣器设备属性文件路径*/
pthread_mutex_t numlock = PTHREAD_MUTEX_INITIALIZER;/* 静态初始化互斥量*/
static int beep_fd;
static int fd;
static int show_value[4] ;/* 保存要显示的信息*/
static int is_show = DIGITRON_ON;/* 保存是否使能数码管显示*/
static int i2c_fd;
static pthread_mutex_t mutex;
static int fd;
static uint8_t mode= 0;
static uint8_t bits = 8;
static uint32_t speed = 10000;
static uint16_t delay = 0;
static int fd_spi;
static int fd_gpio;
uint8_t led_value_table[] = {0xC0, 0xF9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};
typedef int (*key_callback)(int value);
int get_temperature_from_eeprom(int *min_temperature, int *max_temperature){
char buf[3];
int len;
int ret = 0;
pthread_mutex_lock(&mutex);
buf[0] = DATA_ADDR;
len = write(i2c_fd, buf, 1);
if (len < 0) {
printf("write data addr faile \n");
ret = -1;
goto out;
}
len = read(i2c_fd, buf, 3);
if (len < 0) {
printf("read data faile \n");
ret = -1;goto out;
}
*min_temperature = (int)buf[0];
*max_temperature = (int)buf[1];/* 如果在0x02地址没有读出0xAA表示EEPROM还没有写入最低/最高安全温度*/
if (buf[2] != 0xAA) {
ret = -2;
goto out;
}
out:
pthread_mutex_unlock(&mutex);
return ret;
}
int set_temperature_to_eeprom(int min_temperature, int max_temperature){
char buf[4];
int len;
int ret = 0;
pthread_mutex_lock(&mutex);
buf[0] = DATA_ADDR;
buf[1] = (char)min_temperature;
buf[2] = (char)max_temperature;
buf[3] = 0xAA;
len = write(i2c_fd, buf, 4);
if (len < 0) {
printf("write data faile \n");
ret = -1;
goto out;
}
usleep(1000);
out:
pthread_mutex_unlock(&mutex);
return ret;
}
int init_eeprom(void){
int ret;
int T1, T2;
i2c_fd = open(I2C_DEV_NAME, O_RDWR);/* 打开I2C1总线设备*/
if(i2c_fd < 0) {
printf("open %s failed\r\n", I2C_DEV_NAME);
return -1;
}
ret = ioctl(i2c_fd,I2C_SLAVE,I2C_ADDR>>1);
if(ret<0){
printf("setenv address faile ret:%x \n",ret );
return -1;
}
ret= get_temperature_from_eeprom(&T1,&T2);
if(ret == -1){
printf("read data from eeprom faile at %s \n",__FUNCTION__);
return -1;
}else if(ret == -2){
printf("init min & max temperature to eeprom \n");
ret = set_temperature_to_eeprom(25,40);
if(ret){
printf("write date to eeprom faile at %s \n", __FUNCTION__);
}
}
pthread_mutex_init(&mutex,NULL);
return 0;
}
int init_temperature_function(void){
if (access(ADC_DEV, F_OK)) {
/* 检查ADC设备文件是否存在*/
if (access(ADC_MODULE_PATH, F_OK) == 0) {
/* 检查ADC模块是否存在*/
system("insmod /root/lradc.ko");
while (access(ADC_DEV, F_OK)) {
/* 等待ADC设备文件生成完成*/
sleep(1);
}
} else {
printf("ADC module moust at /root/ \n");
return -1;
}
}
fd = open(ADC_DEV, 0);
if(fd < 0){
printf("open error by APP-%d\n",fd);
return -1;
}
return 0;
}
double get_temperature(void){
int adc_value;
double voltage;
double RT = 0;
double temp = 0;
int ret;
fd = open (LRADC_DEV,O_RDONLY);/* 打开ADC设备*/
if (fd < 0) {
perror("open");
close(fd);
return 0;
}
ret = ioctl(fd, IMX28_ADC_CH1, &adc_value); /* 读取通道1的ADC原始值*/
if (ret) {
printf("get adc value faile \n");
return -1;
}
voltage = (adc_value*1.85)/4096.0;/* 计算测量电压*/
RT = (V_ADC/voltage -1)*R33;/* 计算热敏电阻的阻值*/
temp = 3435/log(10*RT) -273.15;/* 计算热敏电阻的温度*/
return temp;
}
static void show_led(int fd_spi, int fd_gpio, int value, int num)
{
int ret;
uint8_t tx[] = {
led_value_table[value],
(1 << num),
};
struct spi_ioc_transfer tr_txrx[] = {
{
.tx_buf = (unsigned long)tx,
.rx_buf = 0,
.len = 2,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
},
};
ret = ioctl(fd_spi, SPI_IOC_MESSAGE(1), &tr_txrx[0]);
if (ret == 1) {
printf("can't revieve spi message");
return;
}
write(fd_gpio, "0", 1);
usleep(100);
write(fd_gpio, "1", 1);
}
int show_led_num(int VALUE, int NUM)
{
int ret = 0;
int fd_spi = 0;
int fd_gpio = 0;
int led_value = 0;
int led_num = 0;
pthread_mutex_lock(&numlock);//----------------------------------------------hu
led_value = VALUE;/* 获取程序输入参数的数码管的显示值*/
led_num = NUM;/* 获取程序输入参数的数字选择值*/
fd_spi = open(SPI_DEVICE, O_RDWR);/* 打开SPI总线的设备文件*/
if (fd_spi < 0) {
printf("can't open --led_num-- %s \n", SPI_DEVICE);
return -1;
}
fd_gpio = open(GPIO_DEVICE, O_RDWR);/* 打开GPIO设备的属性文件*/
if (fd_gpio < 0) {
printf("can't open %s device\n", GPIO_DEVICE);
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
printf("can't set wr spi mode\n");
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &bits);/* 设置SPI的数据位*/
if (ret == -1) {
printf("can't set bits per word\n");
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed);/* 设置SPI的最大总线频率*/
if (ret == -1){
printf("can't set max speed hz\n");
return -1;
}
show_led(fd_spi, fd_gpio, led_value, led_num);
close(fd_spi);
close(fd_gpio);
pthread_mutex_unlock(&numlock);
return 0;
}
static int show_digitron(int *value){
static int i = 0;
//int show_value_led = 0;
while ( 1 ) {
if (is_show == DIGITRON_ON) {/* 当使能显示时*/
/*把show_value数组中各元素的信息,显示在数码管相应的位段上*/
show_led_num(show_value[i], i);
} else {/* 当禁能显示时*/
show_led_num(20, i);/* 在数码管的所有位段不显示任何信息*/
}
i++;
if (i == 4) i = 0;
usleep(1000);
}
return 0;
}
int init_digitron(void){
int ret;
pthread_t thread_show;
fd_spi = open(SPI_DEVICE, O_RDWR);/* 打开SPI总线设备文件*/
if (fd_spi < 0) {
printf("can not open %s \n", SPI_DEVICE);
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_MODE, &mode);/* 设置SPI 总线的相位和极性*/
if (ret == -1) {
printf("can't set wr spi mode\n");
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &bits);/* 设置SPI总线每字长度*/
if (ret == -1) {
printf("can't set bits per word\n");
return -1;
}
ret = ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed);/* 设置SPI总线最高速率*/
if (ret == -1){
printf("can't set max speed hz\n");
return -1;
}
fd_gpio = open(GPIO_DEVICE, O_RDWR);/* 打开GPIO的设备属性文件*/
if (fd_gpio < 0) {
printf("can't open %s device\n", GPIO_DEVICE);
return -1;
}
/* 启动线程*/
ret = pthread_create(&thread_show, PTHREAD_CREATE_JOINABLE, (void *)show_digitron, NULL);
if (ret) {
printf("create thread faile \n");
return -1;
}
return 0;
}
void set_digitron_value(int value1, int value2, int value3, int value4){
show_value[0] = value1;
show_value[1] = value2 + 10;/* 在个位加上小数点*/
show_value[2] = value3;
show_value[3] = value4;
}
void set_digitron_on(int value){
is_show = value;
}
static key_callback key_callback_fun[5] = {NULL, NULL, NULL, NULL, NULL};
int install_key_function(key_callback p, int key)/* 注册回调函数*/{
if (p == NULL) {
return -1;
}
if (key_callback_fun[key]) {
/* 防止多次设置*/
return -1;
} else {
key_callback_fun[key] = p;
}
return 0;
}
enum{
KEY1,
KEY2,
KEY3,
KEY4,
KEY5
};
static int key_pthread(void){
int count;
struct input_event input_event_value;
while(1) {
count=read(fd, &input_event_value, sizeof(struct input_event));/* 监视按键输入事件*/
if (count < 0) {printf("read iput device event error \n");
return -1;
}
/* 判断是哪个键被按下/提起*/
switch (input_event_value.code) {
case KEY_A:/* KEY1按键*/
if (key_callback_fun[KEY1]) {
key_callback_fun[KEY1](input_event_value.value);
}
break;
case KEY_B:/* KEY2按键*/
if (key_callback_fun[KEY2]) {
key_callback_fun[KEY2](input_event_value.value);
}
break;
case KEY_C:/* KEY3按键*/
if (key_callback_fun[KEY3]) {
key_callback_fun[KEY3](input_event_value.value);
}
break;
case KEY_D:/* KEY4按键*/
if (key_callback_fun[KEY4]) {
key_callback_fun[KEY4](input_event_value.value);
}
break;
case KEY_E:/* KEY5按键*/
if (key_callback_fun[KEY5]) {
key_callback_fun[KEY5](input_event_value.value);
}
break;
default:
break;
}
}
return 0;
}
int init_event_key(void){
int ret;
pthread_t thread_key;
if (access(DEV_NAME, F_OK)) { /* 检查按键设备文件是否存在*/
if (access(KEY_MODULE_PATH, F_OK) == 0) {/* 检查按键模块是否存在*/
system("insmod /root/imx_key.ko");
while (access(DEV_NAME, F_OK)) {/* 等待按键设备文件生成*/
sleep(1);
}
} else {
printf("key module moust at /root \n");
return -1;
}
}
fd = open (DEV_NAME, O_RDWR);
if (fd < 0) {
perror(DEV_NAME);
exit(0);
}/* start key conctol pthread */
ret = pthread_create(&thread_key, PTHREAD_CREATE_JOINABLE, (void *)key_pthread, NULL);
if (ret) {
printf("create thread faile at %s \n", __FUNCTION__);
return -1;
}
return 0;
}
static enum {
SHOW_L,
SHOW_H, /* 显示最高安全温度状态 */
SHOW_TEMPER,
} sys_status;
static int key2_action(int value){/* 只响应键按下事件,忽略键提起事件*/
if (value == 0) {
return 0;
}
sys_status = SHOW_L;/* 设置为显示最低安全温度状态*/
return 0;
}
static int key3_action(int value){/* 只响应键按下事件,忽略键提起事件*/
if (value == 0) {
return 0;
}
sys_status = SHOW_TEMPER;/* 设置为显示当前环境温度状态*/
return 0;
}
static int key4_action(int value){/* 只响应键按下事件,忽略键提起事件*/
if (value == 0) {
return 0;
}
sys_status = SHOW_H;/* 设置为显示最高安全温度状态*/
return 0;
}
static int key1_action(int value){
int min_temperature = 0, max_temperature = 0;
int ret;/* 只响应键按下事件,忽略键提起事件*/
if (value == 0) {
return 0;
}
/* 在EEPROM读出最高/最低安全温度值*/
ret = get_temperature_from_eeprom(&min_temperature, &max_temperature);
if (ret) {
printf("get temperature from eeprom faile at %s \n", __FUNCTION__);
return -1;
}
switch (sys_status) {
case SHOW_L:/* 若当处于显示最低安全温度状*/
if (min_temperature > 0) {/* 最低安全温度不能小于0*/
min_temperature--;
}
break;
case SHOW_H:/* 当处于显示最高安全温度状态*//* 最高安全温度不能低于最低安全温度*/
if (max_temperature > min_temperature) {
max_temperature--;
}
break;
case SHOW_TEMPER:
break;
}
/* 在EEPROM写入最低/最安全高温度值*/
ret = set_temperature_to_eeprom(min_temperature, max_temperature);
if (ret) {
printf("set temperature to eeprom faile at %s \n", __FUNCTION__);
return -1;
}
return 0;
}
static int key5_action(int value){
int min_temperature = 0, max_temperature = 0;
int ret;/* 只响应键按下事件,忽略键提起事件*/
if (value == 0) {
return 0;
}
/* 在EEPROM读出最高/最低安全温度值*/
ret = get_temperature_from_eeprom(&min_temperature, &max_temperature);
if (ret) {
printf("get temperature from eeprom faile at %s \n", __FUNCTION__);
return -1;
}
switch (sys_status) {
case SHOW_L:/* 若当处于显示最低安全温度状*//* 最低安全温度不能高于最高安全温度*/
if (min_temperature < max_temperature){
min_temperature++;
}
break;
case SHOW_H:/* 当处于显示最高安全温度状态*/
if (max_temperature < 85) {/* 最高安全温度不能高于85度*/
max_temperature++;
}
break;
case SHOW_TEMPER:
break;
}
/* 在EEPROM写入最低/最高安全温度值*/
ret = set_temperature_to_eeprom(min_temperature, max_temperature);
if (ret) {
printf("set temperature to eeprom faile at %s \n", __FUNCTION__);
return -1;
}
return 0;
}
void set_digitron_vlue(int value1,int value2,int value3,int value4)
{
show_value[0]=value1;
show_value[1]=value2;
show_value[2]=value3;
show_value[3]=value4;
}
int beep_on(void)
{
//int ret;
//ret = write(BEEP_DEV, 1,1);
//if(ret < 0) {
//perror("beep on");
//}
return 0;
}
int beep_off(void)
{
//int ret;
//ret = write(BEEP_DEV, 0,1);
//if(ret < 0) {
//perror("beep off");
//}
return 0;
}
static int thread_control(int *value){
int min_temperature = 0, max_temperature = 0, temperature;
float temp_t;
int ret = -1;
int value1, value2, value3,value4;
while (1) {
/* 在EEPROM读取最低/最高安全温度值*/
ret = get_temperature_from_eeprom(&min_temperature,&max_temperature);
if (ret) {
printf("read temperature faile \n");
break;
}
temp_t = get_temperature();/* 获取当前环境温度*/
temperature = (int )(temp_t * 100.0);/* 获取当前环境温度并转换为整数*/
/* 显示控制*/
switch (sys_status) {
/* 当处于显示最低安全温度状态时,数码管显示类似:26.0L */
case SHOW_L:
value1 = min_temperature / 10;/* 数码管1位段显示最低安全温度的十位*/
value2 = min_temperature % 10;/* 数码管2位段显示最低安全温度的个位*/
value3 = 0;/* 数码管3位段显示0*/
value4 = 22;/* 数码管4位段显示"L"*/
set_digitron_vlue(value1, value2, value3, value4);
break;
/* 当处于显示最高安全温度状态时,数码管显示类似:40.0H */
case SHOW_H:
value1 = max_temperature / 10;/* 数码管1位段显示最高安全温度的十位*/
value2 = max_temperature % 10;/* 数码管2位段显示最高安全温度的个位*/
value3 = 0;/* 数码管3位段显示显示0*/
value4 = 21;/* 数码管4位段显示"H"*/
set_digitron_vlue(value1, value2, value3, value4);
break;
/* 显示当前环境温度:两位整数,两位小数*/
case SHOW_TEMPER:/* 注意temperature已经把当前环境温度值乘以100,转换成整数值*/
value1 = temperature/1000;/* 数码管的1位段显示当前环境温度的十位*/
value1 = value1%10;
value2 = temperature/100;/* 数码管的2位段显示当前环境温度的个位*/
value2 = value2%10;
value3 = temperature/10;/*数码管的3位段显示当前环境温度的十分位*/
value3 = value3%10;
value4 = temperature%10;/*数码管的4位段显示当前环境温度的百分位*/
set_digitron_vlue(value1, value2, value3, value4);
break;
default:
break;
}
/* 监控当前环境温度*/
if (temp_t < min_temperature) { /* 温度过低*/
/* 蜂鸣器短鸣一声,数码管显示的数字闪烁*/
beep_on();
set_digitron_on(DIGITRON_OFF);
usleep(50*1000);
set_digitron_on(DIGITRON_ON);
beep_off();
} else if (temp_t > max_temperature) {/* 温度过高*/
/* 蜂鸣器短鸣两声,数码管显示的数字闪烁*/
beep_on();
set_digitron_on(DIGITRON_OFF);
usleep(50*1000);
set_digitron_on(DIGITRON_ON);
beep_off();
usleep(20*1000);
beep_on();
usleep(50*1000);
beep_off();
}
usleep(300*1000);
}
return NULL;
}
int init_control(void){
int ret;
pthread_t thread_control_t;
sys_status = SHOW_TEMPER;/*初始化为显示当前环境温度状态*/
beep_fd = open(BEEP_DEV, O_RDWR);/* 打开蜂鸣器设备属性文件*/
if(beep_fd < 0) {
perror("open beep device");
}
ret = install_key_function(key1_action, KEY1);/* 在KEY1按键安装响应函数*/
if (ret) {
printf("install call fun faile in %s \n", __FUNCTION__);
return -1;
}
ret = install_key_function(key2_action, KEY2);/* 在KEY2按键安装响应函数*/
if (ret) {
printf("install call fun faile in %s \n", __FUNCTION__);
return -1;
}
ret = install_key_function(key3_action, KEY3);/* 在KEY3按键安装响应函数*/
if (ret) {
printf("install call fun faile in %s \n", __FUNCTION__);
return -1;
}
ret = install_key_function(key4_action, KEY4);/* 在KEY4按键安装响应函数*/
if (ret) {
printf("install call fun faile in %s \n", __FUNCTION__);
return -1;
}
ret = install_key_function(key5_action, KEY5);/* 在KEY5按键安装响应函数*/
if (ret) {printf("install call fun faile in %s \n", __FUNCTION__);
return -1;
}
/* 生成温度监控线程*/
ret = pthread_create(&thread_control_t, PTHREAD_CREATE_JOINABLE, (void *)thread_control, NULL);
if (ret) {
printf("create thread faile in %s \n", __FUNCTION__);
return -1;
}
return 0;
}
int main(int argc, char *argv[]){
int ret;
//int fd;
//double value;
ret = init_digitron();/* 初始化数码管模块*/
if (ret)
return -1;
ret = init_eeprom();/* 初始化eeprom模块*/
if (ret)
return -1;
ret = init_temperature_function();/* 初始化温度读取模块*/
if (ret)
return -1;
ret = init_event_key();/* 初始化按键模块*/
if (ret)
return -1;
ret = init_control();/* 初始化监控模块*/
if (ret)
return -1;
while ( 1 ) {
sleep(1);
}
return ret;
}
显示效果图:
![这里写图片描述](https://img-blog.csdn.net/20180727093803200?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpYm94aXU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)