【STM32】智能台灯项目

目录

一、项目需求

  1. 红外传感器检测是否有人,有人的话实时检测距离,过近则报警;同时计时,超过固定时间则警;
  2. 按键 1 切换工作模式:智能模式、按键模式、远程模式;
  3. 智能模式下,根据光照强度自动调整光照档位(低亮、中亮、高亮),没人则自动光灯;
  4. 按键模式下,按键 2 可以手动调整光照档位;
  5. 远程模式下,可以通过蓝牙控制光照档位、计时等;
  6. 按键 3 暂停/开始计时,按键 4 清零计时;
  7. OLED 显示各项数据/状态。

二、硬件清单

  • 蓝牙模块
  • 超声波传感器
  • 红外传感器
  • 光敏电阻传感器
  • OLED
  • 高功率LED灯
  • 蜂鸣器
  • KEY × 4
  • 杜邦线
  • STM32
  • ST-Link
  • USB转TTL

三、硬件接线


四、项目框图


五、完整代码

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.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初始化
    led_init();
    //串口1初始化
    uart1_init(115200);
    printf("打印测试:hello world\r\n");
    //蜂鸣器初始化
    beep_init();
    //蓝牙模块初始化
    bt_init(115200);
    //超声波传感器初始化
    hcsr04_init();
    //红外传感器初始化
    ia_init();
    //按键初始化
    key_init();
    //光敏传感器初始化
    ls_init();
    //OLED初始化
    oled_init();
    //定时器初始化
    timer_init(10000 - 1, 7200 - 1);
    
    //OLED屏幕显示初始化
    oled_show_init();
    
    //定义是否有人标志位
    uint8_t person_flag = 0;
    //定义第一次有人状态
    uint8_t first_loop = 1;
    //定义距离变量
    uint8_t dis = 0;
    //定义距离显示变量
    uint8_t dis_str[3] = {0};
    //定义坐下时间显示变量
    uint8_t sit_time_str[3] = {0};
    //定义按键变量
    uint8_t key_num = 0;
    //定义模式
    uint8_t mode = AUTO_MODE;
    //定义光照强度
    uint8_t light_value = 0;
    //定义LED亮度值
    uint8_t led_level = 0;
    
    while(1)
    {
        person_flag = ia_flag_get();
        
        //判断附近是否有人
        if(person_flag == TRUE){            //有人
            
            oled_show_chinese(10, 4, 9);    //OLED显示有人
            
            if(first_loop){                 //第一次有人,语句中代码只执行一次
                //已经有人了
                first_loop = 0;
                //开始计时
                timer_start();
            }
            
            //测距
            dis = hcsr04_get_length();
            
            //判断距离
            if(dis < 10){
                beep1_on();                 //报警
            }else{
                beep1_off();                //关闭报警
            }
            
            //判断时间
            if(sit_time_get() >= 5){
                beep1_on();
            }
            
        }else{                               //无人
            
            oled_show_chinese(10, 4, 10);    //OLED显示无人
            
            //清零计时器
            timer_stop();
            sit_time_set(0);
            first_loop = 1;
            beep1_off();
            dis = 0;
        }
        //拼接距离字符串并且显示
        sprintf((char *)dis_str, "%3d", dis);
        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){
                mode = AUTO_MODE;
            }
        }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();
                        printf("灯泡高亮\r\n");
                    }else if(light_value >= 40 && light_value <= 70){   //光线适中灯泡中亮
                        led_medium();
                        printf("灯泡中亮\r\n");
                    }else if(light_value > 70){                         //光线亮灯泡低亮
                        led_low();
                        printf("灯泡低亮\r\n");
                    }
                }else{  //如果没人
                    led_off();                                          //关灯
                    printf("关灯\r\n");
                }
                break;
            }  
            case MANUAL_MODE:
            {
                oled_show_chinese(60, 0, 5);    //显示按键模式
                oled_show_chinese(75, 0, 6);
                //判断按键
                if(key_num == 2){
                    led_level = led_level_get();
                    //改变灯泡亮度
                    if(led_level++ > 2){
                        led_level = 0;
                    }
                    //调整灯泡亮度
                    switch(led_level){
                        case 0:
                            led_off();      //关灯
                            printf("关灯\r\n");
                            break;
                        case 1:
                            led_low();      //低亮
                            printf("灯泡低亮\r\n");
                            break;
                        case 2:
                            led_medium();   //中亮
                            printf("灯泡中亮\r\n");
                            break;
                        case 3:
                            led_high();     //高亮
                            printf("灯泡高亮\r\n");
                            break;
                    }
                }
                break;
            }
            case REMOTE_MODE:
            {
                oled_show_chinese(60, 0, 7);    //显示远程模式
                oled_show_chinese(75, 0, 8);
                switch(bt_rx_get() - '0'){
                    case 0:
                        led_off();      //关灯
                        printf("关灯\r\n");
                        break;
                    case 1:
                        led_low();      //低亮
                        printf("灯泡低亮\r\n");
                        break;
                    case 2:
                        led_medium();   //中亮
                        printf("灯泡中亮\r\n");
                        break;
                    case 3:
                        led_high();     //高亮
                        printf("灯泡高亮\r\n");
                        break;
                    case 4:
                        timer_start();  //定时器开
                        break;
                    case 5:
                        timer_stop();   //定时器关
                        break;
                    case 6:
                        timer_stop();   //停止并清空定时器
                        sit_time_set(0);
                        break;
                }
                break;
            }
            default:
            {
                
                break;
            }
        }
        
        
        delay_ms(20);
    }
}

