
模块光敏电阻传感器光敏电阻作为一种重要的光敏元件在各种光控应用中发挥着关键作用比如光控开关、光敏传感器、光敏报警器等等。其特性在光照强度变化时产生的电阻变化一一光照越强电阻越小。通过测量光敏电阻的电阻值我们可以实时监测光照条件的变化并据此进行相应的控制和反馈。常见的光敏电阻模块有3脚和4脚两种3脚的只有DO、、4脚的有DO和AO。给萌新小伙伴科普下:DO数字开关量输出(0和1)和AO模拟电压输出。·模块中蓝色的电位器是用于调节阀值顺时针旋转阈值会越大逆时针越小。上电后电源指示灯亮。当外界环境光线亮度达不到设定阈值时DO端输出高电平开关指示灯灭;当外界环境光线亮度超过设定阈值时DO端输出低电平开关指示灯亮。环境光线亮度越亮AO端经过AD转换的数值越小。项目智能台灯项目需求1. 红外传感器检测是否有人有人的话实时检测距离过近则报警同时计时超过固定时间则报警2. 按键 1 切换工作模式智能模式、按键模式、远程模式3. 智能模式下根据光照强度自动调整光照档位低亮、中亮、高亮没人则自动关灯4. 按键模式下按键 2 可以手动调整光照档位5. 远程模式下可以通过蓝牙控制光照档位、计时等6. 按键 3 暂停/开始计时按键 4 清零计时7. OLED 显示各项数据/状态。硬件清单蓝牙模块超声波传感器红外传感器光敏电阻传感器OLED高功率LED灯蜂鸣器KEY × 4杜邦线上官二号ST-LinkUSB转TTL项目框图项目流程图代码示例蜂鸣器、蓝牙、超声波、红外传感器原中断实验exti、按键、led新建高功率led、光照传感器原adc实验、oled、定时器中断。 “品”魔法棒 之后编译会发现有错误一个个来改将ia_sensor、light_sensor对应名字都改一下。还有timer重复定义的错误就是timer文件中有个定时器2的msp函数然后在超声波传感器中有这个定时器2 的msp函数。先将超声波这里的注释掉后面再改。之后timer中也有一部分的led函数由于led函数现在是空的所以先注释掉。beep只需要改一下引脚即可。PB13。#include beep.h #include sys.h //初始化GPIO函数 void beep_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_13; // 蜂鸣器对应的引脚 gpio_initstruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽输出 gpio_initstruct.Pull GPIO_PULLUP; // 上拉 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(GPIOB, gpio_initstruct); //关闭蜂鸣器 beep_off(); } //打开蜂鸣器的函数 void beep_on(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); // 拉低蜂鸣器引脚打开蜂鸣器 } //关闭蜂鸣器的函数 void beep_off(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); // 拉高蜂鸣器引脚关闭蜂鸣器 }#ifndef __BEEP_H__ #define __BEEP_H__ void beep_init(void); void beep_on(void); void beep_off(void); #endifbluetooth还是串口2的msp函数加入到uart1中。void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef gpio_init_struct; if (huart-Instance USART1) /* 如果是串口1进行串口1 MSP初始化 */ { __HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能串口TX脚时钟 */ __HAL_RCC_USART1_CLK_ENABLE(); /* 使能串口时钟 */ gpio_init_struct.Pin GPIO_PIN_9; /* 串口发送引脚号 */ gpio_init_struct.Mode GPIO_MODE_AF_PP; /* 复用推挽输出 */ gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */ HAL_GPIO_Init(GPIOA, gpio_init_struct); gpio_init_struct.Pin GPIO_PIN_10; /* 串口RX脚 模式设置 */ gpio_init_struct.Mode GPIO_MODE_AF_INPUT; HAL_GPIO_Init(GPIOA, gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */ HAL_NVIC_EnableIRQ(USART1_IRQn); /* 使能USART1中断通道 */ HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); /* 组2最低优先级:抢占优先级3子优先级3 */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* 使能UART1接收中断 */ __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); /* 使能UART1总线空闲中断 */ } else if (huart-Instance USART2) /* 如果是串口2进行串口2 MSP初始化 */ { __HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能串口TX脚时钟 */ __HAL_RCC_USART2_CLK_ENABLE(); /* 使能串口时钟 */ gpio_init_struct.Pin GPIO_PIN_2; /* 串口发送引脚号 */ gpio_init_struct.Mode GPIO_MODE_AF_PP; /* 复用推挽输出 */ gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */ HAL_GPIO_Init(GPIOA, gpio_init_struct); gpio_init_struct.Pin GPIO_PIN_3; /* 串口RX脚 模式设置 */ gpio_init_struct.Mode GPIO_MODE_AF_INPUT; HAL_GPIO_Init(GPIOA, gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */ HAL_NVIC_EnableIRQ(USART2_IRQn); /* 使能USART2中断通道 */ HAL_NVIC_SetPriority(USART2_IRQn, 3, 3); /* 组2最低优先级:抢占优先级3子优先级3 */ __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* 使能UART2接收中断 */ __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); /* 使能UART2总线空闲中断 */ } }要添加一个函数用来远程控制台灯的不同亮度。#include sys.h #include bluetooth.h #include string.h #include stdarg.h UART_HandleTypeDef uart2_handle; /* uart2句柄 */ uint8_t command 0; /*定义个变量获取rx_buf的第零个字节*/ uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE]; /* uart2接收缓冲区 */ uint16_t uart2_rx_len 0; /* uart2接收字符长度 */ /** * brief 串口1初始化函数 * param baudrate: 波特率, 根据自己需要设置波特率值 * retval 无 */ void bt_init(uint32_t baudrate) { /*uart2 初始化设置*/ uart2_handle.Instance USART2; /* USART */ uart2_handle.Init.BaudRate baudrate; /* 波特率 */ uart2_handle.Init.WordLength UART_WORDLENGTH_8B; /* 字长为8位数据格式 */ uart2_handle.Init.StopBits UART_STOPBITS_1; /* 一个停止位 */ uart2_handle.Init.Parity UART_PARITY_NONE; /* 无奇偶校验位 */ uart2_handle.Init.HwFlowCtl UART_HWCONTROL_NONE; /* 无硬件流控 */ uart2_handle.Init.Mode UART_MODE_TX_RX; /* 收发模式 */ HAL_UART_Init(uart2_handle); /* HAL_UART_Init()会使能uart2 */ } /** * brief uart2接收缓冲区清除 * param 无 * retval 无 */ void uart2_rx_clear(void) { memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf)); /* 清空接收缓冲区 */ uart2_rx_len 0; /* 接收计数器清零 */ } /** * brief 串口2中断服务函数 * note 在此使用接收中断及空闲中断实现不定长数据收发 * param 无 * retval 无 */ void USART2_IRQHandler(void) { uint8_t receive_data 0; if(__HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_RXNE) ! RESET){ /* 获取接收RXNE标志位是否被置位 */ if(uart2_rx_len sizeof(uart2_rx_buf)) /* 如果接收的字符数大于接收缓冲区大小 */ uart2_rx_len 0; /* 则将接收计数器清零 */ HAL_UART_Receive(uart2_handle, receive_data, 1, 1000); /* 接收一个字符 */ uart2_rx_buf[uart2_rx_len] receive_data; /* 将接收到的字符保存在接收缓冲区 */ } if (__HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_IDLE) ! RESET) /* 获取接收空闲中断标志位是否被置位 */ { printf(bt recv: %s\r\n, uart2_rx_buf); /* 将接收到的数据打印出来 */ command uart2_rx_buf[0]; /*如果接收1这是1是字符形式的*/ uart2_rx_clear(); __HAL_UART_CLEAR_IDLEFLAG(uart2_handle); /* 清除UART总线空闲中断 */ } } //void bt_send(char *send_buf, uint8_t size) //{ // HAL_UART_Transmit(uart2_handle, (uint8_t *)send_buf, size, 100); //} void bt_send(char * format, ...) { uint8_t send_buf[128] {0}; va_list arg; va_start(arg, format); vsprintf((char *)send_buf, format, arg); va_end(arg); HAL_UART_Transmit(uart2_handle, send_buf, sizeof(send_buf), 100); } uint8_t bt_rx_get(void) { uint8_t temp command; /*防止受到上一次传递信息的干扰使用中间变量来承接*/ command 9; /*承接之后给command一个无意义的值*/ return temp; }#ifndef __BLUETOOTH_H__ #define __BLUETOOTH_H__ #include stdio.h #include sys.h /* UART收发缓冲大小 */ #define UART2_RX_BUF_SIZE 128 #define UART2_TX_BUF_SIZE 64 void bt_init(uint32_t bound); /* 串口初始化函数 */ //void bt_send(char *send_buf, uint8_t size); void bt_send(char * format, ...); uint8_t bt_rx_get(void); #endifhcsr04这模块只需要在.h文件中改引脚即可 PB6--PA11 PB7--PA12。仅仅将开机时钟函数加入init中。#include hcsr04.h #include delay.h TIM_HandleTypeDef tim2_handle {0}; //定时器初始化函数 void tim2_init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); tim2_handle.Instance TIM2; tim2_handle.Init.Prescaler 72 - 1; tim2_handle.Init.Period 65536 - 1; tim2_handle.Init.CounterMode TIM_COUNTERMODE_UP; tim2_handle.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_Base_Init(tim2_handle); } //msp函数 //void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) //{ // if(htim-Instance TIM2) // { // __HAL_RCC_TIM2_CLK_ENABLE(); // } //} void tim2_start(void) { HAL_TIM_Base_Start(tim2_handle); } void tim2_stop(void) { HAL_TIM_Base_Stop(tim2_handle); } uint16_t tim2_get_cnt(void) { return __HAL_TIM_GetCounter(tim2_handle); } void tim2_set_cnt(uint16_t val) { __HAL_TIM_SetCounter(tim2_handle, val); } void hcsr04_gpio_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 TRIG_GPIO_CLK_ENABLE(); ECHO_GPIO_CLK_ENABLE(); //初始化Trig引脚 gpio_initstruct.Pin TRIG_PIN; gpio_initstruct.Mode GPIO_MODE_OUTPUT_PP; gpio_initstruct.Pull GPIO_NOPULL; gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(TRIG_PORT, gpio_initstruct); gpio_initstruct.Pin ECHO_PIN; gpio_initstruct.Mode GPIO_MODE_INPUT; HAL_GPIO_Init(ECHO_PORT, gpio_initstruct); } void hcsr04_init(void) { tim2_init(); hcsr04_gpio_init(); } float hcsr04_get_length(void) { uint16_t total_time 0; float distance 0; //1. Trig给Trig端口至少10us的高电平 TRIG_HIGH(); delay_us(15); TRIG_LOW(); //2. Echo引脚由低电平跳转到高电平表示开始发送波 //波发出去的那一下开始启动定时器 while(ECHO_STATUS() GPIO_PIN_RESET); tim2_start(); tim2_set_cnt(0); //3. Echo由高电平跳转回低电平表示波回来了 //波回来的那一下我们开始停止定时器计算出中间经过多少时间 while(ECHO_STATUS() GPIO_PIN_SET); tim2_stop(); //4. 计算出中间经过多少时间 total_time tim2_get_cnt(); //5. 距离 速度343m/s * 时间 / 2 distance total_time * 0.01715; return distance; }#ifndef __HCSR04_H__ #define __HCSR04_H__ #include sys.h #define TRIG_PORT GPIOA #define TRIG_PIN GPIO_PIN_11 #define TRIG_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define TRIG_HIGH() HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET) #define TRIG_LOW() HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET) #define ECHO_PORT GPIOA #define ECHO_PIN GPIO_PIN_12 #define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define ECHO_STATUS() HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) void hcsr04_init(void); float hcsr04_get_length(void); #endiflight_sensor光敏传感器初始化实际上也就是adc的初始化。还有就是获取返回值的函数为什么是通道1呢。看一下我们的接线是PA1看下手册对应的就是ADC1的通道1。就添加了这两个函数。#include adc.h ADC_HandleTypeDef adc_handle {0}; //全局变量句柄 init 和 msp都用。 void adc_init(void) { adc_handle.Instance ADC1; //基地址 adc_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; //数据对齐 右对齐 adc_handle.Init.ScanConvMode ADC_SCAN_DISABLE; //是否扫描本次只有光敏传感器一个通道 adc_handle.Init.ContinuousConvMode DISABLE; //单次转换or连续转换 adc_handle.Init.NbrOfConversion 1; // 转换的个数 只用一个通道 就1 adc_handle.Init.DiscontinuousConvMode DISABLE; // 间断模式 adc_handle.Init.NbrOfDiscConversion 0; //间断模式个数 没用0 adc_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; //触发转换的外部事件 软件触发 HAL_ADC_Init(adc_handle); //初始化库函数 上方为句柄对应的成员变量 HAL_ADCEx_Calibration_Start(adc_handle); //校准函数一般写在init函数中 } void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) { if(hadc-Instance ADC1) //防止其他adc在用此msp函数判断一下是否adc1 { RCC_PeriphCLKInitTypeDef adc_clk_init {0}; //对应句柄的定义 GPIO_InitTypeDef gpio_init_struct {0}; //GPIO对应句柄 __HAL_RCC_ADC1_CLK_ENABLE(); //ADC1时钟使能 __HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能 gpio_init_struct.Pin GPIO_PIN_1; //PA1 gpio_init_struct.Mode GPIO_MODE_ANALOG; //ADC DAC的特供模式 模拟量输入 HAL_GPIO_Init(GPIOA, gpio_init_struct); adc_clk_init.PeriphClockSelection RCC_PERIPHCLK_ADC;//外设的时钟选择 adc_clk_init.AdcClockSelection RCC_ADCPCLK2_DIV6;//adc预分频器 12M对应6分频 HAL_RCCEx_PeriphCLKConfig(adc_clk_init); //ADC时钟配置函数 参数为结构体句柄 } } /*配置adc的通道 参数分别操作哪个adc句柄、具体哪个通道、通道放在哪个顺序位置、采集时间*/ void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime) { ADC_ChannelConfTypeDef adc_ch_config {0};//第二个句柄定义 adc_ch_config.Channel ch; //通道 adc_ch_config.Rank rank; //序列 adc_ch_config.SamplingTime stime; //采样时间 HAL_ADC_ConfigChannel(hadc, adc_ch_config); // 参数为两个句柄 } /*获取adc的值获取哪个通道*/ uint32_t adc_get_result(uint32_t ch)/*获取结果*/ { /*调用配置通道的函数*/ adc_channel_config(adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); HAL_ADC_Start(adc_handle); /*开始转换*/ HAL_ADC_PollForConversion(adc_handle, 10); /*等待检测完成。句柄、超时时间*/ return (uint16_t)HAL_ADC_GetValue(adc_handle);/*就是获取DR寄存器里的值*/ } /*ADC1转换的结果在DR寄存器中的低16位强制位uint16_t就可以直接获取高16位有效的是ADC2的结果*/ void ls_init(void) { adc_init(); } uint8_t ls_get_value(void) /*光照越强 电压电阻越小但我们希望光照越强 获取到的值越大用减法*/ { return (uint8_t)(100 - (adc_get_result(ADC_CHANNEL_1) * 100 /4096));/*变100以内的数**/ } /*adc获取的值范围是0-4096因为adc是12位的最大4096*/#ifndef __ADC_H__ #define __ADC_H__ #include sys.h void adc_init(void); uint32_t adc_get_result(uint32_t ch); void ls_init(void); uint8_t ls_get_value(void); #endifoled引脚不用改 就是PB6和PB7。再就是取模到font.h的事情。const unsigned char lamp_16x16[][32]、模、式、智、能、按、键、远、程、有、无、人、亮、度、低、中、高、关、计、时。#include oled.h #include delay.h #include font.h void oled_gpio_init(void) { GPIO_InitTypeDef gpio_initstruct; OLED_I2C_SCL_CLK(); OLED_I2C_SDA_CLK(); gpio_initstruct.Pin OLED_I2C_SCL_PIN; gpio_initstruct.Mode GPIO_MODE_OUTPUT_PP; gpio_initstruct.Pull GPIO_PULLUP; gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(OLED_I2C_SCL_PORT, gpio_initstruct); gpio_initstruct.Pin OLED_I2C_SDA_PIN; HAL_GPIO_Init(OLED_I2C_SDA_PORT, gpio_initstruct); } void oled_i2c_start(void) { OLED_SCL_SET(); OLED_SDA_SET(); OLED_SDA_RESET(); OLED_SCL_RESET(); } void oled_i2c_stop(void) { OLED_SCL_SET(); OLED_SDA_RESET(); OLED_SDA_SET(); } void oled_i2c_ack(void) { OLED_SCL_SET(); OLED_SCL_RESET(); } void oled_i2c_write_byte(uint8_t data) { uint8_t i, tmp; tmp data; for(i 0; i 8; i) { if((tmp 0x80) 0x80) OLED_SDA_SET(); else OLED_SDA_RESET(); tmp tmp 1; OLED_SCL_SET(); OLED_SCL_RESET(); } } void oled_write_cmd(uint8_t cmd) { oled_i2c_start(); oled_i2c_write_byte(0x78); oled_i2c_ack(); oled_i2c_write_byte(0x00); oled_i2c_ack(); oled_i2c_write_byte(cmd); oled_i2c_ack(); oled_i2c_stop(); } void oled_write_data(uint8_t data) { oled_i2c_start(); oled_i2c_write_byte(0x78); oled_i2c_ack(); oled_i2c_write_byte(0x40); oled_i2c_ack(); oled_i2c_write_byte(data); oled_i2c_ack(); oled_i2c_stop(); } void oled_init(void) { oled_gpio_init(); delay_ms(100); oled_write_cmd(0xAE); //设置显示开启/关闭0xAE关闭0xAF开启 oled_write_cmd(0xD5); //设置显示时钟分频比/振荡器频率 oled_write_cmd(0x80); //0x00~0xFF oled_write_cmd(0xA8); //设置多路复用率 oled_write_cmd(0x3F); //0x0E~0x3F oled_write_cmd(0xD3); //设置显示偏移 oled_write_cmd(0x00); //0x00~0x7F oled_write_cmd(0x40); //设置显示开始行0x40~0x7F oled_write_cmd(0xA1); //设置左右方向0xA1正常0xA0左右反置 oled_write_cmd(0xC8); //设置上下方向0xC8正常0xC0上下反置 oled_write_cmd(0xDA); //设置COM引脚硬件配置 oled_write_cmd(0x12); oled_write_cmd(0x81); //设置对比度 oled_write_cmd(0xCF); //0x00~0xFF oled_write_cmd(0xD9); //设置预充电周期 oled_write_cmd(0xF1); oled_write_cmd(0xDB); //设置VCOMH取消选择级别 oled_write_cmd(0x30); oled_write_cmd(0xA4); //设置整个显示打开/关闭 oled_write_cmd(0xA6); //设置正常/反色显示0xA6正常0xA7反色 oled_write_cmd(0x8D); //设置充电泵 oled_write_cmd(0x14); oled_write_cmd(0xAF); //开启显示 } void oled_set_cursor(uint8_t x, uint8_t y) { oled_write_cmd(0xB0 y); oled_write_cmd((x 0x0F) | 0x00); oled_write_cmd(((x 0xF0) 4) | 0x10); } void oled_fill(uint8_t data) { uint8_t i, j; for(i 0; i 8; i) { oled_set_cursor(0, i); for(j 0; j 128; j) oled_write_data(data); } } void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size) { uint8_t i, j, page; num num - ; page size / 8; if(size % 8) page; for(j 0; j page; j) { oled_set_cursor(x, y j); for(i size / 2 * j; i size /2 * (j 1); i) { if(size 12) oled_write_data(ascii_6X12[num][i]); else if(size 16) oled_write_data(ascii_8X16[num][i]); else if(size 24) oled_write_data(ascii_12X24[num][i]); } } } void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size) { while(*p ! \0) { oled_show_char(x, y, *p, size); x size/2; p; } } void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N) { uint16_t i, j; for(j 0; j 2; j) { oled_set_cursor(x, y j); for(i 16 *j; i 16 * (j 1); i) oled_write_data(lamp_16x16[N][i]); } } void oled_show_init(void) /*界面初始化*/ { oled_fill(0x00); //清屏 oled_show_chinese(10,0,1); //模式 oled_show_chinese(25,0,2); oled_show_chinese(40,0,0); oled_show_chinese(10,2,12); //亮度 oled_show_chinese(25,2,13); oled_show_chinese(40,2,0); oled_show_chinese(25,4,11); // 人 cm oled_show_string(96,4,cm,16); oled_show_chinese(10,6,18); //计时 oled_show_chinese(25,6,19); oled_show_chinese(40,6,0); oled_show_char(100,6,s,16); } void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp) { uint8_t i, j; for(j 0; j height; j) { oled_set_cursor(x, y j); for(i 0; i width; i) oled_write_data(bmp[width * j i]); } }#ifndef __OLED_H__ #define __OLED_H__ #include sys.h #define OLED_I2C_SCL_CLK() __HAL_RCC_GPIOB_CLK_ENABLE(); #define OLED_I2C_SCL_PORT GPIOB #define OLED_I2C_SCL_PIN GPIO_PIN_6 #define OLED_I2C_SDA_CLK() __HAL_RCC_GPIOB_CLK_ENABLE(); #define OLED_I2C_SDA_PORT GPIOB #define OLED_I2C_SDA_PIN GPIO_PIN_7 #define OLED_SCL_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT, OLED_I2C_SCL_PIN, GPIO_PIN_RESET) #define OLED_SCL_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT, OLED_I2C_SCL_PIN, GPIO_PIN_SET) #define OLED_SDA_RESET() HAL_GPIO_WritePin(OLED_I2C_SDA_PORT, OLED_I2C_SDA_PIN, GPIO_PIN_RESET) #define OLED_SDA_SET() HAL_GPIO_WritePin(OLED_I2C_SDA_PORT, OLED_I2C_SDA_PIN, GPIO_PIN_SET) void oled_init(void); void oled_write_cmd(uint8_t cmd); void oled_write_data(uint8_t data); void oled_fill(uint8_t data); void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size); void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size); void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N); void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp); void oled_show_init(void); #endifLED我们的高功率LED要实现的是四个亮度的效果那么我们怎么实现呢当然是PWM波就是之前做过的呼吸灯的那个实验。所以把之前这个实验的pwm.c.h拿来改一改。我们这里用定时器3通道3 对应PB0。之后再补充控制led的函数。#include led.h #include oled.h uint8_t led_level 0; TIM_HandleTypeDef pwm_handle {0}; // init函数 void pwm_init(uint16_t arr, uint16_t psc) { TIM_OC_InitTypeDef pwm_config {0}; pwm_handle.Instance TIM3; pwm_handle.Init.Prescaler psc; pwm_handle.Init.Period arr; pwm_handle.Init.CounterMode TIM_COUNTERMODE_UP; HAL_TIM_PWM_Init(pwm_handle); pwm_config.OCMode TIM_OCMODE_PWM1; pwm_config.Pulse 0; /*初始占空比0*/ pwm_config.OCPolarity TIM_OCPOLARITY_HIGH; /*高电平有效 亮灯*/ HAL_TIM_PWM_ConfigChannel(pwm_handle, pwm_config, TIM_CHANNEL_3); HAL_TIM_PWM_Start(pwm_handle, TIM_CHANNEL_3); } //msp函数 void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { if(htim-Instance TIM3) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 __HAL_RCC_TIM3_CLK_ENABLE(); //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_0; // 两个LED对应的引脚 gpio_initstruct.Mode GPIO_MODE_AF_PP; // 推挽输出 gpio_initstruct.Pull GPIO_PULLUP; // 上拉 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(GPIOB, gpio_initstruct); } } //修改CCR值的函数 void pwm_compare_set(uint16_t val) { __HAL_TIM_SET_COMPARE(pwm_handle, TIM_CHANNEL_3, val); } void led_init(void) { pwm_init(500 - 1, 72 - 1); } void led_off(void) { pwm_compare_set(0); led_level 0; oled_show_chinese(70, 2, 17); } void led_low(void) { pwm_compare_set(150); led_level 1; oled_show_chinese(70, 2, 14); } void led_medium(void) { pwm_compare_set(300); led_level 2; oled_show_chinese(70, 2, 15); } void led_high(void) { pwm_compare_set(450); led_level 3; oled_show_chinese(70, 2, 16); } uint8_t led_leve_get(void) { return led_level; }#ifndef __PWM_H__ #define __PWM_H__ #include sys.h void pwm_init(uint16_t arr, uint16_t psc); void pwm_compare_set(uint16_t val); void led_init(void); void led_off(void); void led_low(void); void led_medium(void); void led_high(void); uint8_t led_leve_get(void); #endifkey原本的额代码中key只有两个但这里我们用到的是四个所以增加两个。#include key.h #include delay.h //初始化GPIO void key_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 KEY1_GPIO_CLK_ENABLE(); KEY2_GPIO_CLK_ENABLE(); KEY3_GPIO_CLK_ENABLE(); KEY4_GPIO_CLK_ENABLE(); //调用GPIO初始化函数 gpio_initstruct.Pin KEY1_GPIO_PIN; gpio_initstruct.Mode GPIO_MODE_INPUT; // 输入 gpio_initstruct.Pull GPIO_PULLUP; // 上拉 gpio_initstruct.Speed GPIO_SPEED_FREQ_HIGH; // 高速 HAL_GPIO_Init(KEY1_GPIO_PORT, gpio_initstruct); gpio_initstruct.Pin KEY2_GPIO_PIN; HAL_GPIO_Init(KEY2_GPIO_PORT, gpio_initstruct); gpio_initstruct.Pin KEY3_GPIO_PIN; HAL_GPIO_Init(KEY3_GPIO_PORT, gpio_initstruct); gpio_initstruct.Pin KEY4_GPIO_PIN; HAL_GPIO_Init(KEY4_GPIO_PORT, gpio_initstruct); } //按键扫描函数 uint8_t key_scan(void) { // static如果定义在函数内变量会保留上次调用结束时的值 static uint8_t key_up 1; //按键松开标志 uint8_t key_val 0; if(key_up (KEY1 0 || KEY2 0 || KEY3 0 || KEY4 0)) { delay_ms(10); key_up 0; if(KEY1 0) key_val 1; if(KEY2 0) key_val 2; if(KEY3 0) key_val 3; if(KEY4 0) key_val 4; } else if(KEY1 1 KEY2 1 KEY3 1 KEY4 1) key_up 1; return key_val; }#ifndef __KEY_H__ #define __KEY_H__ #include sys.h #define KEY1_GPIO_PORT GPIOA #define KEY1_GPIO_PIN GPIO_PIN_4 #define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) #define KEY2_GPIO_PORT GPIOA #define KEY2_GPIO_PIN GPIO_PIN_5 #define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) #define KEY3_GPIO_PORT GPIOA #define KEY3_GPIO_PIN GPIO_PIN_6 #define KEY3_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) #define KEY4_GPIO_PORT GPIOA #define KEY4_GPIO_PIN GPIO_PIN_7 #define KEY4_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) #define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) #define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) #define KEY3 HAL_GPIO_ReadPin(KEY3_GPIO_PORT, KEY3_GPIO_PIN) #define KEY4 HAL_GPIO_ReadPin(KEY4_GPIO_PORT, KEY4_GPIO_PIN) void key_init(void); uint8_t key_scan(void); #endifia_sensor红外传感器之前使用中断的方式来写的我们要做的事情呢是希望能够一直检测到有人中断的话只会检测到从无到有的这么一个状态。有人一直做是不会持续触发的。这里是PB12#include ia_sensor.h #include sys.h #include delay.h #include led.h uint8_t ia_flag FALSE; /*是否有人标志*/ void ia_init(void) { GPIO_InitTypeDef gpio_initstruct; //打开时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 //调用GPIO初始化函数 gpio_initstruct.Pin GPIO_PIN_12; // 对应的引脚 gpio_initstruct.Mode GPIO_MODE_INPUT; gpio_initstruct.Pull GPIO_PULLUP; // 上拉 HAL_GPIO_Init(GPIOA, gpio_initstruct); } uint8_t ia_flag_get(void) { if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) GPIO_PIN_RESET) return TRUE; else return FALSE; }#ifndef __EXTI_H__ #define __EXTI_H__ #include sys.h void ia_init(void); uint8_t ia_flag_get(void); #define TRUE 1 #define FALSE 0 #endiftimer只用到计时的功能这里改成定时器4,、定时器2在超声波那里用了。再封装几个函数启动、暂停、清理、翻转定时器跑的时候 就让停停的时候就启动。timer_init到时候设置成arr10000 psc7200就是一秒钟一个中断 记一个数。#include timer.h #include led.h TIM_HandleTypeDef timer_handle {0}; uint32_t sit_time 0; /*坐下的时间*/ uint8_t timer_running 0; /*是否在运行的标志*/ //定时器初始化函数 void timer_init(uint16_t arr, uint16_t psc) { timer_handle.Instance TIM4; timer_handle.Init.Prescaler psc; timer_handle.Init.Period arr; timer_handle.Init.CounterMode TIM_COUNTERMODE_UP; timer_handle.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_Base_Init(timer_handle); // HAL_TIM_Base_Start_IT(timer_handle); } //msp函数 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) { if(htim-Instance TIM4) { __HAL_RCC_TIM4_CLK_ENABLE(); HAL_NVIC_SetPriority(TIM4_IRQn, 2, 2); HAL_NVIC_EnableIRQ(TIM4_IRQn); } } //中断服务函数 void TIM4_IRQHandler(void) { HAL_TIM_IRQHandler(timer_handle); } //更新中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM4) { sit_time; // printf(sit_time: %d\r\n, sit_time); // led1_toggle(); } } void timer_start(void) { HAL_TIM_Base_Start_IT(timer_handle); timer_running 1; } void timer_stop(void) { HAL_TIM_Base_Stop(timer_handle); timer_running 0; } void timer_toggle(void) { if(timer_running) timer_stop(); else timer_start(); } uint32_t sit_time_get(void) { return sit_time; } void sit_time_set(uint32_t value) { sit_time value; }#ifndef __TIMER_H__ #define __TIMER_H__ #include sys.h void timer_init(uint16_t arr, uint16_t psc); void timer_start(void); void timer_stop(void); void timer_toggle(void); uint32_t sit_time_get(void); void sit_time_set(uint32_t value); #endifmain前面已经把所有的模块的代码都已经封装好了接下来的话呢 就可以开始main函数主要是业务逻辑的代码根据之前的流程图来编写。首先就是要先把所有的头文件包含进去 之后init函数都初始化上。然后根据流程图的顺序来写#include sys.h #include delay.h #include uart1.h #include led.h #include beep.h #include bluetooth.h #include hcsr04.h #include ia_sensor.h #include key.h #include light_sensor.h #include oled.h #include timer.h enum lamp_mode { AUTO_MODE 0, MANUAL_MODE, REMOTE_MODE }; int main(void) { HAL_Init(); /* 初始化HAL库 */ stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ // led_init(); /* 初始化LED灯 */ uart1_init(115200); led_init(); beep_init(); bt_init(9600); //蓝牙模块默认波特率9600 不是115200 hcsr04_init(); ia_init(); key_init(); ls_init(); oled_init(); timer_init(10000 - 1, 7200 - 1); //一秒钟 中断一次 printf(hello world!\r\n); oled_show_init(); //首先进行屏幕的初始化 uint8_t person_flag 0, first_loop 1, dis 0, dis_str[3] {0}, sit_time_str[3] {0}, key_num 0, mode AUTO_MODE, light_value 0, led_level 0; while(1) { person_flag ia_flag_get(); //判断是否有人 if(person_flag TRUE) //有人 { oled_show_chinese(10, 4, 9); if(first_loop) //保证只第一次计时一次 不会一直while计时 { first_loop 0; timer_start(); //开启定时器 计时 } dis hcsr04_get_length(); //有人则一直获取距离 if(dis 10) beep_on(); else beep_off(); if(sit_time_get() 5) beep_on(); } else //判断无人 { oled_show_chinese(10, 4, 10); timer_stop(); //定时器停止 sit_time_set(0); //定时器计数清0 first_loop 1; //使下一次来人还会打开定时器 beep_off(); dis 0; } //%3d表示按十进制整数输出至少占 3 个字符宽度 sprintf((char *)dis_str, %3d, dis); //oled只能显示字符 需要sprintf oled_show_string(60, 4, (char *)dis_str, 16); sprintf((char *)sit_time_str, %3d, sit_time_get()); //显示计时时间 oled_show_string(60, 6, (char *)sit_time_str, 16); key_num key_scan(); if(key_num 1) { if(mode 1) //先取值判断之后再最终取到的是012. mode AUTO_MODE; //0 } else if(key_num 3) //暂停定时器 就是翻转 { timer_toggle(); } else if(key_num 4) //停止并清零定时器 { timer_stop(); sit_time_set(0); } switch(mode) { case AUTO_MODE: //智能调节灯光 { oled_show_chinese(60, 0, 3); //智 oled_show_chinese(75, 0, 4); //能 if(person_flag TRUE) //有人再亮灯 { light_value ls_get_value(); if(light_value 40) led_high(); else if(light_value 40 light_value 70) led_medium(); else if(light_value 70) led_low(); } else led_off(); break; } case MANUAL_MODE: { oled_show_chinese(60, 0, 5); //按 oled_show_chinese(75, 0, 6); //键 if(key_num 2) { led_level led_leve_get(); //获取当前的档位 if(led_level 2) //如果为3 恢复0 led_level 0; switch(led_level) { case 0: led_off(); break; case 1: led_low(); break; case 2: led_medium(); break; case 3: led_high(); break; default: break; } } break; } case REMOTE_MODE: { oled_show_chinese(60, 0, 7); //远 oled_show_chinese(75, 0, 8); //程 switch(bt_rx_get() - 0) /*传过来是字符类型的要变成数字有函数atoi*/ { /*或者直接减去ASCII码的字符0*/ case 0: led_off(); break; case 1: led_low(); break; case 2: led_medium(); break; case 3: led_high(); break; case 4: timer_start(); break; case 5: timer_stop(); break; case 6: timer_stop(); sit_time_set(0); break; default: break; } break; } default: break; } delay_ms(20); } }