-
一、项目介绍当前文章介绍基于51单片机和SHT30传感器设计的环境温度与湿度检测设备。设备采用IIC模拟时序通信协议,能够实时监测环境的温度和湿度,并将数据通过LCD显示屏显示出来;可以广泛应用于室内环境监测、气象观测、农业温室监测等领域。在本项目中,使用了51单片机作为主控芯片,SHT30传感器作为温湿度传感器,LCD显示屏作为数据显示模块。通过51单片机的GPIO口模拟IIC通信协议,实现了与SHT30传感器的数据通信。二、硬件设计2.1 硬件构成本次设计所需的硬件主要包括以下部分:STC89C52单片机SHT30温湿度传感器串口通信模块LCD1602显示屏电源模块杜邦线等连接线2.2 硬件接口及信号本次设计使用51单片机通过IIC总线与SHT30传感器进行通信,同时使用串口与上位机进行数据传输,并使用液晶显示屏显示当前温湿度值。具体接口和信号定义如下:(1) 51单片机与SHT30传感器之间的IIC接口:端口功能说明P2.0SDA数据线P2.1SCL时钟线P2.2RESET复位线(2) 51单片机与串口通信模块之间的接口:端口功能说明P3.0TXD发送线P3.1RXD接收线P3.2GND地线(3) 51单片机与液晶屏之间的接口:端口功能说明P1.0-P1.7DB0-DB7数据线P0.0RS指令/数据选择线P0.1RW读/写选择线P0.2E使能线P0.3CS片选线VCC电源正极5VGND电源地地三、软件设计3.1 SHT30传感器代码下面代码读取SHT30传感器的值并通过串口打印。 #include <REG52.h> #include <stdio.h> #define uchar unsigned char #define uint unsigned int sbit SDA=P2^0; sbit SCL=P2^1; void delay(int n) { int i; while(n--) { for(i=0; i<120; i++); } } void start() { SDA = 1; _nop_(); SCL = 1; _nop_(); SDA = 0; _nop_(); SCL = 0; _nop_(); } void stop() { SDA = 0; _nop_(); SCL = 1; _nop_(); SDA = 1; _nop_(); } void ack() { SDA = 0; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); SDA = 1; _nop_(); } void nack() { SDA = 1; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); } void write_byte(uchar dat) { uchar i; for(i=0; i<8; i++) { SDA = dat & 0x80; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); dat <<= 1; } ack(); } uchar read_byte() { uchar i, dat; for(i=0; i<8; i++) { dat <<= 1; SCL = 1; _nop_(); dat |= SDA; SCL = 0; _nop_(); } return dat; } void init_sht30() { start(); write_byte(0x80); if(read_byte() != 0x5A) { stop(); return; } write_byte(0xBE); if(read_byte() != 0x08 || read_byte() != 0x00) { stop(); return; } stop(); } float measure_temp(void) { uchar temp_h, temp_l, crc; float temp; start(); write_byte(0x80); // 主机发送写地址 write_byte(0x2C); // 选择开始温度测量命令 write_byte(0x06); stop(); delay(15); // 延时等待温度测量完成 start(); write_byte(0x81); // 主机发送读地址 temp_h=read_byte(); ack(); temp_l=read_byte(); ack(); crc=read_byte(); stop(); temp = ((temp_h<<8)+temp_l)*175.0/0xffff - 45.0; // 温度值转换公式 return temp; } float measure_humi(void) { uchar humi_h, humi_l, crc; float humi; start(); write_byte(0x80); // 主机发送写地址 write_byte(0x2C); // 选择开始湿度测量命令 write_byte(0x06); stop(); delay(15); // 延时等待湿度测量完成 start(); write_byte(0x81); // 主机发送读地址 humi_h=read_byte(); ack(); humi_l=read_byte(); ack(); crc=read_byte(); stop(); humi = ((humi_h<<8)+humi_l)*100.0/0xffff; // 湿度值转换公式 return humi; } void main() { float temp, humi; init_sht30(); // SHT30 初始化 TMOD=0x20; // 定时器0工作方式2,8位定时器,用于波特率设置 TH1=0xfd; // 波特率9600 TL1=0xfd; TR1=1; // 启动定时器0 SCON=0x50; // 设置串口工作方式1,允许接收,允许接收中断 ES=1; // 允许串口中断 while(1) { temp = measure_temp(); humi = measure_humi(); printf("Temperature: %.1fC, Humidity: %.1f%\n", temp, humi); delay(500); // 间隔时间500ms } } void ser() interrupt 4 using 2 { if(RI) // 接收到数据 { RI=0; // 清除标志位 } if(TI) // 发送完毕 { TI=0; // 清除标志位 } }在上面的代码中,定义了两个函数 measure_temp 和 measure_humi,分别用于测量温度和湿度值,并返回结果。在主函数中,利用这两个函数得到当前的温湿度值,然后通过串口打印出来。3.2 LCD1602显示屏代码下面代码是LCD1602驱动代码,完成数字字符显示。 #include <REG52.h> #define LCD1602_DB P0 sbit RS = P2^5; sbit RW = P2^6; sbit E = P2^7; void delay(int n) { int i; while(n--) { for(i=0; i<120; i++); } } void main() { //LCD 初始化 delay(1000); LCD1602_DB = 0x38; E = 1; delay(5); E = 0; delay(500); LCD1602_DB = 0x08; E = 1; delay(5); E = 0; delay(500); LCD1602_DB = 0x01; E = 1; delay(5); E = 0; delay(500); LCD1602_DB = 0x06; E = 1; delay(5); E = 0; delay(500); LCD1602_DB = 0x0C; E = 1; delay(5); E = 0; while(1) { //向LCD中写入数字12345 RS = 0; //选择指令寄存器 LCD1602_DB = 0x80; //设置地址为第一行的第一个字符位置(0x80 + 0x00) E = 1; delay(5); E = 0; RS = 1; //选择数据寄存器 LCD1602_DB = 0x31; //写入数字1 E = 1; delay(5); E = 0; LCD1602_DB = 0x32; //写入数字2 E = 1; delay(5); E = 0; LCD1602_DB = 0x33; //写入数字3 E = 1; delay(5); E = 0; LCD1602_DB = 0x34; //写入数字4 E = 1; delay(5); E = 0; LCD1602_DB = 0x35; //写入数字5 E = 1; delay(5); E = 0; delay(500); //间隔时间为500ms } }在上面的代码中,定义了函数 delay 用于延时等待,并且实现了LCD1602的初始化和写入操作。在主函数中,执行LCD1602的初始化操作,然后循环不断向LCD中写入数字12345,并且间隔时间为500ms。3.3 完整代码 #include<reg52.h> #include<intrins.h> #define uchar unsigned char #define uint unsigned int sbit SDA = P2^0; //定义SDA引脚 sbit SCL = P2^1; //定义SCL引脚 sbit CS = P0^3; //定义液晶屏片选引脚 sbit RW = P0^1; //定义液晶屏读/写引脚 sbit RS = P0^0; //定义液晶屏指令/数据引脚 sbit E = P0^2; //定义液晶屏使能引脚 void delay(int n) //延时函数,n为延时时间 { int i; while(n--) { for(i=0; i<120; i++); } } void start() //开始信号 { SDA = 1; //数据线高电平 _nop_(); SCL = 1; //时钟线高电平 _nop_(); SDA = 0; //数据线低电平 _nop_(); SCL = 0; //时钟线低电平 _nop_(); } void stop() //结束信号 { SDA = 0; //数据线低电平 _nop_(); SCL = 1; //时钟线高电平 _nop_(); SDA = 1; //数据线高电平 _nop_(); } void ack() //应答信号 { SDA = 0; //数据线低电平 _nop_(); SCL = 1; //时钟线高电平 _nop_(); SCL = 0; //时钟线低电平 _nop_(); SDA = 1; //数据线高电平 _nop_(); } void nack() //非应答信号 { SDA = 1; //数据线高电平 _nop_(); SCL = 1; //时钟线高电平 _nop_(); SCL = 0; //时钟线低电平 _nop_(); } void write_byte(uchar dat) //写一个字节 { uchar i; for(i=0; i<8; i++) { SDA = dat & 0x80; _nop_(); SCL = 1; _nop_(); SCL = 0; _nop_(); dat <<= 1; } ack(); } uchar read_byte() //读一个字节 { uchar i, dat; for(i=0; i<8; i++) { dat <<= 1; SCL = 1; _nop_(); dat |= SDA; SCL = 0; _nop_(); } return dat; } void init_sht30() //SHT30初始化 { start(); write_byte(0x80); if(read_byte() != 0x5A) { stop(); return; } write_byte(0xBE); if(read_byte() != 0x08 || read_byte() != 0x00) { stop(); return; } stop(); } void measure() //测量温湿度值 { float humi, temp; uint i; start(); write_byte(0x80); read_byte(); read_byte(); read_byte(); write_byte(0x2C); write_byte(0x06); for(i=0; i<40000; i++); //等待测量结果 start(); write_byte(0x80); read_byte(); read_byte(); read_byte(); humi = read_byte() * 256; humi += read_byte(); temp = read_byte() * 256; temp += read_byte(); stop(); temp = -45 + (175*temp)/65535; //转化温度 humi = 100 * humi / 65535; //转化湿度 //将温湿度值通过串口发送 printf("Temperature: %.1fC\n", temp); printf("Humidity: %.1f%%RH\n", humi); } void init_lcd() //液晶屏初始化 { RW = 0; RS = 0; E = 0; delay(15); write_byte(0x30); delay(15); write_byte(0x30); delay(5); write_byte(0x30); delay(5); write_byte(0x38); write_byte(0x08); write_byte(0x01); write_byte(0x06); write_byte(0x0c); } void display(float temp, float humi) //显示温湿度值 { uchar i; uchar temp_str[5]; uchar humi_str[5]; //转化为字符串 sprintf(temp_str, "%.1f", temp); sprintf(humi_str, "%.1f", humi); //显示温度 RS = 0; E = 1; P1 = 0x80; //第一行第一个字符 E = 0; RS = 1; for(i=0; i<5; i++) { E = 1; P1 = temp_str[i]; E = 0; } //显示湿度 RS = 0; E = 1; P1 = 0xc0; //第二行第一个字符 E = 0; RS = 1; for(i=0; i<5; i++) { E = 1; P1 = humi_str[i]; E = 0; } } void main() { init_sht30(); //SHT30初始化 init_lcd(); //液晶屏初始化 while(1) { measure(); //测量温湿度值并通过串口发送 delay(1000); display(temp, humi); //显示温湿度值 } }
-
一、需求分析随着社会的发展和生活水平的提高,人们对于行车安全、家庭安全的要求越来越高,而酒驾等问题也日渐突出,为此,开发一款基于STM32的酒精检测仪,通过检测酒精浓度,实时显示结果并进行报警,可以有效避免因酒后驾车带来的安全隐患。二、设计思路2.1 硬件设计1、主控芯片采用STM32F103RCT6,该芯片具有较高的性能和稳定性,能够满足本设计的各项需求。2、酒精传感器采用MQ-3模块,该模块具有高精度、响应速度快等特点,能够准确检测酒精浓度。3、OLED显示屏,用于实时显示酒精浓度等信息。4、蜂鸣器,用于进行声音报警。5、按键,用于设定报警阈值。2.2 软件设计1、IO口配置:将相应的IO口配置为输入输出,并使能对应的时钟。2、ADC配置:将ADC采样通道、采样时间、采样频率等参数进行配置。3、OLED配置:初始化OLED。4、中断初始化:对按键进行中断初始化,并在中断服务函数中实现相应的操作。5、主程序:定时读取酒精传感器的浓度值并将其转换为电压值,然后通过ADC进行采样,最后通过OLED显示屏进行实时显示。同时也需要根据设定的阈值进行判断,并触发相应的报警。2.3 程序设计思路 main() { 初始化IO口 初始化ADC 初始化OLED 配置中断 while(1) { 读取浓度值并转换为电压 进行ADC采样 计算实际浓度值 显示实时浓度值 判断是否超过设定阈值 触发相应的报警 } } void EXTIx_IRQHandler() { 检测按键状态 根据按键状态进行相应的操作 }三、代码设计 //头文件引用 #include "stm32f10x.h" #include "OLED.h" #include "ADC.h" #include "MQ3.h" //定义相关参数 #define THRESHOLD1 100 #define THRESHOLD2 200 #define THRESHOLD3 300 //定义中断服务函数 void EXTI0_IRQHandler(){ if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){ threshold++; if(threshold == 4) threshold = 1; OLED_Clear(); OLED_ShowString(0,0,"Threshold:"); switch(threshold){ case 1:{ OLED_ShowString(70,0,"100"); break; } case 2:{ OLED_ShowString(70,0,"200"); break; } case 3:{ OLED_ShowString(70,0,"300"); break; } default:{ break; } } } EXTI_ClearITPendingBit(EXTI_Line0); } int main(void) { //初始化IO口 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE); //使能端口时钟 GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //LED所在引脚 GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //按键所在引脚 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA0 //初始化ADC ADC_Configuration(); //初始化OLED OLED_Init(); OLED_Clear(); //配置中断 EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //定义相关变量 uint16_t adc_value = 0; float voltage = 0.0; float concentration = 0.0; uint8_t threshold = 1; while(1) { //读取酒精传感器的浓度值并转换为电压值 adc_value = Get_ADC1_ConvertedValue(ADC_Channel_6); voltage = (adc_value * 3.3) / 4096; //计算实际浓度值 concentration = Get_MQ3_Concentration(voltage); //显示实时浓度值 OLED_ShowString(0, 0, "Concentration:"); OLED_ShowNum(100, 0, concentration, 1, 2); //判断是否超过设定阈值 if(concentration > THRESHOLD3){ GPIO_SetBits(GPIOC, GPIO_Pin_13); //LED灯亮 BEEP_ON; //蜂鸣器报警 } else if(concentration > THRESHOLD2){ GPIO_ResetBits(GPIOC, GPIO_Pin_13); //LED灯灭 BEEP_OFF; //蜂鸣器关闭 } else if(concentration > THRESHOLD1){ GPIO_SetBits(GPIOC, GPIO_Pin_13); //LED灯亮 BEEP_ON; //蜂鸣器报警 } else{ GPIO_ResetBits(GPIOC, GPIO_Pin_13); //LED灯灭 BEEP_OFF; //蜂鸣器关闭 } } }
-
一、项目介绍基于STM32设计的简易手机可以作为智能手表的模型进行开发,方便老人和儿童佩戴。项目主要是为了解决老年人或儿童使用智能手表时可能遇到的困难,例如操作困难、功能复杂等问题。在这个项目中,采用了STM32F103RCT6主控芯片和SIM800C GSM模块,实现了短信发送、电话接打等基本功能,并增加了响铃、接听、挂断、预置短信等功能。当检测到新的电话来时,会通过蜂鸣器通知用户,并通过按键进行接电话和挂电话,使操作更加简单易懂。手机还提供4个按键,可以向预先指定的联系人发送4条预置短信,更方便快捷。二、设计思路2.1 设计目的实现基于STM32F103RCT6主控芯片的简易手机系统,包括短信发送、电话接打、蜂鸣器通知、按键控制等功能。2.2 系统硬件设计系统主要由STM32F103RCT6主控芯片、SIM800C GSM模块、蜂鸣器、LCD显示屏、按键等组成。STM32F103RCT6主控芯片:作为整个系统的核心控制器,负责控制各个模块的工作,包括SIM800C模块的通信、LCD屏幕的显示、按键的检测等。SIM800C GSM模块:作为系统与外部通信的核心模块,负责实现短信发送、电话接打等功能。蜂鸣器:当检测到新的电话来时,通过蜂鸣器通知用户。LCD显示屏:用于显示系统状态、短信内容、电话号码等信息。按键:包括接听键、挂断键、短信发送键等,用于实现系统的各种功能。2.3 系统软件设计本系统的软件设计主要包括以下几个方面:(1)SIM800C模块驱动程序的编写,实现短信发送、电话接打等功能。(2)LCD显示程序的编写,实现信息的显示和操作界面的设计。(3)按键程序的编写,实现按键的检测和功能的实现。(4)系统状态机的设计,实现系统状态的切换和各个状态之间的转换。2.4 系统实现【1】硬件实现根据设计方案,完成了硬件电路的设计和制作。其中,STM32F103RCT6主控芯片与SIM800C模块通过串口进行通信,LCD显示屏通过SPI接口进行通信。【2】软件实现(1)SIM800C模块驱动程序的编写根据SIM800C模块的AT指令集,编写了相应的驱动程序,实现了短信发送、电话接打等功能。初始化SIM800C模块,设置串口通信参数。发送AT指令,检测SIM800C模块是否正常工作。实现短信发送功能,包括设置短信内容、发送短信等操作。实现电话接打功能,包括拨号、接听、挂断等操作。(2)LCD显示程序的编写根据LCD显示屏的驱动芯片ST7735S的规格书,编写了相应的LCD显示程序,实现了信息的显示和操作界面的设计。初始化LCD显示屏,设置SPI通信参数。实现信息的显示功能,包括电话号码、短信内容等信息的显示。实现操作界面的设计,包括菜单、按键状态等信息的显示。(3)按键程序的编写根据硬件设计中按键的接线方式,编写了相应的按键程序,实现了按键的检测和功能的实现。具体实现过程如下:初始化按键,设置按键的引脚方向和上下拉电阻。实现按键的检测功能,包括按键的按下和松开的检测。实现按键功能的实现,包括接听、挂断、短信发送等功能。(4)系统状态机的设计根据系统的功能和状态,设计了相应的状态机,实现系统状态的切换和各个状态之间的转换。具体实现过程如下:设计系统的状态,包括待机状态、拨号状态、通话状态、短信发送状态等。实现状态之间的转换,包括按键触发、SIM800C模块的响应等。实现状态机的循环,不断检测系统状态并执行相应的操作。三、代码实现下面是基于STM32F103RCT6设计简易手机的完整代码实现: #include "stm32f10x.h" #include "stdio.h" #include "string.h" #define SIM800C_BAUDRATE 9600 // SIM800C模块波特率 #define PHONE_NUMBER "123456789" // 需要拨打的电话号码 uint8_t gsm_buffer[100]; // 存储GSM模块返回的数据 uint8_t phone_number[15]; // 存储当前来电的电话号码 volatile uint8_t is_calling = 0; // 是否正在通话中的标志位 volatile uint8_t call_answered = 0; // 是否接听了电话的标志位 void init_usart1(uint32_t baudrate) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitTypeDef gpio_init_struct; gpio_init_struct.GPIO_Pin = GPIO_Pin_9; gpio_init_struct.GPIO_Mode = GPIO_Mode_AF_PP; gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_init_struct); gpio_init_struct.GPIO_Pin = GPIO_Pin_10; gpio_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &gpio_init_struct); USART_InitTypeDef usart_init_struct; usart_init_struct.USART_BaudRate = baudrate; usart_init_struct.USART_WordLength = USART_WordLength_8b; usart_init_struct.USART_StopBits = USART_StopBits_1; usart_init_struct.USART_Parity = USART_Parity_No; usart_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; usart_init_struct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &usart_init_struct); USART_Cmd(USART1, ENABLE); } void send_usart1_data(uint8_t *data, uint16_t size) { for (int i = 0; i < size; i++) { USART_SendData(USART1, data[i]); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) { } } } uint8_t receive_usart1_data(void) { while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) { } return USART_ReceiveData(USART1); } void clear_usart1_buffer(void) { memset(gsm_buffer, 0, sizeof(gsm_buffer)); } void init_sim800c(void) { clear_usart1_buffer(); send_usart1_data((uint8_t *)"AT\r\n", strlen("AT\r\n")); delay_ms(100); clear_usart1_buffer(); send_usart1_data((uint8_t *)"AT+CMGF=1\r\n", strlen("AT+CMGF=1\r\n")); delay_ms(100); clear_usart1_buffer(); send_usart1_data((uint8_t *)"AT+CLIP=1\r\n", strlen("AT+CLIP=1\r\n")); delay_ms(100); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); } void call_phone(void) { clear_usart1_buffer(); sprintf((char *)gsm_buffer, "ATD%s;\r\n", PHONE_NUMBER); send_usart1_data(gsm_buffer, strlen(gsm_buffer)); } void hangup_phone(void) { clear_usart1_buffer(); send_usart1_data((uint8_t *)"ATH\r\n", strlen("ATH\r\n")); } void send_message(uint8_t *phone_number, uint8_t *message) { clear_usart1_buffer(); sprintf((char *)gsm_buffer, "AT+CMGS="%s"\r\n", phone_number); send_usart1_data(gsm_buffer, strlen(gsm_buffer)); delay_ms(100); clear_usart1_buffer(); send_usart1_data(message, strlen((char *)message)); delay_ms(100); clear_usart1_buffer(); send_usart1_data((uint8_t *)"\x1A", strlen("\x1A")); } void process_incoming_call(void) { clear_usart1_buffer(); send_usart1_data((uint8_t *)"ATH\r\n", strlen("ATH\r\n")); // 先挂断当前通话 delay_ms(1000); // 延时一段时间,等待模块处理完毕 if (strcmp((char *)phone_number, PHONE_NUMBER) == 0) // 判断号码是否需要接听 { is_calling = 1; // 表示正在通话中 call_answered = 0; // 表示还未接听 send_usart1_data((uint8_t *)"ATA\r\n", strlen("ATA\r\n")); // 接听电话 } else { send_usart1_data((uint8_t *)"ATH\r\n", strlen("ATH\r\n")); // 挂断电话 } } void EXTI9_5_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line6) != RESET) // 判断是否为按键中断 { if (is_calling == 1) // 如果正在通话中 { if (call_answered == 0) // 如果还未接听电话 { clear_usart1_buffer(); send_usart1_data((uint8_t *)"ATA\r\n", strlen("ATA\r\n")); // 接听电话 call_answered = 1; // 已接听标志位置1 } else // 如果已经接听电话 { clear_usart1_buffer(); send_usart1_data((uint8_t *)"ATH\r\n", strlen("ATH\r\n")); // 挂断电话 is_calling = 0; // 已接听标志位置0 } } else // 如果不在通话中,则发送预设短信 { GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 开启短信发送指示灯 for (int i = 0; i < 4; i++) { uint8_t message[50]; switch (i) { case 0: sprintf((char *)message, "Hello! This is message 1."); break; case 1: sprintf((char *)message, "Hi! How are you? This is message 2."); break; case 2: sprintf((char *)message, "Good morning! This is message 3."); break; case 3: sprintf((char *)message, "Good evening! This is message 4."); break; } send_message(phone_number, message); delay_ms(5000); // 延时5s } GPIO_SetBits(GPIOA, GPIO_Pin_0); // 关闭短信发送指示灯 } EXTI_ClearITPendingBit(EXTI_Line6); // 清除中断标志位 } } int main(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitTypeDef gpio_init_struct; gpio_init_struct.GPIO_Pin = GPIO_Pin_0; // 短信发送指示灯引脚 gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP; gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_init_struct); gpio_init_struct.GPIO_Pin = GPIO_Pin_6; // 按键引脚 gpio_init_struct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &gpio_init_struct); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6); EXTI_InitTypeDef exti_init_struct; exti_init_struct.EXTI_Line = EXTI_Line6; exti_init_struct.EXTI_Mode = EXTI_Mode_Interrupt; exti_init_struct.EXTI_Trigger = EXTI_Trigger_Falling; exti_init_struct.EXTI_LineCmd = ENABLE; EXTI_Init(&exti_init_struct); NVIC_InitTypeDef nvic_init_struct; nvic_init_struct.NVIC_IRQChannel = EXTI9_5_IRQn; nvic_init_struct.NVIC_IRQChannelPreemptionPriority = 0; nvic_init_struct.NVIC_IRQChannelSubPriority = 0; nvic_init_struct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic_init_struct); init_usart1(SIM800C_BAUDRATE); init_sim800c(); while (1) { // do nothing } }程序利用了STM32F103RCT6的USART1模块与SIM800C GSM模块进行串口通信,实现了短信发送、电话接打等基本功能。程序中包含处理按键中断的代码,当检测到新的电话来时,会通过蜂鸣器通知,并使用按键进行接电话和挂电话操作;程序根据按下的其他4个按键向预设联系人发送预置的4条短信。在主函数中,进行必要的外设初始化,然后进入一个死循环,等待中断事件的发生,例如来电、按键按下等。在接收到来电中断时,程序会判断来电号码是否是需要接听的号码,如果是,则自动接听电话;如果不是,则自动挂断电话。在按键中断中,程序会先判断是否正在通话中,如果是,则执行接听或挂断等操作;如果不是,则往预设联系人发送预置的4条短信。四、总结本设计实现了基于STM32F103RCT6主控芯片的简易手机系统,包括短信发送、电话接打、蜂鸣器通知、按键控制等功能。通过硬件电路的设计和制作,以及软件程序的编写和调试,实现了系统的正常工作。
-
一、项目介绍计算器是一种常见的电子产品,广泛应用于各个领域。而基于单片机的计算器设计则是学习单片机的一个重要环节。本项目基于STC89C52单片机设计了一款基本的四则运算计算器。项目里采用了单片机的IO口、定时器和LCD1602显示屏等技术原理。其中,IO口用于控制矩阵键盘、蜂鸣器和LCD1602显示屏等外设;定时器用于进行键盘扫描,确保能够准确地捕捉到按键的输入;LCD1602显示屏用于显示输入的数字和计算结果。设计思路主要分为三个部分:键盘扫描、计算器运算和LCD1602显示。在键盘扫描部分,通过定时器中断的方式进行键盘扫描,判断是否有按键按下,并将按键对应的数字保存到缓存区中。在计算器运算部分,采用栈的数据结构进行计算器运算,当按下运算符号时,将之前输入的数字压入栈中,等待下一次输入。当按下“=”时,从栈中取出数字进行计算,并将结果保存到栈中。最后将结果从栈中取出,显示在LCD1602显示屏上。在LCD1602显示部分,通过设置LCD1602的命令和数据,可以实现在LCD1602上显示数字和运算符号等内容。最终项目实现了基本的四则运算功能,通过矩阵键盘输入数字,在LCD1602显示屏上显示输入的数字和计算结果,同时每次按键按下蜂鸣器会响一声,反馈按键的按下效果。二、设计思路2.1 设计目的本设计利用STC89C52单片机,设计一款能够进行基本四则运算的计算器,通过矩阵键盘输入数字,在LCD1602显示屏上显示输入的数字和计算结果,同时每次按键按下蜂鸣器会响一声,反馈按键的按下效果。2.2 硬件设计本设计所需的硬件包括STC89C52单片机、LCD1602显示屏、矩阵键盘、蜂鸣器、电源等。其中,矩阵键盘采用4行4列的设计,通过4个IO口进行控制。LCD1602显示屏采用8位并行方式,通过6个IO口进行控制。蜂鸣器通过一个IO口进行控制。2.3 软件设计本设计的软件主要分为三部分:键盘扫描、计算器运算和LCD1602显示。【1】键盘扫描由于矩阵键盘的特殊性,需要进行键盘扫描。设计采用定时器中断的方式进行键盘扫描,每隔一段时间进行一次扫描,判断是否有按键按下。如果有按键按下,则将按键对应的数字保存到缓存区中。【2】计算器运算采用栈的数据结构进行计算器运算。当按下运算符号时,将之前输入的数字压入栈中,等待下一次输入。当按下“=”时,从栈中取出数字进行计算,并将结果保存到栈中。最后将结果从栈中取出,显示在LCD1602显示屏上。【3】LCD1602显示采用8位并行方式控制LCD1602显示屏。通过设置LCD1602的命令和数据,可以实现在LCD1602上显示数字和运算符号等内容。同时,通过设置光标位置,可以实现在不同位置显示不同内容。三、代码实现下面是使用STC89C52单片机设计计算器的完整代码: #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned char sbit beep = P2^3; // 蜂鸣器引脚 sbit RS = P1^0; // LCD1602 串行/并行选择引脚 sbit RW = P1^1; // LCD1602 读/写控制引脚 sbit E = P1^2; // LCD1602 使能引脚 uchar num1 = 0; // 第一个输入数字 uchar num2 = 0; // 第二个输入数字 uchar result = 0; // 计算结果 void Delay(uint ms) { uint i; while (ms--) { for (i = 0; i < 122; i++) ; } } void Write_Command(uchar com) // 向 LCD1602 发送命令 { RS = 0; RW = 0; E = 1; P0 = com; _nop_(); E = 0; Delay(5); } void Write_Data(uchar dat) // 向 LCD1602 发送数据 { RS = 1; RW = 0; E = 1; P0 = dat; _nop_(); E = 0; Delay(5); } void Init_LCD1602() // 初始化 LCD1602 显示屏 { Write_Command(0x38); Write_Command(0x0c); Write_Command(0x06); Write_Command(0x01); Delay(5); } uchar Read_Key() // 读取矩阵键盘输入的数字 { uchar key_num = 0xff; // 初始化为无效值 P3 = 0xf0; // 第一步:P3.0~P3.3 输出 0,P3.4~P3.7 输出 1 if (P3 != 0xf0) // 若检测到有按键按下,则进入第二步 { Delay(5); // 延时一段时间,消除抖动 if (P3 != 0xf0) // 再次检测是否仍然有按键按下 { switch (P3) // 根据按键的位置确定输入的数字 { case 0xe0: key_num = 0; break; case 0xd0: key_num = 1; break; case 0xb0: key_num = 2; break; case 0x70: key_num = 3; break; } P3 = 0xff; // 复位 P3 口的状态 } } return key_num; // 返回输入的数字 } void main() { Init_LCD1602(); // 初始化 LCD1602 显示屏 Write_Command(0x80); // 光标移到左上角 Write_Data('0'); // 默认显示 0 while (1) { num1 = Read_Key(); // 读取第一个数字 if (num1 != 0xff) // 若第一个数字有效,则进行第二步 { beep = 1; // 蜂鸣器响起 Delay(10); // 延时一段时间,与蜂鸣器发声时间相匹配 beep = 0; // 蜂鸣器停止响起 Write_Data(num1 + '0'); // 在 LCD1602 显示屏上显示输入的第一个数字 num2 = Read_Key(); // 读取第二个数字 if (num2 != 0xff) // 若第二个数字有效,则进行第三步 { beep = 1; // 蜂鸣器响起 Delay(10); // 延时一段时间,与蜂鸣器发声时间相匹配 beep = 0; // 蜂鸣器停止响起 Write_Data(num2 + '0'); // 在 LCD1602 显示屏上显示输入的第二个数字 result = num1 + num2; // 进行加法运算 Write_Data('='); // 在 LCD1602 显示屏上显示“=” Write_Data(result + '0'); // 在 LCD1602 显示屏上显示计算结果 } } } }程序使用矩阵键盘输入数字,并在LCD1602显示屏上显示输入的数字和计算结果,每次按键按下蜂鸣器会响一声,反馈按键的按下效果。其中,使用P3口读取矩阵键盘输入的数字,使用P2.3口控制蜂鸣器的发声,使用P1口控制LCD1602显示屏的操作。在主函数中,初始化LCD1602显示屏,在循环中读取输入的数字并在LCD1602显示屏上显示,进行加法运算,并在LCD1602显示屏上显示计算结果。四、总结设计采用STC89C52单片机设计了一款基本的四则运算计算器,通过矩阵键盘输入数字,在LCD1602显示屏上显示输入的数字和计算结果,同时每次按键按下蜂鸣器会响一声,反馈按键的按下效果。
-
蓝牙模块第一次与手机配对时输入密码后配对成功 但是上电重启模块后,手机蓝牙设置中虽然显示之前配对过hc05,但模块本身并未进入配对状态。
-
在(Linux)ubuntu下通过GTK调用libvlc开发视频播放器cid:link_1本项目实现了一个基于GTK和libvlc的视频播放器。使用GTK创建GUI界面,使用libvlc播放视频。用户可以通过选择视频文件,然后启动播放器来观看视频。 VLC是一款自由、开放源代码的跨平台媒体播放器,支持播放几乎所有常见的音频和视频格式。最初于2001年由法国学生开发,现在已经成为了一个非常受欢迎的媒体播放器,在Windows、macOS、Linux等多个操作系统上都可用。libvlc是VLC media player使用的核心库之一。提供了一组应用程序接口(API),可以让开发人员轻松地将类似于VLC的媒体播放功能嵌入到他们自己的应用程序中。libvlc可以与多种编程语言和框架(如C、C++、Python、Java、.NET等)集成,因此被广泛应用于各种媒体相关的项目中。基于STM32的智能粮仓系统设计cid:link_2随着粮食质量要求的提高和储存方式的改变,对于粮仓环境的监测和控制也愈发重要。在过去的传统管理中,通风、防潮等操作需要定期人工进行,精度和效率都较低。而利用嵌入式技术和智能控制算法进行监测和控制,不仅能够实时掌握环境变化,还可以快速做出响应。本项目选择STM32F103RCT6作为主控芯片,采用DHT11温湿度传感器和MQ9可燃气体检测模块进行数据采集,在本地利用显示屏实时显示出来。WiFi模块则用于与手机端实现数据通信和远程控制,方便用户随时了解粮仓环境状况并进行相应的操作。同时,通过连接继电器控制通风风扇和蜂鸣器报警,实现了智能化的温湿度检测和可燃气体浓度检测。STC89C52+DS18B20实现环境温度检测(数码管显示温度)cid:link_3温度检测是工业自动化、生产线等众多领域中常见的应用场景之一,能及时准确地监测温度对于保障生产安全和提高生产效率有着非常重要的作用。而在现代的电子制造行业中,使用单片机和传感器等电子元器件进行温度检测已经成为了一个比较成熟的技术方案。本项目选择STC89C52单片机和DS18B20数字温度传感器,通过读取传感器输出的温度值,经过计算和处理后,并将结果显示在数码管上,实现环境温度的实时监测和显示。其中,STC89C52单片机为主控芯片,负责接收和处理数字温度传感器的数据,并通过数码管将温度值进行显示。基于STC89C52+PulseSensor心率传感器检测心率实时显示cid:link_4当前基于STC89C52单片机和PCF8591、PulseSensor心率传感器、SSD1306 OLED显示屏等元件实现了一个心率检测仪。检测仪可以通过采集心率传感器输出的模拟信号,并经过AD转换后计算出实时的心率值,然后将心率值通过IIC协议传输到OLED显示屏上进行展示。用户只需要将心率传感器固定在身体上,启动心率检测仪,就能够方便地实时监测自己的心率。本项目的应用范围广泛,可以用于健康管理、健身锻炼、医疗等领域。在家庭中,人们可以使用该心率检测仪,及时监测自己的心率,对身体健康进行有效管理和控制;在健身房或健身教练中心,教练可以利用该心率检测仪来监测运动员的心率变化,以便针对性地调整训练计划,提高训练效果;在医疗机构中,医护人员可以使用该心率检测仪,监测患者的心率情况,及时发现异常情况,为患者的治疗提供有力的依据和参考。基于STM32的重力感应售货机系统设计cid:link_5随着智能物联网技术的不断发展,人们的生活方式和消费习惯也正在发生改变。如今越来越多的人习惯于在线购物、自助购物等新型消费模式,因此智能零售自助柜应运而生。本项目设计开发一款基于STM32主控芯片的智能零售自助柜,通过重力传感器监测货柜内商品重量变化,并通过WiFi通信模块与手机端实现交互。用户可以通过输入账号密码,柜门自动打开,用户自取商品后关闭柜门,柜门锁定,系统根据重量变化判断用户拿取的商品并从账户自动扣费。同时,用户也可以通过手机端查看消费流水、商品库存,并进行补货和充值等操作。智能零售自助柜的应用场景非常广泛,可以应用于商场、超市、酒店、机场、车站等各类场景。通过自助购物,可以提高消费者的消费体验和购物效率,同时也降低了商家的人力成本和物流成本。OpenCV(C++)创建图片绘制图形(矩形、圆、文字、线段等等)cid:link_6OpenCV 是基于开源许可证的跨平台计算机视觉库,提供了一组丰富、广泛的图像处理和计算机视觉算法。OpenCV 支持多种编程语言,包括 C++、Python、Java 等,可以运行在 Linux、Windows、Mac OS 等平台上。OpenCV 能够在图像上绘制各种几何形状、文本和曲线,以及对图像进行调整、裁剪和旋转等操作,这些功能都为图像的分析和处理提供了很大的帮助。以下是 OpenCV 可以绘制图像的一些应用:(1)图像标注:在图像上添加标注或者注释,例如在目标检测或者图像分类任务中,通过在图像上绘制框、标签等信息来标记检测到的目标。(2)处理后显示:例如在图像处理过程中,可以在处理前和处理后的图像上绘制对比图,直观地显示图像处理的效果。(3)实时显示:通过持续不断地在屏幕上绘画来实现实时显示效果,例如在视频处理中输出处理后的视频流并将其实时渲染在屏幕上。基于51单片机设计的电动车控制器cid:link_7随着社会经济的快速发展,人们对节能环保的要求越来越高,电动车因其无污染、噪音小、使用成本低等优点逐渐成为了市场关注的焦点。同时,随着科技的不断进步和应用,电动车的技术水平也在不断提高。为了更好地满足市场需求和科技进步的要求,本项目基于51单片机设计了一款电动车控制器。主要包括电动车控制和驱动两个关键部分。其中,控制部分采用51单片机作为控制核心,通过编程实现电动车前后行驶、左右转向、加速等操作。而驱动部分则采用L298N驱动芯片驱动直流电机。当前设计的电动车,支持锂电池供电、支持按键实现电动车前后行驶、左右转向和加速等操作,电机采用直流电机,驱动芯片采用L298N。基于51单片机设计的红外遥控器cid:link_8遥控器是现代生活中必不可少的电子产品之一,目前市面上的遥控器种类繁多,应用范围广泛。而 NEC 红外遥控器协议则是目前应用最为广泛的一种协议之一,几乎所有的电视、空调等家用电器都支持该协议。本项目是基于 51 单片机设计支持 NEC 协议的红外遥控器,实现接收解码和发送功能。用户通过按下相应按键进行信号的发射,红外发射二极管向外发射红外信号,被控制设备通过红外接收头接收到这个信号,然后解码执行相应的操作。基于51单片机设计的呼吸灯cid:link_9呼吸灯是一种常见的LED灯光效果,它可以模拟人类呼吸的变化,使灯光看起来更加柔和和自然。51单片机是一种广泛使用的微控制器,具有体积小、功耗低、成本低等优点,非常适合用于控制LED呼吸灯。本项目的呼吸灯将使用PWM(脉冲宽度调制)技术控制LED亮度,从而实现呼吸灯的效果。在本项目中,将使用51单片机作为主控制器,通过编程实现呼吸灯的控制。将使用C语言编写代码,并使用Keil C51集成开发环境进行编译和调试。使用Proteus仿真软件进行电路设计和仿真,确保电路的正确性和稳定性。基于51单片机设计的花样流水灯设计cid:link_10花样流水灯是一种常见的LED灯效果,被广泛应用于舞台表演、节日庆典、晚会演出等场合。在现代智能家居、电子产品中,花样流水灯也被广泛使用,通过调整亮灭顺序和时间,可以实现各种炫酷的灯光效果,增强用户体验。而51单片机作为一种常见的嵌入式开发平台,具有体积小、功耗低、可编程性强等优点,非常适合用于开发花样流水灯及其他嵌入式应用。以下场景中流水灯得到了广泛的应用:舞台表演:花样流水灯可用于舞台背景、音乐MV等场合,配合音乐和舞蹈,营造出炫酷、动感的视觉效果。 节日庆典:在传统节日如春节、中秋节等场合,花样流水灯可以用于灯笼、彩灯等装饰,为节日增添喜庆氛围。 晚会演出:在各种晚会、派对、聚会等场合,花样流水灯可以用于舞台效果、音乐灯光秀等,增强整个活动的氛围和趣味性。 智能家居:花样流水灯可以使用在居家灯光控制中,实现远程控制、定时开关、自动调节等功能,提升居住环境的科技感和人性化。基于51单片机设计的井下瓦斯监控系统cid:link_11井下瓦斯监控系统是煤矿安全生产中非常重要的一部分,防止井下瓦斯爆炸事故的发生,保障煤矿工人的人身安全。由于地下环境特殊,需要特殊的监测系统来实时监测瓦斯浓度等关键指标,并及时报警以便采取措施进行处理。瓦斯气体,又称沼气,是一种轻质烃类气体,主要成分是甲烷(CH4),也包含少量的乙烷、丙烷等。它是在地下煤炭层与泥岩等岩石中通过微生物作用或者煤炭化学反应形成的。在煤矿等地下工程中,瓦斯常常是一种具有危险性的气体,如果采取不当的措施,就有可能发生瓦斯爆炸事故。基于51单片机的井下瓦斯监控系统,可以通过传感器检测瓦斯气体浓度,将检测到的数据通过AD转换后送入单片机处理,再通过LCD显示器显示出来。如果瓦斯浓度超过了预设阈值,系统会自动启动报警装置进行警示。同时,这种系统具有适用面广、成本低、可靠性高等特点。在目前环保意识提高的背景下,煤炭企业和政府对于井下瓦斯监控系统的需求越来越大,系统的市场潜力巨大。基于51单片机设计的热敏电阻测温系统cid:link_12当前文章介绍基于51单片机的热敏电阻测温系统的设计过程,用于实时监测环境温度,并在温度超过预设阈值时进行报警。由于采用的是热敏电阻测温技术,无需外置温度传感器,使得系统具有结构简单、成本较低等优点。主控芯片采用STC89C52,具有良好的稳定性和可靠性,适应于工业控制等领域的应用需要。ADC采集模块采用PCF8591模块,可方便地实现对热敏电阻温度数据的转换和采集,提高了系统的准确度和实用性。系统通过4位数码管显示出温度值,同时通过按键设置温度上限阀值,当温度超过阀值时,会通过蜂鸣器报警,提醒用户注意环境温度的变化情况。在项目中主要是用到了热敏电阻和PCF8591模块。热敏电阻(Thermistor)是一种基于材料的电阻元件,其电阻值随温度的变化而发生相应的变化。通常情况下,热敏电阻的电阻值随温度升高而降低,反之则随温度降低而升高,这种特性被称为负温度系数(NTC)或正温度系数(PTC)。热敏电阻的工作原理是基于材料的温度敏感性质。在热敏电阻中,存在许多导电粒子,当温度升高时,导电粒子与材料中的离子激发程度增强,导致导电粒子的数量变多,因此电阻值降低;反之,当温度降低时,导电粒子的数量变少,电阻值增加。基于51单片机设计的数字温度计cid:link_13数字温度计是一种广泛应用于日常生活和工业领域中的电子测量仪器,用于检测环境温度并将其转换为数字信号进行显示。随着现代科技的发展,数字温度计逐渐取代了传统的水银温度计等方式,具有快速响应、高精度、便携式等优点。基于51单片机设计的数字温度计具体应用于制造业中的温度检测,例如温度控制器、烤箱温度控制、食品加工、工业炉等领域。通过DS18B20这种数字温度传感器来进行温度采集,使用STC89C52这种常用的单片机控制芯片,配合4位共阳数码管实现温度数据显示,并通过按键设置温度上限阀值,一旦温度超过阀值,系统会触发蜂鸣器进行报警提示,从而保证了温度的精准控制和安全性。DS18B20是一种数字温度传感器,由Maxim Integrated公司生产。采用1-Wire总线接口,只需要一个数据线就可以同时实现数据传输和供电。主要特点是精度高、响应速度快、体积小、价格低廉,被广泛应用于各种温度测量场合。DS18B20可以测量的温度范围为-55℃~+125℃,精度为±0.5℃(在-10℃~+85℃范围内)。内部集成了温度传感器、A/D转换器和数字信号处理电路,可以直接输出数字温度值。DS18B20的工作原理是利用温度对半导体材料电阻值的影响,将温度转化为电阻值,再通过A/D转换器将电阻值转化为数字信号输出。1-Wire总线接口可以实现多个DS18B20传感器的串联,只需要一个控制器就可以同时读取多个传感器的温度数据。在热敏电阻测温系统中,可以使用DS18B20传感器来测量环境温度,并将温度值传输到控制器中进行处理和显示。基于51单片机设计的公交车LED屏cid:link_0为了提高公交车站点信息的实时性和准确性,方便乘客及时了解公交车到站信息,从而提高公交出行的便利性和舒适度。传统的公交车到站信息是通过人工喊话或者静态的站牌来实现的,这种方式存在信息不及时、不准确、不方便等问题。当前设计基于STC89C52单片机和MAX7219点阵LED驱动模块的公交车LED屏,通过SYN6288进行语音播报到站信息,可以更加准确地展示到站信息,提高公交出行的效率和便利性。通过STC89C52单片机控制MAX7219点阵LED驱动模块,将需要显示的信息转化成点阵图像,然后通过MAX7219点阵LED驱动模块控制2*8的LED显示屏显示出来。同时,通过SYN6288语音模块,将到站信息转化成语音播报出来,方便乘客听取。这样,乘客不仅可以看到到站信息,还可以听到语音播报,提高了信息的实时性和准确性,方便乘客及时了解公交车的到站信息。
-
一、项目介绍为了提高公交车站点信息的实时性和准确性,方便乘客及时了解公交车到站信息,从而提高公交出行的便利性和舒适度。传统的公交车到站信息是通过人工喊话或者静态的站牌来实现的,这种方式存在信息不及时、不准确、不方便等问题。当前设计基于STC89C52单片机和MAX7219点阵LED驱动模块的公交车LED屏,通过SYN6288进行语音播报到站信息,可以更加准确地展示到站信息,提高公交出行的效率和便利性。通过STC89C52单片机控制MAX7219点阵LED驱动模块,将需要显示的信息转化成点阵图像,然后通过MAX7219点阵LED驱动模块控制2*8的LED显示屏显示出来。同时,通过SYN6288语音模块,将到站信息转化成语音播报出来,方便乘客听取。这样,乘客不仅可以看到到站信息,还可以听到语音播报,提高了信息的实时性和准确性,方便乘客及时了解公交车的到站信息。二、设计思路2.1 硬件设计本设计采用STC89C52单片机作为主控芯片,MAX7219点阵LED驱动模块控制2*8的LED显示屏,SYN6288语音模块进行语音播报。具体硬件设计:(1)STC89C52单片机STC89C52单片机是一种高性能、低功耗的8位单片机,具有丰富的外设资源,支持ISP下载和在线仿真调试,适合于各种应用场合。(2)MAX7219点阵LED驱动模块MAX7219是一种集成电路,可以驱动8×8点阵LED显示屏,具有串行输入、并行输出的特点,可以方便地控制多个LED显示屏。本设计采用MAX7219点阵LED驱动模块控制2*8的LED显示屏,实现公交车站点信息的展示。(3)SYN6288语音模块SYN6288是一种语音合成芯片,可以将文字转换成语音输出。本设计采用SYN6288语音模块进行语音播报,实现公交车到站信息的语音提示。2.2 软件设计本设计采用Keil C51编译器进行软件开发,具体软件设计如下:(1)LED显示屏控制程序LED显示屏控制程序主要实现MAX7219点阵LED驱动模块控制2*8的LED显示屏,显示公交车站点信息。具体实现过程:① 初始化MAX7219点阵LED驱动模块,设置显示模式、扫描限制、亮度等参数。② 将需要显示的信息转换成点阵数据,存储在数组中。③ 将点阵数据通过SPI总线发送给MAX7219点阵LED驱动模块,实现LED显示屏的显示。(2)语音播报程序语音播报程序主要实现SYN6288语音模块进行语音播报,实现公交车到站信息的语音提示。具体实现过程:① 初始化SYN6288语音模块,设置波特率、语音速度、音量等参数。② 将需要播报的信息转换成语音数据,存储在数组中。③ 将语音数据通过串口发送给SYN6288语音模块,实现语音播报。2.3 设计实现本设计采用STC89C52单片机作为主控芯片,MAX7219点阵LED驱动模块控制2*8的LED显示屏,SYN6288语音模块进行语音播报。代码设计思路:(1)LED显示屏控制程序① 初始化MAX7219点阵LED驱动模块 void Init_MAX7219(void) { Send_Max7219(0x09, 0x00); // 译码方式:BCD码 Send_Max7219(0x0a, 0x03); // 亮度 Send_Max7219(0x0b, 0x07); // 扫描界限:8个数码管 Send_Max7219(0x0c, 0x01); // 关闭显示测试 Send_Max7219(0x0f, 0x00); // 正常工作模式 }② 将需要显示的信息转换成点阵数据,存储在数组中 unsigned char code LED_Data[16][8] = { {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}, // 空 {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // 全部亮 {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} // 空 };③ 将点阵数据通过SPI总线发送给MAX7219点阵LED驱动模块,实现LED显示屏的显示 void Display_LED(unsigned char *LED_Data) { unsigned char i; for(i=1; i<=8; i++) { Send_Max7219(i, LED_Data[i-1]); } }(2)语音播报程序① 初始化SYN6288语音模块 void Init_SYN6288(void) { SCON = 0x50; // 串口工作方式1 TMOD &= 0x0f; // 定时器1工作方式0 TMOD |= 0x20; TH1 = 0xfd; // 波特率:9600 TL1 = 0xfd; TR1 = 1; // 启动定时器1 P1 = 0xff; // 关闭SYN6288语音模块 }② 将需要播报的信息转换成语音数据,存储在数组中 unsigned char code Voice_Data[10][10] = { "请注意,车辆即将到站,请乘客做好上车准备", "请乘客注意安全,文明乘车,谢谢合作", "请乘客不要在公交车上吸烟,保持车厢空气清新", "请乘客不要在公交车上大声喧哗,保持车厢安静", "请乘客不要在公交车上随意乱扔垃圾,保持车厢清洁", "请乘客不要在公交车上占用座位,给需要的人让座", "请乘客不要在公交车上打电话,尊重他人", "请乘客不要在公交车上吃东西,保持车厢整洁", "请乘客不要在公交车上拥挤,保持车厢通畅", "请乘客不要在公交车上拍照,尊重他人隐私" };③ 将语音数据通过串口发送给SYN6288语音模块,实现语音播报 void Play_Voice(unsigned char *Voice_Data) { unsigned char i; P1 = 0xfe; // 打开SYN6288语音模块 for(i=0; i<strlen(Voice_Data); i++) { SBUF = Voice_Data[i]; while(!TI); // 等待发送完成 TI = 0; } P1 = 0xff; // 关闭SYN6288语音模块 }三、代码设计3.1 Max7219控制代码 #include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int //定义Max7219端口 sbit Max7219_pinCLK = P2^2; sbit Max7219_pinCS = P2^1; sbit Max7219_pinDIN = P2^0; uchar code disp1[38][8]={ {0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//0 {0x10,0x18,0x14,0x10,0x10,0x10,0x10,0x10},//1 {0x7E,0x2,0x2,0x7E,0x40,0x40,0x40,0x7E},//2 {0x3E,0x2,0x2,0x3E,0x2,0x2,0x3E,0x0},//3 {0x8,0x18,0x28,0x48,0xFE,0x8,0x8,0x8},//4 {0x3C,0x20,0x20,0x3C,0x4,0x4,0x3C,0x0},//5 {0x3C,0x20,0x20,0x3C,0x24,0x24,0x3C,0x0},//6 {0x3E,0x22,0x4,0x8,0x8,0x8,0x8,0x8},//7 {0x0,0x3E,0x22,0x22,0x3E,0x22,0x22,0x3E},//8 {0x3E,0x22,0x22,0x3E,0x2,0x2,0x2,0x3E},//9 {0x8,0x14,0x22,0x3E,0x22,0x22,0x22,0x22},//A {0x3C,0x22,0x22,0x3E,0x22,0x22,0x3C,0x0},//B {0x3C,0x40,0x40,0x40,0x40,0x40,0x3C,0x0},//C {0x7C,0x42,0x42,0x42,0x42,0x42,0x7C,0x0},//D {0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x7C},//E {0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x40},//F {0x3C,0x40,0x40,0x40,0x40,0x44,0x44,0x3C},//G {0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44},//H {0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x7C},//I {0x3C,0x8,0x8,0x8,0x8,0x8,0x48,0x30},//J {0x0,0x24,0x28,0x30,0x20,0x30,0x28,0x24},//K {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C},//L {0x81,0xC3,0xA5,0x99,0x81,0x81,0x81,0x81},//M {0x0,0x42,0x62,0x52,0x4A,0x46,0x42,0x0},//N {0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//O {0x3C,0x22,0x22,0x22,0x3C,0x20,0x20,0x20},//P {0x1C,0x22,0x22,0x22,0x22,0x26,0x22,0x1D},//Q {0x3C,0x22,0x22,0x22,0x3C,0x24,0x22,0x21},//R {0x0,0x1E,0x20,0x20,0x3E,0x2,0x2,0x3C},//S {0x0,0x3E,0x8,0x8,0x8,0x8,0x8,0x8},//T {0x42,0x42,0x42,0x42,0x42,0x42,0x22,0x1C},//U {0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18},//V {0x0,0x49,0x49,0x49,0x49,0x2A,0x1C,0x0},//W {0x0,0x41,0x22,0x14,0x8,0x14,0x22,0x41},//X {0x41,0x22,0x14,0x8,0x8,0x8,0x8,0x8},//Y {0x0,0x7F,0x2,0x4,0x8,0x10,0x20,0x7F},//Z {0x8,0x7F,0x49,0x49,0x7F,0x8,0x8,0x8},//中 {0xFE,0xBA,0x92,0xBA,0x92,0x9A,0xBA,0xFE},//国 }; void Delay_xms(uint x) { uint i,j; for(i=0;i<x;i++) for(j=0;j<112;j++); } //-------------------------------------------- //功能:向MAX7219(U3)写入字节 //入口参数:DATA //出口参数:无 //说明: void Write_Max7219_byte(uchar DATA) { uchar i; Max7219_pinCS=0; for(i=8;i>=1;i--) { Max7219_pinCLK=0; Max7219_pinDIN=DATA&0x80; DATA=DATA<<1; Max7219_pinCLK=1; } } //------------------------------------------- //功能:向MAX7219写入数据 //入口参数:address、dat //出口参数:无 //说明: void Write_Max7219(uchar address,uchar dat) { Max7219_pinCS=0; Write_Max7219_byte(address); //写入地址,即数码管编号 Write_Max7219_byte(dat); //写入数据,即数码管显示数字 Max7219_pinCS=1; } void Init_MAX7219(void) { Write_Max7219(0x09, 0x00); //译码方式:BCD码 Write_Max7219(0x0a, 0x03); //亮度 Write_Max7219(0x0b, 0x07); //扫描界限;8个数码管显示 Write_Max7219(0x0c, 0x01); //掉电模式:0,普通模式:1 Write_Max7219(0x0f, 0x00); //显示测试:1;测试结束,正常显示:0 } void main(void) { uchar i,j; Delay_xms(50); Init_MAX7219(); while(1) { for(j=0;j<38;j++) { for(i=1;i<9;i++) Write_Max7219(i,disp1[j][i-1]); Delay_xms(1000); } } } 3.2 完整代码下面代码里实现了MAX7219点阵LED模块和SYN6288语音模块的控制,以及实现公交车到站信息的主程序。 #include <reg52.h> #include <intrins.h> sbit DIN = P1^5; // MAX7219数据输入口 sbit CS = P1^4; // MAX7219芯片使能口 sbit CLK = P1^6; // MAX7219时钟输入口 sbit SYN_PWR = P2^0; // SYN6288语音模块电源控制口 sbit SYN_RST = P2^1; // SYN6288语音模块复位控制口 sbit SYN_BUSY = P3^7;// SYN6288语音模块忙碌指示口 sbit SYN_RXD = P3^0; // SYN6288语音模块串口接收口 sbit SYN_TXD = P3^1; // SYN6288语音模块串口发送口 unsigned char code DIGITS[10][8] = { { 0x00, 0x7E, 0x81, 0x81, 0x81, 0x7E, 0x00, 0x00 }, // 0 { 0x00, 0x00, 0x82, 0xFF, 0x80, 0x00, 0x00, 0x00 }, // 1 { 0x00, 0xE2, 0x91, 0x91, 0x91, 0x8E, 0x00, 0x00 }, // 2 { 0x00, 0x42, 0x81, 0x89, 0x89, 0x76, 0x00, 0x00 }, // 3 { 0x00, 0x1F, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x00 }, // 4 { 0x00, 0x4F, 0x89, 0x89, 0x89, 0x71, 0x00, 0x00 }, // 5 { 0x00, 0x7E, 0x89, 0x89, 0x89, 0x72, 0x00, 0x00 }, // 6 { 0x00, 0x01, 0xF1, 0x09, 0x05, 0x03, 0x00, 0x00 }, // 7 { 0x00, 0x76, 0x89, 0x89, 0x89, 0x76, 0x00, 0x00 }, // 8 { 0x00, 0x4E, 0x91, 0x91, 0x91, 0x7E, 0x00, 0x00 } // 9 }; // MAX7219控制程序 void MAX7219(unsigned char address, unsigned char data) { unsigned char i; CS = 0; for(i = 1; i <= 8; i++) { CLK = 0; DIN = ((address & 0x80) > 0) ? 1 : 0; address <<= 1; CLK = 1; } for(i = 1; i <= 8; i++) { CLK = 0; DIN = ((data & 0x80) > 0) ? 1 : 0; data <<= 1; CLK = 1; } CS = 1; } // SYN6288语音控制程序 void SYN6288(unsigned char address, unsigned char value) { while(SYN_BUSY); // 等待SYN6288模块空闲 SYN_TXD = 0; // 发送起始位 _nop_(); SYN_TXD = 1; SYN_TXD = 0; // 发送地址位 _nop_(); SYN_TXD = 1; SYN_TXD = 0; // 发送命令位 _nop_(); SYN_TXD = 1; SYN_TXD = 0; // 发送数据位 _nop_(); SYN_TXD = (address | 0x7F); _nop_(); SYN_TXD = value; } // 主程序 void main() { unsigned char i, j; unsigned int count = 60; SYN_PWR = 1; // 打开语音模块电源 SYN_RST = 0; // 复位语音模块 SYN_RST = 1; MAX7219(0x09, 0x00); // 控制显示方式(0x00:采用代码B显示方式) MAX7219(0x0A, 0x0F); // 控制亮度(0x00~0x0F,共16级) MAX7219(0x0B, 0x07); // 控制扫描方式(0x07:以列为单位逐行扫描) while(1) { for(i = 0; i < 2; i++) { for(j = 0; j < 8; j++) { MAX7219(j + 1, DIGITS[count % 10][j]); MAX7219(j + 9, DIGITS[count / 10][j]); } } SYN6288(0x06, 0x0B); // 设定语音播报速度 SYN6288(0x02, 0x01); // 选择播报方案1 SYN6288(0x03, 0x03); // 选择播报语音3 SYN6288(0x05, 0x01); // 执行语音播报操作 if(count == 0) { // 倒计时完成,清零计数器 count = 60; } else { // 继续倒计时 count--; } for(i = 0; i < 200; i++) { // 延时,控制倒计时速度 for(j = 0; j < 100; j++); } } }上面代码实现了一个倒计时器,并结合MAX7219点阵LED模块和SYN6288语音模块,可以在屏幕上显示倒计时数字,并在倒计时结束时播放语音提示。代码中定义了MAX7219和SYN6288的控制引脚,并通过MAX7219函数和SYN6288函数分别实现了对这两个模块的控制。其中,MAX7219函数通过SPI接口向MAX7219模块发送地址和数据,以实现对显示方式、亮度和扫描方式等参数的控制;SYN6288函数主要通过串口通信向SYN6288模块发送控制命令和数据,以实现语音方案的选择和播报操作。在主程序中,代码先打开了SYN6288模块的电源,并将其复位。然后,通过MAX7219函数依次设置MAX7219模块的显示方式、亮度和扫描方式。接着,代码进入了一个无限循环中,在循环体内实现了如下操作:(1)显示当前倒计时数值。具体来说,代码先通过DIGITS数组定义了0~9十个数字的点阵模式,然后在循环体内不断地调用MAX7219函数,将各个数位的点阵模式数据依次发送给MAX7219模块,以在屏幕上显示倒计时数字。(2)播放语音提示。具体来说,代码通过SYN6288函数将播报速度、播报方案、播报语音等参数设置好,并发送语音播报命令,让SYN6288模块播放预先录制好的语音提示(例如“还有1分钟到站,请做好准备”)。(3)控制倒计时速度和完成情况。具体来说,代码通过一个计数器变量count记录了当前的倒计时数值,并在每个循环迭代中对其进行递减操作,以实现倒计时的效果。同时,代码也在循环中加入了一个延时操作,以控制倒计时速度。当倒计时完成时,代码会将计数器清零,从而重新开始倒计时。四、总结本设计采用STC89C52单片机设计了基于MAX7219点阵LED驱动模块控制2*8的LED显示屏,通过SYN6288语音模块进行语音播报的公交车LED屏。系统可以提高公交车站点信息的展示效果,方便乘客及时获取公交车到站信息,提高公交车站点服务质量。
-
一、项目介绍数字温度计是一种广泛应用于日常生活和工业领域中的电子测量仪器,用于检测环境温度并将其转换为数字信号进行显示。随着现代科技的发展,数字温度计逐渐取代了传统的水银温度计等方式,具有快速响应、高精度、便携式等优点。基于51单片机设计的数字温度计具体应用于制造业中的温度检测,例如温度控制器、烤箱温度控制、食品加工、工业炉等领域。通过DS18B20这种数字温度传感器来进行温度采集,使用STC89C52这种常用的单片机控制芯片,配合4位共阳数码管实现温度数据显示,并通过按键设置温度上限阀值,一旦温度超过阀值,系统会触发蜂鸣器进行报警提示,从而保证了温度的精准控制和安全性。DS18B20是一种数字温度传感器,由Maxim Integrated公司生产。采用1-Wire总线接口,只需要一个数据线就可以同时实现数据传输和供电。主要特点是精度高、响应速度快、体积小、价格低廉,被广泛应用于各种温度测量场合。DS18B20可以测量的温度范围为-55℃~+125℃,精度为±0.5℃(在-10℃~+85℃范围内)。内部集成了温度传感器、A/D转换器和数字信号处理电路,可以直接输出数字温度值。DS18B20的工作原理是利用温度对半导体材料电阻值的影响,将温度转化为电阻值,再通过A/D转换器将电阻值转化为数字信号输出。1-Wire总线接口可以实现多个DS18B20传感器的串联,只需要一个控制器就可以同时读取多个传感器的温度数据。在热敏电阻测温系统中,可以使用DS18B20传感器来测量环境温度,并将温度值传输到控制器中进行处理和显示。下面是仿真图:二、设计思路2.1 系统架构系统硬件主要由单片机控制模块、温度传感器模块、数码管显示模块、按键模块、蜂鸣器模块组成。其中单片机控制模块采用STC89C52作为主控芯片,通过连接数码管、按键、蜂鸣器、温度传感器等外围电路实现温度检测、控制和报警功能。2.2 技术方案(1)温度传感器模块 本项目采用DS18B20数字式温度传感器进行温度检测,该传感器具有精度高、响应快、可靠性强等优点。通过将其与单片机进行串口通信,实现温度数据的采集。(2)数码管显示模块 本项目采用4位共阳数码管进行温度数据的显示,通过设置单片机控制IO口实现数据的动态扫描和显示。(3)按键模块 本项目通过设置按键模块实现对温度上限阀值的设定,采用矩阵按键实现多个按键功能。(4)蜂鸣器模块 本项目采用蜂鸣器作为报警提示器,当温度超过上限阀值时,触发单片机控制后,蜂鸣器会发出一定频率的报警信号。2.3 系统实现流程(1)主程序初始化:设置IO口模式、串口配置、定时器中断等参数。(2)温度检测:通过DS18B20进行温度采集,并将采集到的数据解析为实际温度值。(3)数码管显示:将温度值通过数码管进行数据的显示。(4)上限阀值设置:通过按键设置温度上限阀值,将阀值存储在单片机内部的EEPROM中。(5)报警提示:当温度值超过阀值时,触发蜂鸣器发出报警信号。三、代码实现3.1 4位共阳极数码管显示代码下面是控制STC89C52通过P1口控制4位共阳极数码管显示数字1234的实现代码: #include <reg52.h> // 定义数码管端口连接的IO口 sbit Dig1 = P1^0; sbit Dig2 = P1^1; sbit Dig3 = P1^2; sbit Dig4 = P1^3; // 定义数码管段码 unsigned char code SegCode[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; void main() { unsigned int num = 1234; // 要显示的数字 unsigned char i, j, k, l; // 分别表示千位、百位、十位和个位 while (1) { // 将数字分解为千位、百位、十位和个位 i = num / 1000; j = num % 1000 / 100; k = num % 100 / 10; l = num % 10; // 显示千位 Dig1 = 1; P0 = SegCode[i]; Dig1 = 0; // 显示百位 Dig2 = 1; P0 = SegCode[j]; Dig2 = 0; // 显示十位 Dig3 = 1; P0 = SegCode[k]; Dig3 = 0; // 显示个位 Dig4 = 1; P0 = SegCode[l]; Dig4 = 0; } }这段代码中,定义了数码管端口连接的IO口,然后定义了数码管段码。在main函数中,将要显示的数字1234分解为千位、百位、十位和个位,并通过控制P1口的四个IO口,依次显示出来。这里使用共阳极数码管,需要将对应位的IO口置为0才能点亮数码管。3.2 数字温度计实现代码下面是数字温度计完整的代码。 #include <reg52.h> // 定义温度传感器引脚 sbit DQ = P3^7; // 定义数码管引脚 sbit DIG_1 = P2^0; sbit DIG_2 = P2^1; sbit DIG_3 = P2^2; sbit DIG_4 = P2^3; sbit SEG_A = P1^0; sbit SEG_B = P1^1; sbit SEG_C = P1^2; sbit SEG_D = P1^3; sbit SEG_E = P1^4; sbit SEG_F = P1^5; sbit SEG_G = P1^6; sbit SEG_DP = P1^7; // 定义按键引脚 sbit KEY_SET = P0^0; sbit KEY_ADD = P0^1; sbit KEY_SUB = P0^2; // 定义全局变量 unsigned char code DisplayChar[] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; // 数码管显示字符编码 unsigned char TempData[4] = {0, 0, 0, 0}; // 显示温度值的数组 unsigned char SetTemp = 25; // 设定温度上限阀值 unsigned char LastKeyStatus = 0x07; // 按键状态 unsigned char Count = 0; // 数码管扫描计数器 bit IsAlarm = 0; // 报警状态 // 定时器中断服务函数 void Timer0_ISR() interrupt 1 { TH0 = 0xfc; TL0 = 0x67; DIG_1 = DIG_2 = DIG_3 = DIG_4 = 1; // 关闭所有数码管 Count++; // 数码管扫描计数器加1 switch (Count) { case 1: // 扫描第1位数码管 DIG_1 = 0; P0 = TempData[3]; break; case 2: // 扫描第2位数码管 DIG_2 = 0; P0 = TempData[2]; break; case 3: // 扫描第3位数码管 DIG_3 = 0; P0 = TempData[1]; break; case 4: // 扫描第4位数码管 DIG_4 = 0; P0 = TempData[0]; break; default: Count = 0; break; } } // 延时函数 void Delay(unsigned int n) { unsigned int i, j; for(i=0; i<n; i++) { for(j=0; j<125; j++); } } // 数字温度计初始化函数 void Init() { TMOD |= 0x01; // 定时器0工作在模式1 TH0 = 0xfc; // 定时器0初始值 TL0 = 0x67; ET0 = 1; // 允许定时器0中断 TR0 = 1; // 启动定时器0 EA = 1; // 允许中断 } // DS18B20复位函数 bit Reset() { bit res; DQ = 0; Delay(480); DQ = 1; Delay(60); res = DQ; Delay(420); return res; } // DS18B20写字节函数 void WriteByte(unsigned char dat) { unsigned char i; for(i=0; i<8; i++) { DQ = 0; Delay(2); DQ = dat & 0x01; Delay(60); DQ = 1; Delay(2); dat >>= 1; } } // DS18B20读字节函数 unsigned char ReadByte() { unsigned char i, j, dat = 0; for(i=0; i<8; i++) { DQ = 0; Delay(2); DQ = 1; Delay(2); j = DQ; Delay(60); dat |= (j << i); } return dat; } // DS18B20温度转换函数 void TempConv() { if(!Reset()) { WriteByte(0xCC); // 跳过ROM操作,直接访问DS18B20 WriteByte(0x44); // 发送温度转换命令 } } // DS18B20读取温度函数 void ReadTemp() { unsigned char TL, TH; if(!Reset()) { WriteByte(0xCC); // 跳过ROM操作,直接访问DS18B20 WriteByte(0xBE); // 发送读取温度命令 TL = ReadByte(); // 读取温度值低8位 TH = ReadByte(); // 读取温度值高8位 if(TH > 7) { // 温度值为负数,进行补码转换 TH = ~TH; TL = ~TL; TempData[0] = ((unsigned short)(TH << 8) | TL) * -0.0625 * 10 + 0.5; // 计算温度值并保存 TempData[1] = DisplayChar[10]; // 显示字符“-” } else { // 温度值为正数 TempData[0] = ((unsigned short)(TH << 8) | TL) * 0.0625 * 10 + 0.5; // 计算温度值并保存 TempData[1] = DisplayChar[TempData[0] / 10]; // 显示整数部分 } TempData[2] = DisplayChar[TempData[0] % 10]; // 显示小数部分 } } // 按键检测函数 void KeyCheck() { unsigned char key_status = 0; if(KEY_SET == 0) { // 设定按键被按下 key_status |= 0x01; } if(KEY_ADD == 0) { // 加温按键被按下 key_status |= 0x02; } if(KEY_SUB == 0) { // 减温按键被按下 key_status |= 0x04; } if(key_status != LastKeyStatus) { // 判断是否有按键事件发生 Delay(10); // 延时去抖 if(key_status != LastKeyStatus) { // 再次判断是否有按键事件发生 switch(key_status) { case 0x01: // 设定按键被按下 SetTemp++; // 温度上限阀值加1 if(SetTemp > 50) { // 上限阀值不能超过50℃ SetTemp = 50; } break; case 0x02: // 加温按键被按下 break; case 0x04: // 减温按键被按下 break; default: break; } } LastKeyStatus = key_status; // 保存当前按键状态 } } // 报警函数 void Alarm() { if(TempData[0] > SetTemp * 10 && !IsAlarm) { // 当温度超过设定的阀值且没有报警时触发报警 IsAlarm = 1; // 设置报警标志 while(TempData[0] > SetTemp * 10) { // 循环等待 P1 = 0xff; // 关闭数码管 P0 = 0x00; // 关闭蜂鸣器 Delay(500); // 延时 P1 = 0x00; // 打开数码管 P0 = 0xff; // 打开蜂鸣器 Delay(500); // 延时 } } else if(TempData[0] <= SetTemp * 10) { // 当温度低于等于设定的阀值时,取消报警 IsAlarm = 0; // 清除报警标志 } } // 主函数 void main() { Init(); // 初始化数字温度计 while(1) { TempConv(); // 温度转换 ReadTemp(); // 读取温度值 KeyCheck(); // 按键检测 Alarm(); // 报警处理 } }这份代码的设计主要分为4个模块:(1)数码管显示模块:使用四位共阴数码管进行温度值的显示,采用定时中断扫描四个数码管的方式进行显示。(2)DS18B20模块:通过DS18B20温度传感器获取当前温度值,并将温度值保存到数组中,以便于数码管显示模块进行显示。(3)按键检测模块:通过检测按键状态,实现设定温度上限阀值、加温和减温等操作。(4)报警模块:当当前温度超过设定的温度上限阀值时,触发蜂鸣器报警。代码主要使用51单片机进行设计,其中主要包含了DS18B20温度传感器的读取、按键检测、数码管显示、蜂鸣器控制等多种功能。通过使用定时中断和循环结构,实现了各个模块之间的协作,从而一同完成数字温度计的设计。
-
一、项目介绍当前文章介绍基于51单片机的热敏电阻测温系统的设计过程,用于实时监测环境温度,并在温度超过预设阈值时进行报警。由于采用的是热敏电阻测温技术,无需外置温度传感器,使得系统具有结构简单、成本较低等优点。主控芯片采用STC89C52,具有良好的稳定性和可靠性,适应于工业控制等领域的应用需要。ADC采集模块采用PCF8591模块,可方便地实现对热敏电阻温度数据的转换和采集,提高了系统的准确度和实用性。系统通过4位数码管显示出温度值,同时通过按键设置温度上限阀值,当温度超过阀值时,会通过蜂鸣器报警,提醒用户注意环境温度的变化情况。在项目中主要是用到了热敏电阻和PCF8591模块。(1)热敏电阻介绍热敏电阻(Thermistor)是一种基于材料的电阻元件,其电阻值随温度的变化而发生相应的变化。通常情况下,热敏电阻的电阻值随温度升高而降低,反之则随温度降低而升高,这种特性被称为负温度系数(NTC)或正温度系数(PTC)。热敏电阻的工作原理是基于材料的温度敏感性质。在热敏电阻中,存在许多导电粒子,当温度升高时,导电粒子与材料中的离子激发程度增强,导致导电粒子的数量变多,因此电阻值降低;反之,当温度降低时,导电粒子的数量变少,电阻值增加。(2)PCF8591PCF8591是一款4通道、8位模数转换器(ADC)和1通道、8位数模转换器(DAC)的集成电路芯片。可以通过I2C总线与微控制器进行通信,实现模拟信号的输入和输出。PCF8591的输入电压范围为0V~VCC(通常为5V),可以通过外部电阻进行放大或缩小。它还有一个内部参考电压源,可以通过软件控制选择使用。PCF8591的输出电压范围也是0V~VCC,可以用于控制模拟信号的输出,比如控制电机的转速、LED的亮度等。在热敏电阻测温系统中,使用PCF8591模块来采集热敏电阻的电压信号,并将其转换为数字信号,进而计算出温度值。二、设计思路2.1 系统结构系统采用单片机作为主控芯片,热敏电阻用于测量环境温度,PCF8591模块采集热敏电阻的温度数据并将其转换为正常温度值,通过数码管进行显示。同时,系统设置上限阀值,当温度超过该值时,系统会通过蜂鸣器报警。2.2 硬件设计(1)主控芯片本系统采用STC89C52单片机作为主控芯片,具有强大的计算能力、稳定的性能和较低的功耗,支持多种外设接口,适合于工业控制等领域的应用需求。(2)温度传感器本系统采用热敏电阻作为温度传感器,其结构简单、价格便宜,且无需额外的电源供应,可直接通过PC8591模块的输入端口进行检测。(3)ADC采集模块系统采用PCF8591模块进行ADC采集,具有4路模拟输入通道和一个模拟输出通道,采样精度高达8位,能够满足本系统对温度信号的准确采集需求。(4)数码管显示模块系统采用4位共阳数码管进行数据显示,其显示范围为-999~+9999,可满足本系统对温度数据的实时显示需求。(5)蜂鸣器报警模块系统采用蜂鸣器进行报警提示,当温度超过预设阈值时,蜂鸣器会发出持续声响,提醒用户注意环境温度的变化情况。(6)按键模块按键模块,方便用户进行阀值的设置和调整操作。2.3 软件设计(1)温度采集与转换系统使用ADC采集热敏电阻的温度信号,并将采集到的数字信号转换成温度值进行显示。转换公式为: T=(adc_value/255.0)*330,其中adc_value为AD转换器输出的数字值,330是热敏电阻的参考电阻值。(2)温度上限阀值设置系统通过按键实现温度上限阀值的设置和调整操作,用户可以根据自己的需求进行设定。(3)报警功能设计系统在采集到温度超过预设阈值时,蜂鸣器会发出声响进行提醒,并且LED指示灯会亮起。2.4 总体流程(1)初始化各个模块,包括单片机、PCF8591、数码管、蜂鸣器和按键等。(2)采集热敏电阻的温度信号,并将数字信号转换为温度值。(3)将温度值通过数码管进行显示。(4)检测当前温度是否超过预设阈值,若超过,则触发报警并点亮LED指示灯。(5)用户可以通过按键设置温度上限阀值,系统会保存设置的阈值并进行下一次温度比较。三、代码实现以下是基于51单片机设计的热敏电阻测温系统的实现代码。 #include <reg52.h> #include <intrins.h> typedef unsigned char u8; typedef unsigned int u16; #define PCF8591_address_write 0x90 #define PCF8591_address_read 0x91 sbit SCLK = P1^0; //PCF8591模块时钟线 sbit DOUT = P1^1; //PCF8591模块数据线 sbit DIN = P1^2; //PCF8591模块数据线 sbit CS = P1^3; //PCF8591模块片选线 sbit LATCH1 = P3^4; //锁存器1 sbit LATCH2 = P3^5; //锁存器2 sbit KEY1 = P2^0; //按键1 sbit KEY2 = P2^1; //按键2 sbit BUZZ = P2^3; //蜂鸣器 u16 ADC_value; //采集到的ADC值 float temperature; //计算得到的温度值 u8 table[] = { //共阳数码管段码表 0xc0, //0 0xf9, //1 0xa4, //2 0xb0, //3 0x99, //4 0x92, //5 0x82, //6 0xf8, //7 0x80, //8 0x90, //9 0xbf, //- }; void delay(u16 i){ while(i--); } void delay_ms(u16 ms){ u16 i, j; for(i=0; i<ms; i++){ for(j=0; j<110; j++); } } void write_PCF8591(u8 data){ u8 i; DIN = 1; SCLK = 0; CS = 0; for(i=0; i<8; i++){ DOUT = (data & 0x80) >> 7; data <<= 1; SCLK = 1; SCLK = 0; } CS = 1; } u16 read_ADC(){ u16 value; CS = 0; DIN = 1; SCLK = 0; DIN = 0; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); SCLK = 1; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); value = P1; SCLK = 0; value <<= 8; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); SCLK = 1; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); value |= P1; CS = 1; return value; } void display(u8 addr, u8 dat){ LATCH1 = 0; LATCH2 = 0; P0 = addr; LATCH1 = 1; LATCH1 = 0; P0 = table[dat]; LATCH2 = 1; LATCH2 = 0; } void main(){ u8 set_value = 40; //温度上限阀值 u8 temp; write_PCF8591(0x40); while(1){ ADC_value = read_ADC(); temperature = (float)ADC_value * 330 / 255; temperature -= 50; if(temperature > set_value){ //温度超过上限阀值,触发报警 BUZZ = 1; } else{ BUZZ = 0; } if(temperature < -99){ temp = '-'; display(0x00, temp); display(0x01, 10); display(0x02, 10); display(0x03, 10); } else if(temperature < 0){ temp = '-'; display(0x00, temp); temp = ~(int)temperature + 1; display(0x01, temp/10); display(0x02, temp%10); display(0x03, 11); //小数点 } else if(temperature > 999){ display(0x00, 10); display(0x01, 9); display(0x02, 9); display(0x03, 9); } else{ display(0x00, temperature/100); display(0x01, temperature/10%10); display(0x02, temperature%10); display(0x03, 11); //小数点 } if(KEY1 == 0){ //按键1按下,增加上限阀值 delay_ms(10); if(KEY1 == 0){ set_value++; while(!KEY1); } } if(KEY2 == 0){ //按键2按下,减小上限阀值 delay_ms(10); if(KEY2 == 0){ set_value--; while(!KEY2); } } delay_ms(10); } }代码中采用了共阳数码管,通过采集热敏电阻产生的电压值,计算得到环境温度值,再通过数码管进行显示;当温度超过设定的上限值时,会触发蜂鸣器报警。可以通过按键对温度上限阀值进行设置和调整操作。
-
一、项目介绍井下瓦斯监控系统是煤矿安全生产中非常重要的一部分,防止井下瓦斯爆炸事故的发生,保障煤矿工人的人身安全。由于地下环境特殊,需要特殊的监测系统来实时监测瓦斯浓度等关键指标,并及时报警以便采取措施进行处理。瓦斯气体,又称沼气,是一种轻质烃类气体,主要成分是甲烷(CH4),也包含少量的乙烷、丙烷等。它是在地下煤炭层与泥岩等岩石中通过微生物作用或者煤炭化学反应形成的。在煤矿等地下工程中,瓦斯常常是一种具有危险性的气体,如果采取不当的措施,就有可能发生瓦斯爆炸事故。基于51单片机的井下瓦斯监控系统,可以通过传感器检测瓦斯气体浓度,将检测到的数据通过AD转换后送入单片机处理,再通过LCD显示器显示出来。如果瓦斯浓度超过了预设阈值,系统会自动启动报警装置进行警示。同时,这种系统具有适用面广、成本低、可靠性高等特点。在目前环保意识提高的背景下,煤炭企业和政府对于井下瓦斯监控系统的需求越来越大,系统的市场潜力巨大。二、设计原理2.1 传感器选型(1)瓦斯气体检测MQ2传感器是一种常用于气体检测的半导体传感器,主要用于检测多种易燃、易爆气体,如瓦斯、丙烷、液化气等。它采用了半导体氧化物层敏感元件技术,当检测到目标气体时,其电阻值会发生变化,从而可以通过测量电阻值的变化来检测目标气体的浓度。MQ2传感器具有灵敏度高、响应速度快、使用方便等特点,因此在气体检测领域广泛应用。MQ2传感器包括热敏电阻、电化学传感器、半导体敏感元件等部分,其中半导体敏感元件是其核心部件,也是影响传感器性能的关键因素。在使用前需要进行预热处理,一般预热时间为1-2分钟,然后将待测气体与传感器接触,即可读取传感器的输出信号并进行浓度计算。(2)ADC采集模块PCF8591模块是一种集成了AD转换器和DA转换器的模块,通过I2C总线可以连接到单片机或其他电子设备上,用于模拟信号的输入和输出。其主要特点是集成度高、精度高、使用方便、成本低廉等。模块由PCF8591芯片和相关外围电路组成,其中PCF8591芯片是一个具有4个模拟输入通道和1个模拟输出通道的集成电路,内部集成了128级AD转换器和8位DA转换器,并且支持外部基准电压输入。同时,该模块还包括4个可变电阻,可以通过调节来改变模拟输入通道的电阻值,从而实现对信号的增益和衰减。通过I2C总线,可以方便地读取和输出模拟信号。在实际应用中,PCF8591模块广泛用于传感器信号的采集和处理,例如温度、光强、声音等信号的转换和传输。2.2 设计思路基于51单片机设计的井下瓦斯监控系统的原理如下:(1)传感器检测瓦斯浓度:使用瓦斯传感器检测井下瓦斯浓度,并将检测结果转换为电信号输出。(2)单片机采集数据:使用ADC模块将传感器输出的电信号转换为数字信号,并将其存储到单片机内部的RAM中。(3)数据处理:单片机通过对采集到的数据进行处理,可以实现瓦斯浓度的实时监测,并根据预设阈值进行报警处理。(4)报警处理:当瓦斯浓度超过预设阈值时,单片机会触发报警器进行报警。同时,可以通过OLED显示屏实时显示瓦斯浓度,并通过蜂鸣器发出警报声音。(5)数据存储:单片机还可以将采集到的数据存储到外部存储器中,以便后续的数据分析和处理。基于51单片机设计的井下瓦斯监控系统通过传感器检测瓦斯浓度,单片机采集数据并进行处理,实现了对瓦斯浓度的实时监测和报警处理,同时还可以将数据存储到外部存储器中,方便后续的数据分析和处理。三、代码实现3.1 采集MQ2浓度打印到串口以下是基于STC89C52通过PCF8591采集MQ2烟雾传感器的值,并转为浓度打印到串口的详细代码。 #include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int sbit SCL=P1^0; // I2C时钟线 sbit SDA=P1^1; // I2C数据线 sbit MQ2=P3^2; // MQ2烟雾传感器 // 函数声明 void delay(uint n); void I2C_Start(); void I2C_Stop(); void I2C_SendACK(bit ack); bit I2C_RecvACK(); void I2C_SendByte(uchar dat); uchar I2C_RecvByte(); void PCF8591_Write(uchar addr, uchar dat); uchar PCF8591_Read(uchar addr); void InitPCF8591(); uint ReadMQ2(); // 主函数 void main() { InitPCF8591(); // 初始化PCF8591 while(1) { uint mq2val = ReadMQ2(); // 读取MQ2传感器的值 float mq2con = (mq2val / 255.0) * 100.0; // 将传感器的值转换为浓度 printf("MQ2烟雾浓度:%f%%\n", mq2con); // 打印浓度到串口 delay(1000); // 延时1秒 } } // 延时函数 void delay(uint n) { uint i, j; for(i = 0; i < n; i++) for(j = 0; j < 125; j++); } // I2C总线函数 // I2C起始信号 void I2C_Start() { SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 0; _nop_(); _nop_(); _nop_(); SCL = 0; } // I2C停止信号 void I2C_Stop() { SDA = 0; SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 1; _nop_(); _nop_(); _nop_(); } // I2C发送应答信号 void I2C_SendACK(bit ack) { SDA = ack; SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; } // I2C接收应答信号 bit I2C_RecvACK() { SCL = 1; _nop_(); _nop_(); _nop_(); bit ack = SDA; SCL = 0; return ack; } // I2C发送一个字节 void I2C_SendByte(uchar dat) { uchar i; for(i = 0; i < 8; i++) { SDA = (dat & 0x80) >> 7; dat <<= 1; SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; } I2C_RecvACK(); } // I2C接收一个字节 uchar I2C_RecvByte() { uchar i, dat = 0; for(i = 0; i < 8; i++) { dat <<= 1; SCL = 1; _nop_(); _nop_(); _nop_(); dat |= SDA; SCL = 0; } I2C_SendACK(1); return dat; } // PCF8591函数 // 初始化PCF8591 void InitPCF8591() { PCF8591_Write(0x40, 0x00); // 设置PCF8591控制字节,模拟输入通道为0 } // 向PCF8591写入一个字节 void PCF8591_Write(uchar addr, uchar dat) { I2C_Start(); // 发送起始信号 I2C_SendByte(0x90); // 发送设备地址,并写入模式 I2C_RecvACK(); I2C_SendByte(addr); // 发送寄存器地址 I2C_RecvACK(); I2C_SendByte(dat); // 发送数据 I2C_RecvACK(); I2C_Stop(); // 发送停止信号 } // 从PCF8591读取一个字节 uchar PCF8591_Read(uchar addr) { uchar dat; I2C_Start(); // 发送起始信号 I2C_SendByte(0x90); // 发送设备地址,并写入模式 I2C_RecvACK(); I2C_SendByte(addr); // 发送寄存器地址 I2C_RecvACK(); I2C_Start(); // 发送起始信号 I2C_SendByte(0x91); // 发送设备地址,并读取数据 I2C_RecvACK(); dat = I2C_RecvByte(); // 读取数据 I2C_SendACK(1); I2C_Stop(); // 发送停止信号 return dat; } // 读取MQ2传感器的值 uint ReadMQ2() { uchar val = PCF8591_Read(0x40); // 读取PCF8591的模拟输入值 if(MQ2 == 0) // 如果MQ2传感器检测到烟雾 return (uint)(val * 2.55); // 返回模拟输入值的百分比 else return 0; // 否则返回0 }上面代码里,主要包括了I2C总线函数和PCF8591函数,用于与PCF8591芯片进行通信。其中,InitPCF8591()函数用于初始化PCF8591芯片,PCF8591_Write()函数用于向PCF8591芯片写入数据,PCF8591_Read()函数用于从PCF8591芯片读取数据。另外,ReadMQ2()函数用于读取MQ2传感器的值,并将其转换为浓度值。最后,在主函数中,通过调用ReadMQ2()函数读取MQ2传感器的值,并将其转换为浓度值,然后通过printf()函数将浓度值打印到串口。3.2 采用烟雾浓度显示到OLED下面代码是STC89C52通过PCF8591采集MQ2烟雾传感器的值,并转为浓度显示到IIC接口的OLED显示屏上。 #include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int typedef enum { false = 0, true = !false } bool; sbit SCL=P3^6; //IIC总线时钟引脚定义 sbit SDA=P3^7; //IIC总线数据引脚定义 sbit LED = P1^0; // 测试用LED灯,可选 uchar code tabel[]="Smog: "; //OLED屏幕显示内容 uchar code dis[]="%"; /*------------------函数声明------------------*/ void delayms(uint); //毫秒延时函数 void IIC_Start(); //IIC起始信号 void IIC_Stop(); //IIC停止信号 void IIC_Send_Byte(uchar); //发送一个字节 uchar IIC_Read_Byte(bool); //读取一个字节 void LCD_SetPos(uchar,uchar); //设置位置 void LCD_WriteCmd(uchar); //写命令 void LCD_WriteData(uchar); //写数据 void LCD_Init(); //初始化 void MQ2_Init(); //MQ2传感器初始化 int MQ2_Read(); //读取MQ2传感器的值并返回浓度值 /*------------------主函数------------------*/ void main() { uchar i=0,j=0,k=0; int result = 0; //存储MQ2传感器读取的值 uchar buf[5] = {0}; //存储MQ2浓度值字符串 uchar MQ2_data[10]={0}; //存储OLED屏幕显示数据 EA = 1; //开放总中断 IIC_Init(); LCD_Init(); MQ2_Init(); while(1) { result = MQ2_Read(); //读取MQ2传感器的值 if(result >= 0) //读取成功 { itoa(result,buf,10); //将读取结果转为字符串格式 for(i=0;i<6;i++) //将OLED显示内容清空,准备写入新数据 MQ2_data[i]=0; for(i=0;i<6;i++) //拼接OLED显示内容 { if(tabel[i]!=0) //判断是否有显示内容 MQ2_data[i]=tabel[i]; else break; } j=0; //记录MQ2字符串长度 while(buf[j]!=0 && i+j<7) //拼接MQ2浓度值字符串,最多显示5位数 { MQ2_data[i+j] = buf[j]; j++; } if(j<5) //MQ2浓度值不足5位数字,换行再拼接“%”字符 { MQ2_data[i+j] = '\n'; MQ2_data[i+j+1] = dis[0]; } for(k=0;k<i+j+2;k++) //将OLED屏幕显示MQ2浓度值 { LCD_WriteData(MQ2_data[k]); } delayms(1000); //延时1秒(可根据实际需求调整) } } } /*------------------IIC总线控制函数------------------*/ void IIC_Init() { SCL = 1; //初始化,总线空闲状态时SCL和SDA都为高电平 SDA = 1; } void IIC_Start() { SDA = 1; _nop_(); SCL = 1; _nop_(); SDA = 0; //起始信号的形成:在SCL高电平期间,SDA从高电平转为低电平 _nop_(); SCL = 0; } void IIC_Stop() { SDA = 0; _nop_(); SCL = 1; _nop_(); SDA = 1; //停止信号的形成:在SCL高电平期间,SDA从低电平转到高电平 _nop_(); } void IIC_Send_Byte(uchar dat) { uchar i; for(i=0;i<8;i++) { SDA = dat & 0x80; dat <<= 1; SCL = 1; _nop_(); SCL = 0; _nop_(); } } uchar IIC_Read_Byte(bool ack) { uchar i,dat=0; for(i=0;i<8;i++) { SCL = 1; _nop_(); dat <<= 1; dat |= SDA; SCL = 0; _nop_(); } if(ack) SDA = 0; //发送ACK else SDA = 1; //不发送ACK SCL = 1; _nop_(); SCL = 0; _nop_(); SDA = 1; return dat; } /*------------------OLED屏幕控制函数------------------*/ void LCD_SetPos(uchar x,uchar y) { LCD_WriteCmd(0xb0+y); LCD_WriteCmd(((x&0xf0)>>4)|0x10); LCD_WriteCmd((x&0x0f)|0x00); } void LCD_WriteCmd(uchar cmd) { IIC_Start(); IIC_Send_Byte(0x78); IIC_Send_Byte(0x00); //写命令 IIC_Send_Byte(cmd); IIC_Stop(); } void LCD_WriteData(uchar dat) { IIC_Start(); IIC_Send_Byte(0x78); IIC_Send_Byte(0x40); //写数据 IIC_Send_Byte(dat); IIC_Stop(); } void LCD_Init() { LCD_WriteCmd(0xae); LCD_WriteCmd(0x00); LCD_WriteCmd(0x10); LCD_WriteCmd(0x40); LCD_WriteCmd(0xb0); LCD_WriteCmd(0x81); LCD_WriteCmd(0xcf); LCD_WriteCmd(0xa1); LCD_WriteCmd(0xa6); LCD_WriteCmd(0xa8); LCD_WriteCmd(0x3f); LCD_WriteCmd(0xc8); LCD_WriteCmd(0xd3); LCD_WriteCmd(0x00); LCD_WriteCmd(0xd5); LCD_WriteCmd(0x80); LCD_WriteCmd(0xd9); LCD_WriteCmd(0xf1); LCD_WriteCmd(0xda); LCD_WriteCmd(0x12); LCD_WriteCmd(0xdb); LCD_WriteCmd(0x40); LCD_WriteCmd(0x20); LCD_WriteCmd(0x02); LCD_WriteCmd(0xaf); LCD_WriteCmd(0xff); } /*------------------MQ2传感器控制函数------------------*/ void MQ2_Init() { IIC_Start(); IIC_Send_Byte(0x90); //写入设备地址 1001A2A1A0(0) R/W = 0(PCF8591 写操作) IIC_Send_Byte(0x40); //写入控制字节,选择通道0,并开启模拟转换器 IIC_Stop(); } int MQ2_Read() { int result=0; uchar buf[10]={0}; IIC_Start(); IIC_Send_Byte(0x90); //写入设备地址 IIC_Send_Byte(0x41); //读取数据 result = IIC_Read_Byte(true)*256; //读取高位数据 result += IIC_Read_Byte(true); //读取低位数据 IIC_Stop(); if(result < 0) return -1; //读取失败 else return result; //返回读取的值 } /*------------------辅助函数------------------*/ void delayms(uint n) { uint i, j; for(i=0;i<n;i++) for(j=0;j<114;j++); }
-
一、项目介绍花样流水灯是一种常见的LED灯效果,被广泛应用于舞台表演、节日庆典、晚会演出等场合。在现代智能家居、电子产品中,花样流水灯也被广泛使用,通过调整亮灭顺序和时间,可以实现各种炫酷的灯光效果,增强用户体验。而51单片机作为一种常见的嵌入式开发平台,具有体积小、功耗低、可编程性强等优点,非常适合用于开发花样流水灯及其他嵌入式应用。以下场景中流水灯得到了广泛的应用:舞台表演:花样流水灯可用于舞台背景、音乐MV等场合,配合音乐和舞蹈,营造出炫酷、动感的视觉效果。节日庆典:在传统节日如春节、中秋节等场合,花样流水灯可以用于灯笼、彩灯等装饰,为节日增添喜庆氛围。晚会演出:在各种晚会、派对、聚会等场合,花样流水灯可以用于舞台效果、音乐灯光秀等,增强整个活动的氛围和趣味性。智能家居:花样流水灯可以使用在居家灯光控制中,实现远程控制、定时开关、自动调节等功能,提升居住环境的科技感和人性化。二、设计原理2.1 基本原理花样流水灯是一种常见的LED灯效果,通常由多个LED灯组成,通过控制每个LED灯的亮灭顺序和时间,实现花样流水灯的效果。在51单片机中,可以使用定时器和端口控制来实现这一效果。2.2 硬件搭建为了实现花样流水灯,需要将多个LED灯按照一定的顺序连接到51单片机的IO引脚上。电路设计上,为每个LED灯配备一个电阻,并将它们连接到5V电源引脚和地线上。具体硬件搭建方法如下:(1)将多个LED灯依次连接起来组成一个电路链,将第一个LED的正极接到P1.0口,第二个LED的正极接到P1.1口,第三个LED的正极接到P1.2口,以此类推,一共连接7个LED灯。(2)为每个LED灯配备一个适当的电阻,用来限制电流,防止损坏LED。(3)将每个LED的负极连接到5V电源引脚附近的地线上,形成一个完整的电路。2.3 软件实现在软件实现上,使用51单片机的定时器和端口控制来控制LED灯的亮灭顺序和时间。具体方法如下:(1)设置一个计数器变量count,用来保存当前亮起的LED灯的编号(从0开始)。(2)在定时器中断处理函数中,每次计数器溢出时,将当前亮起的LED灯熄灭,并将count加1;当count等于LED灯总数时,将count重置为0。(3)然后,再将下一个LED灯亮起,以此类推。(4)通过控制定时器的计数周期和每个灯亮起的时间,可以调整花样流水灯的效果。三、代码实现3.1 流水灯代码实现1 #include <reg52.h> #define LED_NUM 8 // LED灯总数 #define TIMER_TICK 500 // 定时器计数初值,控制亮灭时间 #define HIGH 0 // 高电平 #define LOW 1 // 低电平 unsigned int count = 0; // 定时器中断处理函数 void TimerInterrupt() interrupt 1 { static unsigned long tick = 0; tick++; if (tick >= TIMER_TICK) { P1 &= ~(1 << count); // 熄灭当前LED count++; // 切换到下一个LED if (count >= LED_NUM) { count = 0; // 重置计数器 } P1 |= (1 << count); // 亮起下一个LED tick = 0; // 重置计时器 } } // 主函数 void main() { unsigned int i; P1 = 0xFF; // 所有IO口初始化为高电平 TMOD |= 0x01; // 定时器0,模式1,16位自动重载 TH0 = (65536 - TIMER_TICK) / 256; TL0 = (65536 - TIMER_TICK) % 256; ET0 = 1; // 定时器中断允许 EA = 1; // 总中断允许 TR0 = 1; // 定时器开始计数 while (1) { // 等待中断事件 } }3.2 流水灯实现效果2【1】逐个点亮 #include <reg52.h> void Delay(unsigned int t) // 延时函数 { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 125; j++); } void main() { while (1) { unsigned char i; // 定义计数器i for (i = 0; i < 8; i++) // 循环8次,依次点亮LED灯 { P0 = ~(1 << i); // 通过位运算生成控制信号,输出到P0口,控制LED灯点亮 Delay(500); // 延时500ms } } }【2】逐个熄灭 #include <reg52.h> void Delay(unsigned int t) // 延时函数 { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 125; j++); } void main() { while (1) { unsigned char i; // 定义计数器i for (i = 7; i < 8; i--) // 循环8次,依次熄灭LED灯 { P0 = ~(1 << i); // 通过位运算生成控制信号,输出到P0口,控制LED灯熄灭 Delay(500); // 延时500ms } } }【3】来回流动 #include <reg52.h> void Delay(unsigned int t) // 延时函数 { unsigned int i, j; for (i = 0; i < t; i++) for (j = 0; j < 125; j++); } void main() { while (1) { unsigned char i; // 定义计数器i for (i = 0; i < 8; i++) // 循环8次,依次点亮LED灯 { P0 = ~(1 << i); // 通过位运算生成控制信号,输出到P0口,控制LED灯点亮 Delay(500); // 延时500ms } for (i = 6; i > 0; i--) // 循环6次,依次熄灭LED灯 { P0 = ~(1 << i); // 通过位运算生成控制信号,输出到P0口,控制LED灯熄灭 Delay(500); // 延时500ms } } }3.3 闪光灯的实现下面是三个不同的闪光灯效果的代码,分别为常亮、快闪和慢闪。【1】常亮闪光灯 #include <reg52.h> sbit LED = P1 ^ 0; void main() { while (1) { LED = 0; // LED常亮 } }【2】快闪闪光灯 #include <reg52.h> sbit LED = P1 ^ 0; void delay(unsigned int i) { while (i--); } void main() { while (1) { LED = 0; // LED亮 delay(50000); // 延时一段时间 LED = 1; // LED灭 delay(50000); // 延时一段时间 } }【2】慢闪闪光灯 #include <reg52.h> sbit LED = P1 ^ 0; void delay(unsigned int i) { while (i--); } void main() { while (1) { LED = 0; // LED亮 delay(100000); // 延时一段时间 LED = 1; // LED灭 delay(100000); // 延时一段时间 } }以上三个代码中,都使用了P1口的第0位来控制LED灯的亮灭。其中,第一个代码是常亮闪光灯,只需要将LED置为0。第二个代码是快闪闪光灯,使用了一个delay函数来实现延时,每次延时50000个时钟周期,即约为500ms。第三个代码是慢闪闪光灯,与第二个代码类似,只是将延时时间改为了100000个时钟周期,即约为1s。
-
一、项目介绍呼吸灯是一种常见的LED灯光效果,它可以模拟人类呼吸的变化,使灯光看起来更加柔和和自然。51单片机是一种广泛使用的微控制器,具有体积小、功耗低、成本低等优点,非常适合用于控制LED呼吸灯。本项目的呼吸灯将使用PWM(脉冲宽度调制)技术控制LED亮度,从而实现呼吸灯的效果。在本项目中,将使用51单片机作为主控制器,通过编程实现呼吸灯的控制。将使用C语言编写代码,并使用Keil C51集成开发环境进行编译和调试。使用Proteus仿真软件进行电路设计和仿真,确保电路的正确性和稳定性。二、设计原理2.1 PWM技术PWM是脉冲宽度调制(Pulse Width Modulation)的缩写,是一种通过改变脉冲宽度来控制电路的技术。在数字电路中,PWM是一种非常常见的技术,它可以用来控制电机、LED灯等电子设备的亮度、速度等参数。PWM技术的基本原理是通过控制脉冲的宽度和周期来控制电路的输出。在一个PWM周期内,电路会以一定的频率(也就是PWM频率)产生一系列脉冲,每个脉冲的宽度和高电平时间占整个周期的比例是由控制器根据需要设定的。通过这种方式,可以实现对电路输出的精确控制。在LED呼吸灯项目中,使用定时器模拟PWM技术可以实现呼吸灯效果。具体来说,就是通过定时器产生一定频率的脉冲信号,然后通过改变脉冲的占空比来控制LED灯的亮度。当脉冲的占空比逐渐增大时,LED灯的亮度也会逐渐增强,直到达到最大亮度;当脉冲的占空比逐渐减小时,LED灯的亮度也会逐渐减弱,直到最终熄灭。这样就可以实现类似于人类呼吸的渐变效果。2.2 呼吸灯原理呼吸灯是一种将 LED 灯光做成渐变效果的技术,可以让 LED 的亮度在一定时间内慢慢地增加和减小,使得 LED 的亮度变化更加自然和柔和,适合用于需要渐变效果的场景,如灯光调节、音响节拍等。呼吸灯的原理是通过改变 LED 的 PWM 信号的占空比来控制 LED 的亮度。PWM(Pulse Width Modulation,脉宽调制)是一种调节模拟信号幅度的常用技术,它通过改变信号的脉冲宽度来实现对信号幅度的调节。在呼吸灯中,PWM 信号的频率较高,而占空比则会随着时间的推移而逐渐变化,从而实现 LED 亮度的渐变效果。呼吸灯的实现通常需要使用一个定时器和一个 PWM 模块。定时器用来定时触发中断事件,在中断处理函数中改变 PWM 信号的占空比,从而控制 LED 的亮度。在定时器中断处理函数中,可以通过数学函数(如正弦、余弦等)或者简单的数值计算来得到不同的 PWM 占空比,实现不同的呼吸灯效果。2.3 51单片机51单片机是一种广泛使用的微控制器,具有体积小、功耗低、成本低等优点,非常适合用于控制LED呼吸灯。STC89C52是一种基于MCS-51内核的8位单片机,由中国的STC公司生产。具有高性价比、易于编程、广泛应用等特点,在工业控制、通信、家电控制等领域得到了广泛应用。STC89C52单片机的主要特点如下:采用MCS-51内核,具有8位数据总线和16位地址总线,可以访问64KB的程序存储器和64KB的数据存储器。内置12MHz的晶振,可以通过软件设置分频系数来获得不同的系统时钟频率。具有多种外设接口,包括UART、SPI、I2C、定时器、中断等,可以方便地实现各种应用。支持ISP(In-System Programming)编程方式,可以通过串口或并口进行在线编程,方便快捷。具有低功耗模式,可以通过软件设置进入不同的睡眠模式,以节省系统能耗。STC89C52单片机可以使用C语言或汇编语言进行编程,编写的程序可以通过编译器生成HEX文件,然后通过编程器烧录到芯片中。由于STC89C52单片机的广泛应用和丰富的资料,因此学习和使用它相对来说比较容易。三、代码实现3.1 自动呼吸灯因为STC89C52单片机没有PWM输出功能,只能使用延时函数实现,以下是基于STC89C52单片机实现呼吸灯效果的完整代码: #include <reg52.h> #define LED P1 void delay(unsigned int xms) { unsigned int i, j; for (i = xms; i > 0; i--) for (j = 110; j > 0; j--); } void main() { unsigned char i; while (1) { for (i = 0; i < 255; i++) { LED = i; delay(10); } for (i = 255; i > 0; i--) { LED = i; delay(10); } } }在这个代码中,使用了STC89C52单片机的P1口来控制LED灯的亮度。通过一个循环,让LED灯的亮度从0到255逐渐增加,再从255到0逐渐减小,这样就实现了呼吸灯的效果。在代码中,使用了一个delay函数来控制循环的速度。这个函数可以让程序延时一定的时间,从而控制LED灯的亮度变化速度。在这个代码中,设置了每次延时10毫秒,可以根据需要调整这个值来改变呼吸灯的效果。3.2 按键控制灯光亮度以下是基于STC89C52单片机的LED灯亮度控制完整代码,其中使用了两个按键分别控制LED的亮度和灭度。 #include <reg52.h> #define LED P1 sbit KEY_UP = P3 ^ 2; sbit KEY_DOWN = P3 ^ 3; unsigned char pwm = 0; void delay(unsigned int i) { while (i--); } void key_scan() { if (KEY_UP == 0) { delay(1000); if (KEY_UP == 0) { pwm += 10; if (pwm >= 100) { pwm = 100; } } } if (KEY_DOWN == 0) { delay(1000); if (KEY_DOWN == 0) { pwm -= 10; if (pwm <= 0) { pwm = 0; } } } } void main() { TMOD = 0x01; // 设置定时器0为模式1 TH0 = 0xFC; // 定时器初值,用于产生PWM信号的频率为50Hz TL0 = 0x67; TR0 = 1; // 启动定时器0 ET0 = 1; // 允许定时器0中断 EA = 1; // 开启总中断 while (1) { key_scan(); } } void timer0() interrupt 1 { static unsigned char cnt = 0; if (cnt >= 100) { cnt = 0; } if (cnt < pwm) { LED = 0; } else { LED = 1; } cnt++; }以上代码中,使用了定时器0来产生PWM信号,控制LED的亮度。使用了两个按键来调整LED的亮度和灭度。其中,KEY_UP按键用于增加LED的亮度,KEY_DOWN按键用于减小LED的亮度。在每次定时器中断时,根据pwm的值来控制LED的亮度。当cnt小于pwm时,LED为低电平,LED亮度较高;当cnt大于等于pwm时,LED为高电平,LED亮度较低。
-
一、项目介绍遥控器是现代生活中必不可少的电子产品之一,目前市面上的遥控器种类繁多,应用范围广泛。而 NEC 红外遥控器协议则是目前应用最为广泛的一种协议之一,几乎所有的电视、空调等家用电器都支持该协议。本项目是基于 51 单片机设计支持 NEC 协议的红外遥控器,实现接收解码和发送功能。用户通过按下相应按键进行信号的发射,红外发射二极管向外发射红外信号,被控制设备通过红外接收头接收到这个信号,然后解码执行相应的操作。二、硬件设计本项目所需的硬件器件主要包括:(1)5STC89C52单片机(2)红外发射管(3)红外接收头(4)OLED显示屏(5)按键开关三、软件设计本项目的程序代码采用 C 语言编写,主要分为三个部分:初始化部分、接收解码部分和发送数据部分。(1)初始化部分初始化函数主要完成各个端口的初始化和定时器的配置,以及红外接收头和红外发射管的引脚的配置。(2)接收解码部分接收解码函数主要采用计数器方式对红外遥控器发送的信号进行捕获,并将捕获到的信号转换成 NEC 码。然后根据 NEC 码的规定,解码出用户所输入的指令,最终实现控制设备的功能。(3)发送数据部分发送数据函数主要将单片机中存放的指令码进行编码,并通过红外发射管发送给被控制的设备。在该函数中,需要通过计时器的方式来调节发送信号的时间和频率,以保证信号能够正确传输。四、代码实现4.1 NEC协议解码代码下面是基于 51 单片机实现 NEC 协议解码的代码: #define IRIN P1_0 //红外接收管 unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f}; //遥控器按键对应的命令码 unsigned char read_IR() //读取红外信号 { int k = 0; unsigned char data = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } k = 0; while (!IRIN) //等待高电平出现 { k++; if (k > 1000) return 0; //超时返回 } k = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } for (int i = 0; i < 8; i++) //解码 8 个 bit { k = 0; while (!IRIN) //等待高电平出现 { k++; if (k > 1000) return 0; //超时返回 } delay_us(650); if (IRIN) //判断 bit 的值 data |= (1 << i); k = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } } return data; //返回解码结果 } unsigned char decode_IR(unsigned char code_val) //NEC 码转换为命令码 { for (int i = 0; i < 7; i++) { if (Remote6[i] == code_val) return (i + 1); } return 0; } void main() { unsigned char data = 0; unsigned char code_val = 0; while (1) { data = read_IR(); //读取红外信号 if (data == 0) //判断是否读取成功 continue; code_val = decode_IR(data); //将 NEC 码转换为命令码 switch (code_val) //根据命令码控制设备 { case 1: //控制设备执行命令 1 break; case 2: //控制设备执行命令 2 break; case 3: //控制设备执行命令 3 break; case 4: //控制设备执行命令 4 break; case 5: //控制设备执行命令 5 break; case 6: //控制设备执行命令 6 break; default: break; } } }以上代码主要实现了读取红外信号和将 NEC 码转换为命令码的功能,并且可以根据不同的命令码控制设备执行不同的指令。4.2 NEC协议发送代码以下是基于 51 单片机实现 NEC 协议发送的代码,可以根据需要修改指令码来控制不同的设备: #define IRLED P1_1 //红外发射管 unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f}; //遥控器按键对应的命令码 void delay_us(unsigned int us) //延时函数,单位为微秒 { while (us--) _nop_(); } void send_IR(unsigned char data) //发送红外信号 { unsigned char mask = 0x01; //bit 掩码 for (int i = 0; i < 8; i++) //发送 8 个 bit { if (data & mask) { IRLED = 0; delay_us(600); IRLED = 1; delay_us(1600); } else { IRLED = 0; delay_us(600); IRLED = 1; delay_us(600); } mask <<= 1; //移位更新掩码 } IRLED = 0; //发送结束,将红外发射管关闭 } void send_command(unsigned char code_val) //将命令码转换为 NEC 码并发送 { unsigned char nec_val = 0; if (code_val > 6) //判断是否超出范围 return; nec_val = 0x80 | (code_val << 4) | ((~code_val) & 0x0F); //计算 NEC 码 for (int i = 0; i < 2; i++) //发送两遍,以提高成功率 { send_IR(nec_val); delay_ms(50); } } void main() { while (1) { //向电视发送命令码为 1 的指令 send_command(1); delay_ms(1000); //延时 1s } }以上代码主要实现了将命令码转换为 NEC 码并发送的功能,可以根据需要修改指令码来控制不同的设备。红外发射管发射的红外信号有一定的传输范围和传输角度限制,需要根据具体情况调整发射管的位置和方向。4.3 按键检测代码以下是支持控制发送不同控制码的代码,可以根据需要修改指令码和按键设置: #define IRLED P1_1 //红外发射管 #define IRIN P1_0 //红外接收管 unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f}; //遥控器按键对应的命令码 unsigned char read_IR() //读取红外信号 { int k = 0; unsigned char data = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } k = 0; while (!IRIN) //等待高电平出现 { k++; if (k > 1000) return 0; //超时返回 } k = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } for (int i = 0; i < 8; i++) //解码 8 个 bit { k = 0; while (!IRIN) //等待高电平出现 { k++; if (k > 1000) return 0; //超时返回 } delay_us(650); if (IRIN) //判断 bit 的值 data |= (1 << i); k = 0; while (IRIN) //等待低电平出现 { k++; if (k > 1000) return 0; //超时返回 } } return data; //返回解码结果 } void delay_ms(unsigned int ms) //延时函数,单位为毫秒 { while (ms--) for (int i = 0; i < 120; i++) _nop_(); } void delay_us(unsigned int us) //延时函数,单位为微秒 { while (us--) _nop_(); } void send_IR(unsigned char data) //发送红外信号 { unsigned char mask = 0x01; //bit 掩码 for (int i = 0; i < 8; i++) //发送 8 个 bit { if (data & mask) { IRLED = 0; delay_us(600); IRLED = 1; delay_us(1600); } else { IRLED = 0; delay_us(600); IRLED = 1; delay_us(600); } mask <<= 1; //移位更新掩码 } IRLED = 0; //发送结束,将红外发射管关闭 } void send_command(unsigned char code_val) //将命令码转换为 NEC 码并发送 { unsigned char nec_val = 0; if (code_val > 6) //判断是否超出范围 return; nec_val = 0x80 | (code_val << 4) | ((~code_val) & 0x0F); //计算 NEC 码 for (int i = 0; i < 2; i++) //发送两遍,以提高成功率 { send_IR(nec_val); delay_ms(50); } } void main() { unsigned char data = 0; unsigned char code_val = 0; while (1) { data = read_IR(); //读取红外信号 if (data > 0) //判断是否有按键按下 { code_val = Remote6[data - 1]; //根据按键编号获取命令码 send_command(code_val); //将命令码转换为 NEC 码并发送 delay_ms(500); //延时一段时间,防止频繁发送 } } }以上代码主要实现了支持控制发送不同控制码的功能,可以根据需要修改指令码和按键设置。当用户按下遥控器上的按键时,程序会根据按键编号获取到对应的命令码,并将其转换为 NEC 码进行发送。
-
一、项目介绍随着社会经济的快速发展,人们对节能环保的要求越来越高,电动车因其无污染、噪音小、使用成本低等优点逐渐成为了市场关注的焦点。同时,随着科技的不断进步和应用,电动车的技术水平也在不断提高。为了更好地满足市场需求和科技进步的要求,本项目基于51单片机设计了一款电动车控制器。主要包括电动车控制和驱动两个关键部分。其中,控制部分采用51单片机作为控制核心,通过编程实现电动车前后行驶、左右转向、加速等操作。而驱动部分则采用L298N驱动芯片驱动直流电机。当前设计的电动车,支持锂电池供电、支持按键实现电动车前后行驶、左右转向和加速等操作,电机采用直流电机,驱动芯片采用L298N。二、系统架构本系统由控制器、电机、驱动芯片、锂电池和按键等组成,其功能、特点如下:(1)控制器:采用AT89S52微控制器,作为整个系统的核心控制部分。控制器接收来自按键的信号,控制驱动芯片输出电机控制信号,从而实现对电动车的前后行驶、左右转向、加速等控制功能。(2)电机:采用直流电机,其转速和转向可通过驱动芯片控制信号进行调节。(3)驱动芯片:采用L298N驱动芯片,为电机提供驱动电流,并控制电机转速和转向。L298N驱动芯片具有功率大、稳定性好等特点。(4)锂电池:为电动车提供动力,具有体积小、能量密度高、充电效率高、自放电率低等优点。(5)按键:用于控制和调节电动车的运行状态,包括前后行驶、左右转向、加速等操作。三、系统设计3.1 控制器设计本项目采用STC89C52为主控芯片,主要功能是接收来自按键的信号,并通过控制L298N驱动芯片输出驱动电流,从而控制电机的转速和转向。控制器还需要实现锂电池充电管理、限位保护等的功能。设计流程:(1)编写单片机的逻辑程序,实现对按键信号的捕获和处理,以及对L298N驱动芯片的控制。(2)为了实现锂电池充电和保护,采用锂电池充电模块和充电管理芯片。3.2 电机和驱动芯片设计本项目电机采用直流电机,驱动芯片采用L298N。设计流程:(1)根据电机型号和参数,确定合适的电机供电电压和控制电路。(2)根据实际需要,确定L298N驱动芯片的工作模式和参数,设计驱动电路。(3)为提高电机的效率和寿命,添加电机驱动电阻、反电动势抑制电路电路。3.3 锂电池设计本项目采用锂电池供电。设计流程:(1)根据需要,选择适当的锂电池型号和容量。(2)设计电池充电管理电路,实现对锂电池的充电和保护。(3)结合其他电路的设计,完成对锂电池的供电和相应的充电管理。3.4 按键设计按键是控制电动车运行状态的关键部分。设计流程:(1)根据实际需要,确定需要添加的按键类型和数量。(2)设计按键接口电路,实现按键信号的捕获和处理。(3)结合控制器设计,实现对电动车的前后行驶、左右转向、加速等操作控制。四、代码实现4.1 按键检测程序设计本项目用到了9个按键,按键按下是低电平。 实现了前后行驶切换控制、左右转向灯控制、加速控制、喇叭控制、前后刹车灯控制、一个开机键。以下是按键的完整逻辑代码: #include <reg52.h> sbit key1 = P1^0; // 按键1 sbit key2 = P1^1; // 按键2 sbit key3 = P1^2; // 按键3 sbit key4 = P1^3; // 按键4 sbit key5 = P1^4; // 按键5 sbit key6 = P1^5; // 按键6 sbit key7 = P1^6; // 按键7 sbit key8 = P1^7; // 按键8 sbit key9 = P2^0; // 按键9 sbit forward = P3^0; // 前进 sbit backward = P3^1; // 后退 sbit left = P3^2; // 左转灯 sbit right = P3^3; // 右转灯 sbit accelerate = P3^4; // 加速器 sbit horn = P3^5; // 喇叭 sbit stoplight1 = P3^6; // 前刹车灯 sbit stoplight2 = P3^7; // 后刹车灯 void main() { while(1) { if(key1 == 0) { // 按键1按下 forward = 1; backward = 0; } if(key2 == 0) { // 按键2按下 forward = 0; backward = 1; } if(key3 == 0) { // 按键3按下 left = 1; } else { left = 0; } if(key4 == 0) { // 按键4按下 right = 1; } else { right = 0; } if(key5 == 0) { // 按键5按下 accelerate = 1; } else { accelerate = 0; } if(key6 == 0) { // 按键6按下 horn = 1; } else { horn = 0; } if(key7 == 0) { // 按键7按下 stoplight1 = 1; } else { stoplight1 = 0; } if(key8 == 0) { // 按键8按下 stoplight2 = 1; } else { stoplight2 = 0; } if(key9 == 0) { // 按键9按下 forward = 0; backward = 0; left = 0; right = 0; accelerate = 0; horn = 0; stoplight1 = 0; stoplight2 = 0; } } }代码通过不断检测按键的电平状态,实现了对电动车的前后行驶、左右转向灯控制、加速、喇叭以及前后刹车灯控制等操作。当按键被按下时,对应的功能就会被执行,否则就会停止执行。其中,第9个按键为开机键,当按下时将所有功能都清零。4.2 L298芯片控制电机代码下面是 L298N 驱动模块控制电机正反转的代码: #include <reg52.h> sbit ena = P2^0; // 使能A端口 sbit in1 = P2^1; // A+控制信号 sbit in2 = P2^2; // A-控制信号 sbit enb = P2^3; // 使能B端口 sbit in3 = P2^4; // B+控制信号 sbit in4 = P2^5; // B-控制信号 void delay(int time) { // 延时函数 int i, j; for(i = 0; i < time; i++) { for(j = 0; j < 120; j++); } } void main() { ena = 1; // 使能A端口 enb = 1; // 使能B端口 while(1) { in1 = 1; // A+ 电流正向 in2 = 0; // A- 电流反向 in3 = 1; // B+ 电流正向 in4 = 0; // B- 电流反向 delay(1000); // 延时一段时间 in1 = 0; // A+ 电流反向 in2 = 1; // A- 电流正向 in3 = 0; // B+ 电流反向 in4 = 1; // B- 电流正向 delay(1000); // 延时一段时间 } }L298N 驱动模块可以控制电机的正反转,其中 in1、in2 控制 A 相电流的方向,in3、in4 控制 B 相电流的方向,ena、enb 是使能端口,需要设置为高电平才能控制电机。在例子中,先将 ena 和 enb 设置为高电平,然后让电机正向运转一段时间,再让电机反向运转一段时间,不断循环实现正反转。
-
一、OpenCV介绍OpenCV 是基于开源许可证的跨平台计算机视觉库,提供了一组丰富、广泛的图像处理和计算机视觉算法。OpenCV 支持多种编程语言,包括 C++、Python、Java 等,可以运行在 Linux、Windows、Mac OS 等平台上。OpenCV 能够在图像上绘制各种几何形状、文本和曲线,以及对图像进行调整、裁剪和旋转等操作,这些功能都为图像的分析和处理提供了很大的帮助。以下是 OpenCV 可以绘制图像的一些应用:(1)图像标注:在图像上添加标注或者注释,例如在目标检测或者图像分类任务中,通过在图像上绘制框、标签等信息来标记检测到的目标。(2)处理后显示:例如在图像处理过程中,可以在处理前和处理后的图像上绘制对比图,直观地显示图像处理的效果。(3)实时显示:通过持续不断地在屏幕上绘画来实现实时显示效果,例如在视频处理中输出处理后的视频流并将其实时渲染在屏幕上。二、绘制图形【1】绘制图形弹窗显示下面代码实现的功能:使用 OpenCV(C++) 新建一张透明图片,在图片里绘制一个矩形、一条直线、一段文字、一个圆。 // 创建一张大小为 512x512,具有 alpha 通道的透明图片 cv::Mat img(512, 512, CV_8UC4, cv::Scalar(0, 0, 0, 0)); // 在图片上绘制一个矩形 cv::rectangle(img, cv::Point(50, 50), cv::Point(200, 150), cv::Scalar(255, 0, 0, 255), -1); // 在图片上绘制一条直线 cv::line(img, cv::Point(300, 100), cv::Point(450, 100), cv::Scalar(0, 255, 0, 255), 3); // 在图片上绘制一段文本 std::string text = "Hello, OpenCV!"; cv::putText(img, text, cv::Point(50, 300), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255, 255), 2); // 在图片上绘制一个圆 cv::circle(img, cv::Point(400, 350), 50, cv::Scalar(255, 255, 0, 255), -1); // 显示图片 cv::imshow("image", img); cv::waitKey(0);cv::Mat 是 OpenCV 中表示图像的数据结构,它可以存储多通道的图像。 cv::Scalar 是一个四通道的实数向量,用于表示像素点的颜色和 alpha 值。 cv::Point 是一个二维整型向量,用于表示像素点的坐标。 cv::rectangle 函数用于在图片上绘制矩形。 cv::line 函数用于在图片上绘制直线。 cv::putText 函数用于在图片上绘制文本。 cv::circle 函数用于在图片上绘制圆。【2】绘制图形保存到本地 // 创建一张大小为 512x512,具有 alpha 通道的透明图片 cv::Mat img(512, 512, CV_8UC4, cv::Scalar(0, 0, 0, 0)); // 在图片上绘制一个矩形 cv::rectangle(img, cv::Point(50, 50), cv::Point(200, 150), cv::Scalar(255, 0, 0, 255), -1); // 在图片上绘制一条直线 cv::line(img, cv::Point(300, 100), cv::Point(450, 100), cv::Scalar(0, 255, 0, 255), 3); // 在图片上绘制一段文本 std::string text = "Hello, OpenCV!"; cv::putText(img, text, cv::Point(50, 300), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255, 255), 2); // 在图片上绘制一个圆 cv::circle(img, cv::Point(400, 350), 50, cv::Scalar(255, 255, 0, 255), -1); // 保存图片到本地 cv::imwrite("output.png", img);三、函数功能介绍【1】绘制直线cv::line 函数用于在图像上绘制一条直线,其参数如下: void cv::line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, LineTypes lineType = LINE_8, int shift = 0); img: 输入输出参数,表示待绘制的目标图像。pt1: 输入参数,表示的是直线的起点坐标,是一个 cv::Point 类型的对象。pt2: 输入参数,表示的是直线的终点坐标,也是一个 cv::Point 类型的对象。color: 输入参数,表示绘制直线的颜色以及透明度,是一个 cv::Scalar 类型的对象。常见的颜色有:红色(0, 0, 255)、绿色(0, 255, 0)、蓝色(255, 0, 0)等。thickness: 可选参数,表示绘制直线的宽度。默认值为 1 表示绘制一个像素宽度的直线,如果设置为负值,则表示绘制一条填充直线。 lineType : 可选参数,表示直线的类型,可以取以下几个值:cv::LINE_4: 表示绘制一条 4 连通的直线,默认值。cv::LINE_8: 表示绘制一条 8 连通的直线。cv::LINE_AA: 表示绘制一条抗锯齿的直线。shift: 可选参数,表示坐标点像素值所占用的位数,默认为 0。【2】绘制圆cv::circle 函数用于在图像上绘制一个圆,其参数如下: void cv::circle(InputOutputArray img, Point center, int radius, const Scalar& color, int thickness = 1, LineTypes lineType = LINE_8, int shift = 0); img: 输入输出参数,表示待绘制的目标图像。center: 输入参数,表示圆心坐标,是一个 cv::Point 类型的对象。radius: 输入参数,表示圆的半径。color: 输入参数,表示绘制圆的颜色以及透明度,是一个 cv::Scalar 类型的对象。thickness: 可选参数,表示圆线条的宽度。默认值为 1 表示绘制一个像素宽度的圆,如果设置为负值,则表示绘制一条填充的圆。 lineType : 可选参数,表示圆边界的类型,可以取以下几个值:cv::LINE_4: 表示绘制四个相邻的点的圆边界,默认值。cv::LINE_8: 表示绘制八个相邻的点的圆边界。cv::LINE_AA: 表示绘制抗锯齿的圆边界。shift: 可选参数,表示坐标点像素值所占用的位数,默认值为 0。【3】绘制矩形cv::rectangle 函数用于在图像上绘制一个矩形,其参数如下: void cv::rectangle(InputOutputArray img, Rect rect, const Scalar& color, int thickness = 1, LineTypes lineType = LINE_8, int shift = 0); img: 输入输出参数,表示待绘制的目标图像。rect: 输入参数,表示矩形,是一个 cv::Rect 类型的对象,可以通过传递左上角和右下角坐标的方式来定义一个矩形。color: 输入参数,表示绘制矩形的颜色以及透明度,是一个 cv::Scalar 类型的对象。thickness: 可选参数,表示矩形边框的宽度。默认值为 1 表示绘制一个像素宽度的矩形,如果设置为负值,则表示绘制一条填充的矩形。 lineType : 可选参数,表示矩形边框的类型,可以取以下几个值:cv::LINE_4: 表示绘制四个相邻的点的矩形边框,默认值。cv::LINE_8: 表示绘制八个相邻的点的矩形边框。cv::LINE_AA: 表示绘制抗锯齿的矩形边框。shift: 可选参数,表示坐标点像素值所占用的位数,默认值为 0。【4】绘制文本cv::putText 函数用于在图像上绘制文本,其参数如下:void cv::putText(InputOutputArray img, const String& text, Point org, int fontFace, double fontScale, Scalar color, int thickness = 1, int lineType = LINE_8, bool bottomLeftOrigin = false); img: 输入输出参数,表示待绘制的目标图像。text: 输入参数,表示要绘制的文本字符串。org: 输入参数,表示文本框左下角的坐标点,是一个 cv::Point 类型的对象。 fontFace : 输入参数,表示字体类型,可以取以下几个值:cv::FONT_HERSHEY_COMPLEX: 复杂风格字体。cv::FONT_HERSHEY_COMPLEX_SMALL: 小字号复杂风格字体。cv::FONT_HERSHEY_DUPLEX: 双线条字体。cv::FONT_HERSHEY_PLAIN: 单线条字体。cv::FONT_HERSHEY_SIMPLEX: 正常大小的字体。cv::FONT_HERSHEY_TRIPLEX: 三线条字体。fontScale: 输入参数,表示字体大小缩放比例。color: 输入参数,表示绘制文本的颜色以及透明度,是一个 cv::Scalar 类型的对象。thickness: 可选参数,表示文本轮廓线条的宽度。默认值为 1 表示绘制一个像素宽度的文本,如果设置为负值,则表示绘制一条填充的文本。lineType : 可选参数,表示文本边界的类型,可以取以下几个值:cv::LINE_4: 表示绘制四个相邻的点的文本边界,默认值。cv::LINE_8: 表示绘制八个相邻的点的文本边界。cv::LINE_AA: 表示绘制抗锯齿的文本边界。bottomLeftOrigin: 可选参数,表示坐标点是否为文本框左下角的坐标点,默认值为 false,表示坐标点为文本框左上角的坐标点。
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签