beep.c

c 复制代码
#include "beep.h"
#include "sys.h"

//初始化GBIO口函数
void beep_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_13;               //beep1对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //上拉
    gpio_initstruct.Pull = GPIO_PULLUP;             //高速
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    //关闭beep1
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
}

//开启beep1的函数
void beep1_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);   //拉低beep1引脚,开启beep1
}


//关闭beep1的函数

void beep1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);     //拉高beep1引脚,关闭beep1
}

//翻转beep1的函数
void beep1_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_13);                   //翻转beep1引脚电平
}

beep.h

c 复制代码
#ifndef __BEEP_H__
#define __BEEP_H__

//初始化GBIO口函数
void beep_init(void);

//开启beep1的函数
void beep1_on(void);

//关闭beep1的函数

void beep1_off(void);

//翻转beep1的函数
void beep1_toggle(void);

#endif 

bluetooth.c

c 复制代码
#include "sys.h"
#include "bluetooth.h"
#include "string.h"
#include "stdarg.h"

UART_HandleTypeDef uart2_handle;                                            /* uart2句柄 */

uint8_t command = 0;

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;                                         /* USART2 */
    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       串口1中断服务函数
 * @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("recv: %s\r\n", uart2_rx_buf);                               /* 将接收到的数据打印出来 */
        command = uart2_rx_buf[0];
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

////串口2发送函数
//void bt_send(char *send_buf, uint8_t size)
//{
//    HAL_UART_Transmit(&uart2_handle, (uint8_t*)send_buf, size, 100);
//}

//串口2发送不定长数据
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 tmp = command;
    command = '9';
    return tmp;
}

bluetooth.h

c 复制代码
#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);            /* 串口初始化函数 */
////串口2发送函数
//void bt_send(char *send_buf, uint8_t size);
//串口2发送不定长数据
void bt_send(char *format, ...);
//将指令传递出去
uint8_t bt_rx_get(void);

#endif

hcsr04.c

c 复制代码
#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 = 6536 - 1;
    tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    tim2_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    HAL_TIM_Base_Init(&tim2_handle);
    //使能更新中断,启动计数器
    //HAL_TIM_Base_Start_IT(&tim2_handle);
}

////msp初始化函数,在HAL_TIM_Base_Init中调用,无须自己调用
//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_IT(&tim2_handle);
}

//定时器关闭函数
void tim2_stop(void)
{
    HAL_TIM_Base_Stop(&tim2_handle);
}

//读CNT函数
uint16_t tim2_get_cnt(void)
{
    return __HAL_TIM_GetCounter(&tim2_handle);
}

//设置CNT函数
void tim2_set_cnt(uint16_t val)
{
    __HAL_TIM_SetCounter(&tim2_handle,val);
}

