音控灯项目软件编程
- 软件流程图
- 软件编程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int fifoSize;
#define HTSETRXFIFO 0xAA01
#define RECV_FIFOSIZE 4 //串口接收缓冲区大小,默认4字节,可设置为1,4,8,14字节
#define SET_RECV_FIFOSIZE(com_fd) fifoSize = RECV_FIFOSIZE; ioctl((com_fd),HTSETRXFIFO,fifoSize) //设置串口接收缓冲区大小
#define VOICE_COM "/dev/ttyS1" //噪声传感器节点
#define DISPLAY_RELAY_COM "/dev/ttyS2" //显示、继电器节点
#define VOICE_STANDARD 50 //标准分贝值,均值大于此值继电器开路
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
//分贝模块数据
//站号 功能码 噪声寄存器地址 读取寄存器长度 CRC
uint8_t VoiceSendData[8] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A};
uint8_t VoiceRecvData[7];
//显示模块数据
//站号 功能码 数码管显示寄存器 要显示的数据 CRC
//char DisplayData[8] = {0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
char* DisplayData = NULL;
//继电器模块数据
//站号 功能码 继电器寄存器 闭合 CRC
uint8_t RelayCloseData[8] = {0x02, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x8C, 0x09};
//站号 功能码 继电器寄存器 开路 CRC
uint8_t RelayOpenData[8] = {0x02, 0x05, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xF9};
uint16_t VoiceBuf[100];
uint8_t RelayState = 1; //继电器状态,初始状态为1闭合
//串口结构
typedef struct{
char prompt; //prompt after reciving data
int baudrate; //baudrate
char databit; //data bits, 5, 6, 7, 8
char debug; //debug mode, 0: none, 1: debug
char echo; //echo mode, 0: none, 1: echo
char fctl; //flow control, 0: none, 1: hardware, 2: software
char parity; //parity 0: none, 1: odd, 2: even
char stopbit; //stop bits, 1, 2
const int reserved; //reserved, must be zero
}portinfo_t;
typedef portinfo_t *pportinfo_t;
/*******************************************
* 波特率转换函数(请确认是否正确)
********************************************/
int convbaud(unsigned long int baudrate)
{
switch(baudrate){
case 2400:
return B2400;
case 4800:
return B4800;
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
default:
return B9600;
}
}
/*******************************************
* Setup comm attr
* fdcom: 串口文件描述符,pportinfo: 待设置的端口信息(请确认)
*
********************************************/
int PortSet(int fdcom, const pportinfo_t pportinfo)
{
struct termios termios_old, termios_new;
int baudrate, tmp;
char databit, stopbit, parity, fctl;
bzero(&termios_old, sizeof(termios_old));
bzero(&termios_new, sizeof(termios_new));
cfmakeraw(&termios_new);
tcgetattr(fdcom, &termios_old); //get the serial port attributions
/*------------设置端口属性----------------*/
//baudrates
baudrate = convbaud(pportinfo -> baudrate);
cfsetispeed(&termios_new, baudrate); //填入串口输入端的波特率
cfsetospeed(&termios_new, baudrate); //填入串口输出端的波特率
termios_new.c_cflag |= CLOCAL; //控制模式,保证程序不会成为端口的占有者
termios_new.c_cflag |= CREAD; //控制模式,使能端口读取输入的数据
// 控制模式,flow control
fctl = pportinfo-> fctl;
switch(fctl){
case '0':{
termios_new.c_cflag &= ~CRTSCTS; //no flow control
}break;
case '1':{
termios_new.c_cflag |= CRTSCTS; //hardware flow control
}break;
case '2':{
termios_new.c_iflag |= IXON | IXOFF |IXANY; //software flow control
}break;
}
//控制模式,data bits
termios_new.c_cflag &= ~CSIZE; //控制模式,屏蔽字符大小位
databit = pportinfo -> databit;
switch(databit){
case '5':
termios_new.c_cflag |= CS5;
case '6':
termios_new.c_cflag |= CS6;
case '7':
termios_new.c_cflag |= CS7;
default:
termios_new.c_cflag |= CS8;
}
//控制模式 parity check
parity = pportinfo -> parity;
switch(parity){
case '0':{
termios_new.c_cflag &= ~PARENB; //no parity check
termios_new.c_iflag &= ~INPCK;
}break;
case '1':{
termios_new.c_cflag |= (PARODD | PARENB); //odd check
termios_new.c_iflag |= INPCK;
}break;
case '2':{
termios_new.c_cflag |= PARENB; //even check
termios_new.c_cflag &= ~PARODD;
termios_new.c_iflag |= INPCK;
}break;
}
//控制模式,stop bits
stopbit = pportinfo -> stopbit;
if(stopbit == '2'){
termios_new.c_cflag |= CSTOPB; //2 stop bits
}
else{
termios_new.c_cflag &= ~CSTOPB; //1 stop bits
}
//other attributions default
termios_new.c_oflag &= ~OPOST; //输出模式,原始数据输出
termios_new.c_cc[VMIN] = 1; //控制字符, 所要读取字符的最小数量
termios_new.c_cc[VTIME] = 1; //控制字符, 读取第一个字符的等待时间 unit: (1/10)second
tcflush(fdcom, TCIFLUSH); //溢出的数据可以接收,但不读
tmp = tcsetattr(fdcom, TCSANOW, &termios_new); //设置新属性,TCSANOW:所有改变立即生效 tcgetattr(fdcom, &termios_old);
return(tmp);
}
/********************************************
* send data
* fdcom: 串口描述符,data: 待发送数据,datalen: 数据长度
* 返回实际发送长度
*********************************************/
int PortSend(int fdcom, char *data, int datalen)
{
int len = 0;
len = write(fdcom, data, datalen); //实际写入的长度
if(len == datalen){
return (len);
}
else{
tcflush(fdcom, TCOFLUSH);
return -1;
}
}
/*******************************************
* receive data
* 返回实际读入的字节数
*
********************************************/
int PortRecv(int fdcom, char *data, int datalen)
{
int readlen, fs_sel;
fd_set fs_read;
struct timeval tv_timeout;
FD_ZERO(&fs_read);
FD_SET(fdcom, &fs_read);
tv_timeout.tv_sec = 0;
tv_timeout.tv_usec = 20000;
fs_sel = select(fdcom+1, &fs_read, NULL, NULL, &tv_timeout);
if(fs_sel){
readlen = read(fdcom, data, datalen);
return(readlen);
}
else{
return(-1);
}
return (readlen);
}
int main()
{
int voice_fd, display_relay_fd;
portinfo_t portinfo ={
'0', // print prompt after receiving
9600, // baudrate: 9600
'8', // databit: 8
'0', // debug: off
'0', // echo: off
'0', // flow control: none
'0', // parity: none
'1', // stopbit: 1
0 // reserved
};
voice_fd = open(VOICE_COM, O_RDWR | O_NOCTTY | O_NONBLOCK);
if( voice_fd < 0 ){
printf("Open voice_com failed,exit!\n");
exit(1);
}else{
printf("Open voice_com success.\n");
}
display_relay_fd = open(DISPLAY_RELAY_COM, O_RDWR | O_NOCTTY | O_NONBLOCK);
if( display_relay_fd < 0 ){
printf("Open display_relay_com failed,exit!\n");
exit(1);
}else{
printf("Open display_relay_com success.\n");
}
SET_RECV_FIFOSIZE(voice_fd); //设置接收缓冲区大小,如不设置,默认4字节
SET_RECV_FIFOSIZE(display_relay_fd); //设置接收缓冲区大小,如不设置,默认4字节
PortSet(voice_fd, &portinfo);
PortSet(display_relay_fd, &portinfo);
while(1){
usleep(50*1000);
int send_num = PortSend(voice_fd,VoiceSendData,8);
usleep(50*1000);
printf("send_num = %d\n", send_num);
if( 8 != send_num ){
printf("send voice data error, continue!\n");
continue;
}
int recv_num = 0;
while(1){
usleep(50*1000);
recv_num = PortRecv(voice_fd,VoiceRecvData,7);
usleep(50*1000);
printf("recv_num = %d, break!\n", recv_num);
break;
}
if( 7 == recv_num ){
int current_voice = (VoiceRecvData[3]<<8) | VoiceRecvData[4]; //获取到的分贝值是实际值的10倍
printf("current_voice is %d\n", current_voice);
float current_voice_float = current_voice*1.0f/10;
char temp[5];
sprintf(temp, "%3.1f", current_voice_float);
printf("current_voice_float = %3.1f\n", current_voice_float);
DisplayData = (char*)malloc(strlen("$001,") + 4 + strlen("dB#")); //分贝大小最多4位:123.4,小数点不计入位数
strcat(DisplayData, ("$001,"));
strcat(DisplayData, temp);
strcat(DisplayData, ("dB#"));
printf("display string is %s\n", DisplayData);
usleep(50*1000);
send_num = PortSend(display_relay_fd,DisplayData,12); //发送分贝值到显示设备
usleep(50*1000);
free(DisplayData);
printf("send_num = %d\n", send_num);
if( 12 != send_num ){
printf("发送数据到显示设备失败!\n");
continue;
}
int i = 0;
for( i = 0; i < 99; i++ ){
VoiceBuf[i] = VoiceBuf[i+1];
}
VoiceBuf[99] = current_voice;
uint16_t voice_sum = 0;
for( i = 0; i < 100; i++ ){
voice_sum += VoiceBuf[i];
}
uint8_t voice_average = (voice_sum/10)/100; //先得到实际分贝总值,然后再计算均值
printf("voice_sum = %d, voice_average is %d\n", voice_sum, voice_average);
//刚开始这段代码肯定不会执行,只有到100s后分贝均值大于阈值才触发
//刚开始肯定分贝均值肯定是小于阈值的
if( voice_average > VOICE_STANDARD ){
printf("voice_average > VOICE_STANDARD\n");
if( 1 == RelayState ){ //当前分贝均值超过阈值,并且此时的继电器是闭合的
usleep(50*1000);
send_num = PortSend(display_relay_fd, RelayOpenData, 8); //发送开路数据到继电器
usleep(50*1000);
printf("send_num = %d\n", send_num);
if( 8 != send_num ){
printf("发送寄存器开路数据失败!\n");
exit(1);
}else{
RelayState = 0; //寄存器状态开路
printf("change the relay state %d\n", RelayState);
}
}
}else{
//刚开始的继电器状态肯定是闭合的
if( 0 == RelayState ){ //当前分贝均值未超过阈值,并且此时的继电器是断开的
usleep(50*1000);
send_num = PortSend(display_relay_fd, RelayCloseData, 8); //发送闭合数据到继电器
usleep(50*1000);
if( 8 != send_num ){
printf("发送寄存器闭合数据失败!\n");
exit(1);
}else{
RelayState = 1; //寄存器状态开路
}
}
}
}else{
continue;
}
sleep(1); //1s等待
}
close(voice_fd);
close(display_relay_fd);
return 0;
}