音控灯项目——软件编程

 

                                      音控灯项目软件编程

  • 软件流程图

  • 软件编程
#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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言: 利用单片机演奏音乐是单片机爱好者的兴趣之一,应用的范围也比较广泛。所谓音乐播放器,由单片机进行信息处理,再经过信号放大,由蜂鸣器发出乐曲声。我们知道,振动产生声音,振动频率不同所发出的声音也就不同。有规律的振动发出的声音叫“乐音”。音乐由音频和节拍构成,音频即发声的频率;节拍即延时的长短。因此利用单片机的定时器,产生一定频率的方波,即可以产生一定频率的声音。再利用单片机软件延时的方法来产生不同的节拍。把音频和节拍结合起来,进行合理的排列,即可播放出比较悦耳的音乐。本文比较详细地介绍了音乐播放器的工作原理、设计思路、硬件的选择及相关作用、软件的实现方法以及详细的程序清单。 控制单片机播放音乐的方式有很多,多数使用者利用单片机存储音乐与控制播放。本设计利用STC89C51单片机及少数外围电路控制音乐播放,产生两首不同的歌曲。 对于单片机产生音乐,关键是控制频率的输出。本设计采用了定时器T0中断的方法产生不同频率的脉冲,从而产生不同频率的声音。此外,使用红外一体化头VS1838B接收红外信号,通过外部中断INT0来进行对红外信号的解码,由单片机内部程序对不同的键值完成不同的操作。并且使用常用的无源蜂鸣器进行发声,实现了一个简单的遥控音乐播放器的功能。 红外遥控音乐主要功能: 本次设计所要实现为以STC89C51为核心的红外遥控音乐LED具组的功能。首先,我们选择耐压值为12V的白色发光二极管作为本次设计的具组成器件。其次,选用ULN2003及扬声器的语音模块通过对所选的音乐进行编码输入到单片机后再解码到语音模块来实现我们的台音乐播放功能 红外遥控音乐整体电路设计: 根据设计要求,红外遥控音乐可以分为三个模块进行设计: 1.单片机最小系统电路:单片机最小系统,或者称为最小应用系统,是指用最少的元件组成的单片机可以工作的最小的系统单元。 对51系列单片机来说,最小系统一般应该包括:单片机、时钟电路、复位电路。本设计中使用的单片机为STC89C51,晶振电路使用12MHZ的晶振。复位电路:由电容串联电阻构成,当系统一上电,RST脚将会出现高电平,当RST脚的高电平持续两个机器周期以上就将复位。 2.红外解码电路:本电路主要用于对红外遥控器发出的红外信号进行解码,然后程序根据解码后的不同键值相应完成不同的功能。 3.音乐播放模块:本模块采用ULN2003及扬声器组成语音系统对经单片机解码后所得的音乐代码的数字量放大再转换为模拟量实现音乐的播放。 4.LED具组:我们选用耐压值为12V的发光二极管总共为12个。每4个led分为一组,分别与单片机A8、A9、A10管脚相连,从而实现台的三档亮度调节。 红外遥控音乐原理框图: 红外遥控音乐原理图截图: 红外遥控音乐红外遥控器:
### 回答1: protuse八路彩控制器是一种用于控制彩的设备。它具有八个独立控制通道,可以分别对八个彩进行控制。这使得用户可以根据需要独立地调整每个彩的亮度、颜色和光效果。 该控制器采用先进的技术和设计,使用户能够轻松地操作和控制彩。它具有简单易用的界面,让用户可以直观地设置彩的参数。此外,它还支持多种控制方式,如遥控器、APP远程控制等,增加了用户的使用便捷性。 protuse八路彩控制器也具有更多的功能。它支持多种光模式,如呼吸、闪烁、渐变等,用户可以根据需要选择合适的模式。此外,它还具有定时任务功能,用户可以设置彩在特定时间自动开启或关闭,以适应不同的场景需求。 除了以上功能,protuse八路彩控制器还具有互动性强的特点。它兼容声控、音乐控制等功能,用户可以将彩音乐或声音相结合,创造出更加炫彩的光效果。 总的来说,protuse八路彩控制器是一种功能全面、易操作的设备,通过它可以实现对彩的全面控制。它广泛应用于家庭、商业场所、娱乐场所等,可以为用户带来更加丰富多彩的光体验。 ### 回答2: protuse八路彩控制器是一种功能强大的彩控制设备。它具有八个独立的通道,可以分别控制八个彩的亮度、颜色、动画效果等参数。 这款彩控制器采用先进的技术,可以实现多种控制方式。使用者可以通过面板上的按钮直接调节每个通道的参数,也可以通过连接电脑或手机上的软件进行远程控制。更为方便的是,它还支持DMX控制协议,可以与其他DMX设备实现联动控制。 protuse八路彩控制器除了具备强大的控制功能外,还具有高度的稳定性和可靠性。它采用优质的电子元件和精密的电路设计,确保其在长时间使用中不易出现故障。同时,它还具备过压、过流、过热等保护功能,能够有效保护彩和设备的安全。 此外,protuse八路彩控制器还具备节能环保的特点。它采用先进的功率控制技术,能够在不降低彩亮度的情况下减少能耗,从而降低对环境的负荷。 总之,protuse八路彩控制器是一款功能强大、操作灵活、稳定可靠的设备。它广泛应用于舞台演出、娱乐场所、商业展览等场合,为用户带来视觉盛宴的同时,也提升了彩控制的效率和便利性。 ### 回答3: Protuse八路彩控制器是一种功能强大、具有灵活性的光控制设备。该控制器适用于各种场所、场景和活动,可以控制多达八路的彩,以实现多种效果和光变化。它具有用户友好的界面和易于操作的功能,方便用户轻松地控制彩的色彩、亮度、速度和模式。 Protuse八路彩控制器的主要特点之一是它的灵活性。它可以与多种类型的彩兼容,包括RGB彩、色温调节和多彩效果。用户可以根据需要选择不同类型的彩,以实现不同的光效果。 此外,Protuse八路彩控制器还具有多种控制模式和预设场景,用户可以通过设定不同模式和场景,轻松地切换不同的光效果。例如,用户可以选择自动模式,让控制器按照预设的场景逐渐变化光,也可以选择音乐模式,在音乐的节奏下实现彩的跳动和闪烁。 此外,Protuse八路彩控制器还具有定时开关、遥控器和手机APP控制等功能,可以方便地进行远程控制和管理。用户可以根据自己的需求和喜好,在任何时间和任何地点对八路彩进行控制。 总体而言,Protuse八路彩控制器是一款功能强大、灵活性高、易于操作的设备。它提供了多种光效果和控制方式,为用户带来了更加丰富多样的光体验。无论是家庭装饰、商业场所还是娱乐场所,Protuse八路彩控制器都能够满足用户的需求,并为其带来独特的光氛围。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值