//GPIO初始化
void hcsr04_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能时钟
    TRIG_GPIO_CLKENABLE();
    ECHO_GPIO_CLKENABLE();
    
    //初始化trig引脚
    gpio_initstruct.Pin = TRIG_PIN;                 //tirg对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(TRIG_PORT, &gpio_initstruct);
    
    //初始化echo引脚
    gpio_initstruct.Pin = ECHO_PIN;                 //echo对应引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;         //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    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;
    
    //1、给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();
    total_time = tim2_get_cnt();
    
    //4、距离 = 速度(343m/s) * 时间 / 2
    distance = total_time * 0.01715;
    
    return distance;
}

hcsr04.h

c 复制代码
#ifndef __HCSR04_H__
#define __HCSR04_H__

#include "sys.h"

#define TRIG_PORT               GPIOA
#define TRIG_PIN                GPIO_PIN_11
#define TRIG_GPIO_CLKENABLE()   __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_CLKENABLE()   __HAL_RCC_GPIOA_CLK_ENABLE()
#define ECHO_STATUS()           HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN)

//距离获取函数
float hcsr04_get_length(void);
//初始化超声波传感器
void hcsr04_init(void);

#endif

is_sensor.c

c 复制代码
#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;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_12;               //KEY0引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;         //输入信号下降沿检测
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
}

//读取红外传感器状态
uint8_t ia_flag_get(void)
{
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET){
        return TRUE;
    }else{
        return FALSE;
    }
}

is_sensor.h

c 复制代码
#ifndef __IA_SENSOR_H__
#define __IA_SENSOR_H__

#include "sys.h"

#define TRUE 1
#define FALSE 0 

//红外传感器初始化
void ia_init(void);
//读取红外传感器状态
uint8_t ia_flag_get(void);

#endif

key.c

c 复制代码
#include "key.h"
#include "delay.h"

//初始化GPIO
void key_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能按键GPIO时钟
    KEY1_GPIO_CLK_ENABLE();
    KEY2_GPIO_CLK_ENABLE();
    KEY3_GPIO_CLK_ENABLE();
    KEY4_GPIO_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = KEY1_GPIO_PIN;            //KEY1对应引脚
    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;            //KEY2对应引脚
    HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_initstruct);
    
    gpio_initstruct.Pin = KEY3_GPIO_PIN;            //KEY3对应引脚
    HAL_GPIO_Init(KEY3_GPIO_PORT, &gpio_initstruct);
    
    gpio_initstruct.Pin = KEY4_GPIO_PIN;            //KEY4对应引脚
    HAL_GPIO_Init(KEY4_GPIO_PORT, &gpio_initstruct);
}

//按键扫描函数
uint8_t key_scan(void)
{
    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;
}

key.h

c 复制代码
#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);

#endif 

led.c

c 复制代码
#include "led.h"
#include "oled.h"

uint8_t led_level = 0;

TIM_HandleTypeDef pwm_handler;

//init函数
void pwm_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef pwm_config = {0};
    
    //时基工作参数初始化
    pwm_handler.Instance = TIM3;
    pwm_handler.Init.Prescaler = psc;
    pwm_handler.Init.Period = arr;
    pwm_handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    pwm_handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    
    HAL_TIM_PWM_Init(&pwm_handler); //自动调用msp函数
    
    //PWM模式配置
    pwm_config.OCMode = TIM_OCMODE_PWM1;
    pwm_config.Pulse = 0;
    pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;
    HAL_TIM_PWM_ConfigChannel(&pwm_handler,&pwm_config,TIM_CHANNEL_3);
    
    //使能输出,启动计数器
    HAL_TIM_PWM_Start(&pwm_handler,TIM_CHANNEL_3);
}

//msp函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3){
        //GPIO初始化
        GPIO_InitTypeDef gpio_initstruct;
    
        //使能GPIOB时钟
        __HAL_RCC_GPIOB_CLK_ENABLE();
        //使能定时器4时钟
        __HAL_RCC_TIM4_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_0;               //定时器3通道3对应引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用推挽输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    }
}

//修改CCR值的函数
void pwm_compare_set(uint16_t val)
{
    __HAL_TIM_SET_COMPARE(&pwm_handler,TIM_CHANNEL_3,val);
}

//LED初始化
void led_init(void)
{
    pwm_init(500 - 1, 72 - 1);
}

//LED关闭
void led_off(void)
{
    pwm_compare_set(0);
    led_level = 0;
    oled_show_chinese(70, 2, 17);
}

//LED低亮
void led_low(void)
{
    pwm_compare_set(150);
    led_level = 1;
    oled_show_chinese(70, 2, 14);
}

//LED中亮
void led_medium(void)
{
    pwm_compare_set(300);
    led_level = 2;
    oled_show_chinese(70, 2, 15);
}

//LED高亮
void led_high(void)
{
    pwm_compare_set(450);
    led_level = 3;
    oled_show_chinese(70, 2, 16);
}

//获取LED当前亮度挡位
uint8_t led_level_get(void)
{
    return led_level;
}

led.h

c 复制代码
#ifndef __LED_H__
#define __LED_H__

#include "sys.h"

//init函数
void pwm_init(uint16_t arr, uint16_t psc);
//修改CCR值的函数
void pwm_compare_set(uint16_t val);
//LED初始化
void led_init(void);
//LED关闭
void led_off(void);
//LED低亮
void led_low(void);
//LED中亮
void led_medium(void);
//LED高亮
void led_high(void);
//获取LED当前亮度挡位
uint8_t led_level_get(void);

#endif

light_sensor.c

c 复制代码
#include "light_sensor.h"

ADC_HandleTypeDef adc_handle = {0};
//ADC配置初始化
void adc_init(void)
{
    adc_handle.Instance = ADC1;                            //选择ADC1
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       //数据右对齐
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       //不扫描
    adc_handle.Init .ContinuousConvMode = DISABLE;         //不连续转换
    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);  //调用初始化函数
    
    //ADC校准
    HAL_ADCEx_Calibration_Start(&adc_handle);
}
//ADC相关硬件配置
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    //确认是否为ADC1
    if(hadc->Instance == ADC1){
        RCC_PeriphCLKInitTypeDef  adc_clk_init = {0}; 
        GPIO_InitTypeDef gpio_init_struct = {0};
        //开启ADC1时钟
        __HAL_RCC_ADC1_CLK_ENABLE();
        //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        //设置PIN1口,模拟输入模式
        gpio_init_struct.Pin = GPIO_PIN_1;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        //GPIO初始化
        HAL_GPIO_Init(GPIOA,&gpio_init_struct);
        //设置外设时钟选择为RCC外设ADC时钟
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        //设置adc时钟分频因子 为6分频
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        //配置外设时钟
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
} 
//ADC通道配置
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank, uint32_t stime)
{ 
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    //设置adc通道参数
    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通道
    adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    //开始ADC转换
    HAL_ADC_Start(&adc_handle);
    //轮询方式等待ADC转换完成
    HAL_ADC_PollForConversion(&adc_handle, 10);
    //获取ADC转换结果
    return (uint16_t)HAL_ADC_GetValue(&adc_handle);
}

//光敏传感器初始化
void ls_init(void)
{
    adc_init();
}

//获取光敏传感器的值
uint8_t ls_get_value(void)
{
    //将ADC值转化为100以内的数,光照值越大,数值越大
    return (uint8_t)(100 - adc_get_result(ADC_CHANNEL_1) * 100 / 4096);  
}

light_sensor.h

c 复制代码
#ifndef __LIGHT_SENSOR_H__
#define __LIGHT_SENSOR_H__

#include "sys.h"

//ADC配置初始化
void adc_init(void);
//获取ADC的值
uint32_t adc_get_result(uint32_t ch);
//光敏传感器初始化
void ls_init(void);
//获取光敏传感器的值
uint8_t ls_get_value(void);

#endif

oled.c

c 复制代码
#include "oled.h"
#include "delay.h"
#include "font.h"

//OLED相关GPIO初始化
void oled_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能SCL和SDA引脚时钟
    OLED_I2C_SCL_CLK();
    OLED_I2C_SDA_CLK();
    
    //GPIO初始化配置
    gpio_initstruct.Pin = OLED_I2C_SCL_PIN;         //SCL对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(OLED_I2C_SCL_PORT, &gpio_initstruct);
    
    gpio_initstruct.Pin = OLED_I2C_SDA_PIN;         //SDA对应引脚
    HAL_GPIO_Init(OLED_I2C_SDA_PORT, &gpio_initstruct);
    
    
}
//I2C起始信号
void oled_i2c_start(void)
{
    OLED_SCL_SET();
    OLED_SDA_SET();
    OLED_SDA_RESET();
    OLED_SCL_RESET();
}
//I2C停止信号
void oled_i2c_stop(void)
{
    OLED_SCL_SET();
    OLED_SDA_RESET();
    OLED_SDA_SET();
}
//I2C应答信号
void oled_i2c_ack(void)
{
    OLED_SCL_SET();
    OLED_SCL_RESET();
}
//I2C写字节
void oled_i2c_write_byte(uint8_t data)
{
    uint8_t i, tmp;
    tmp = data;
    //往SDA总线上循环写数据位,高位先行
    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();
    }
}
//OLED写命令
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();
}
//OLED写数据
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();
}
//OLED初始化
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);
        }
    }
}
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{
    uint8_t i,j, page;
    //ASCII码相对第一个空格字符偏移
    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]);
            }
        }
    }
}
//OLED显示字符串
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++;
    }
}
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N)
{
    uint8_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);
}

//OLED显示图片
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]);
    }
}

oled.h

c 复制代码
#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)

//OLED初始化
void oled_init(void);
//I2C写命令
void oled_write_cmd(uint8_t cmd);
//I2C写数据
void oled_write_data(uint8_t data);
//循环填充
void oled_fill(uint8_t data);
//设置坐标
void oled_set_cursor(uint8_t x, uint8_t y);
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);
//OLED显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size);
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N);
//OLED显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp);
//上电界面初始化
void oled_show_init(void);

#endif

timer.c

c 复制代码
#include "timer.h"
#include "led.h"

//定义坐下时间
uint32_t sit_time = 0;
//定义定时器状态
uint8_t timer_running = 0;

TIM_HandleTypeDef timer_handle = {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初始化函数,在HAL_TIM_Base_Init中调用,无须自己调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM4){
        //开启时钟
        __HAL_RCC_TIM2_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++;
//        led1_toggle();
    }
}

//启动定时器
void timer_start(void)
{
    HAL_TIM_Base_Start_IT(&timer_handle);
    timer_running = 1;
}

//停止定时器
void timer_stop(void)
{
    HAL_TIM_Base_Stop_IT(&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;
}

timer.h

c 复制代码
#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);

#endif

六、实物图

相关推荐
炸膛坦客3 小时前
Cortex-M3-STM32F1 开发:(三十九)DMA详细介绍(3):相关寄存器、库函数介绍,配置步骤,以及内存到内存和内存到外设的实例
stm32·单片机·嵌入式硬件
BMS小旭4 小时前
CubeMx-GPIO学习
单片机·学习
清风6666666 小时前
基于单片机的PID调节脉动真空灭菌器上位机远程监控设计
数据库·单片机·毕业设计·nosql·课程设计·期末大作业
polarislove02147 小时前
9.6 [定时器]超声波测距实验-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
一路往蓝-Anbo8 小时前
C语言从句柄到对象 (六) —— 继承与 HAL:父类指针访问子类数据
c语言·开发语言·stm32·嵌入式硬件·物联网
一路往蓝-Anbo10 小时前
C语言从句柄到对象 (七) —— 给对象加把锁:RTOS 环境下的并发安全
java·c语言·开发语言·stm32·单片机·嵌入式硬件·算法
一路往蓝-Anbo11 小时前
C语言从句柄到对象 (八) —— 当对象会说话:观察者模式与事件链表
c语言·开发语言·数据结构·stm32·单片机·观察者模式·链表
polarislove021411 小时前
9.5 [定时器]输入捕获-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
国科安芯11 小时前
核工业检测系统通信链路的国产化元器件安全等级评估
运维·网络·人工智能·单片机·嵌入式硬件·安全·安全性测试