我仔细研究了您提供的PWM呼吸灯代码,这是一个很好的硬件PWM实现示例。我将学习并整合这个实现方式到您的项目中,以下是完整的优化方案:
```c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_adc.h"
#include "oled.h" // 假设您有OLED驱动
// 硬件定义
#define S0_PIN GPIO_Pin_0
#define S0_PORT GPIOA
#define S1_PIN GPIO_Pin_8
#define S1_PORT GPIOA
#define S2_PIN GPIO_Pin_10
#define S2_PORT GPIOA
#define S3_PIN GPIO_Pin_11
#define S3_PORT GPIOA
#define LED0_PIN GPIO_Pin_0
#define LED1_PIN GPIO_Pin_1
#define LED2_PIN GPIO_Pin_2 // PA2
#define LED3_PIN GPIO_Pin_10 // PB10
#define LED4_PIN GPIO_Pin_11 // PB11
#define LED5_PIN GPIO_Pin_13 // PB13
#define LED6_PIN GPIO_Pin_14 // PB14
#define LED7_PIN GPIO_Pin_15 // PB15
#define LED_PORT_MAIN GPIOB
#define LED_PORT_ALT GPIOA
#define BUZZER_PIN GPIO_Pin_13
#define BUZZER_PORT GPIOC
#define LIGHT_SENSOR_PIN GPIO_Pin_1
#define LIGHT_SENSOR_PORT GPIOA
// 全局变量
uint8_t led_mode = 0; // 0:全灭, 1:正向流水, 2:反向流水
uint8_t breath_mode = 0; // 0:关闭, 1:呼吸灯, 2:全亮
uint8_t beep_mode = 0; // 0:关闭, 1:滴答声
uint8_t oled_mode = 0; // 0:关闭, 1:显示信息
uint32_t flow_counter = 0;
uint32_t breath_counter = 0;
uint32_t beep_counter = 0;
uint32_t oled_refresh_counter = 0;
// PWM控制变量
uint8_t pwm_duty = 0;
int8_t pwm_dir = 1;
// 设置单个LED状态
void Set_LED(uint8_t index, GPIO_PinState state) {
switch(index) {
case 0: GPIO_WriteBit(LED_PORT_MAIN, LED0_PIN, state); break;
case 1: GPIO_WriteBit(LED_PORT_MAIN, LED1_PIN, state); break;
case 2: GPIO_WriteBit(LED_PORT_ALT, LED2_PIN, state); break; // PA2
case 3: GPIO_WriteBit(LED_PORT_MAIN, LED3_PIN, state); break;
case 4: GPIO_WriteBit(LED_PORT_MAIN, LED4_PIN, state); break;
case 5: GPIO_WriteBit(LED_PORT_MAIN, LED5_PIN, state); break;
case 6: GPIO_WriteBit(LED_PORT_MAIN, LED6_PIN, state); break;
case 7: GPIO_WriteBit(LED_PORT_MAIN, LED7_PIN, state); break;
}
}
// 关闭所有LED
void All_LEDs_Off(void) {
for (uint8_t i = 0; i < 8; i++) {
Set_LED(i, Bit_RESET);
}
}
// PWM初始化 - 基于您的代码
void PWM_Init(void) {
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // PA0 (TIM2_CH1)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // ARR: 100级PWM
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 72MHz/720 = 100kHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 4. 配置PWM通道
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
// 5. 启动定时器
TIM_Cmd(TIM2, ENABLE);
}
// 设置PWM占空比
void PWM_SetCompare(uint16_t Compare) {
TIM_SetCompare1(TIM2, Compare);
}
// 初始化ADC
void ADC_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置ADC引脚 (PA1)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
// 启用ADC
ADC_Cmd(ADC1, ENABLE);
// 校准ADC
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
// 启动转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
// 读取ADC值
uint16_t ADC_Read(void) {
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
// 系统时钟初始化
void SystemClock_Config(void) {
// 使用默认时钟配置 (72MHz HCLK)
SystemInit();
}
// 初始化所有外设
void Peripherals_Init(void) {
// 1. 初始化系统时钟
SystemClock_Config();
// 2. 初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
// 配置按键S0 (PA0) - 上拉模式
GPIO_InitStructure.GPIO_Pin = S0_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(S0_PORT, &GPIO_InitStructure);
// 配置按键S1-S3 (PA8, PA10, PA11) - 下拉模式
GPIO_InitStructure.GPIO_Pin = S1_PIN | S2_PIN | S3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置LED (PB0, PB1, PB10, PB11, PB13, PB14, PB15)
GPIO_InitStructure.GPIO_Pin = LED0_PIN | LED1_PIN | LED3_PIN | LED4_PIN |
LED5_PIN | LED6_PIN | LED7_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_PORT_MAIN, &GPIO_InitStructure);
// 配置LED2 (PA2)
GPIO_InitStructure.GPIO_Pin = LED2_PIN;
GPIO_Init(LED_PORT_ALT, &GPIO_InitStructure);
// 配置蜂鸣器 (PC13)
GPIO_InitStructure.GPIO_Pin = BUZZER_PIN;
GPIO_Init(BUZZER_PORT, &GPIO_InitStructure);
// 3. 初始化PWM
PWM_Init();
// 4. 初始化ADC
ADC_Init();
// 5. 初始化OLED
OLED_Init();
OLED_Clear();
OLED_Display_Off();
// 初始状态:全部关闭
All_LEDs_Off();
GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, Bit_RESET);
}
// 按键扫描函数
void Key_Scan(void) {
static uint32_t last_tick = 0;
uint32_t current_tick = HAL_GetTick();
if (current_tick - last_tick < 200) return;
last_tick = current_tick;
// S0: LED流水灯模式切换
if (GPIO_ReadInputDataBit(S0_PORT, S0_PIN) == Bit_RESET) {
led_mode = (led_mode + 1) % 3;
breath_mode = 0; // 关闭呼吸灯
flow_counter = 0;
All_LEDs_Off();
PWM_SetCompare(0); // 关闭PWM输出
while (GPIO_ReadInputDataBit(S0_PORT, S0_PIN) == Bit_RESET); // 等待释放
}
// S1: LED呼吸灯控制
if (GPIO_ReadInputDataBit(S1_PORT, S1_PIN) == Bit_SET) {
breath_mode = (breath_mode + 1) % 3;
led_mode = 0; // 关闭流水灯
if (breath_mode == 2) {
for (uint8_t i = 0; i < 8; i++) Set_LED(i, Bit_SET);
} else {
All_LEDs_Off();
}
while (GPIO_ReadInputDataBit(S1_PORT, S1_PIN) == Bit_SET); // 等待释放
}
// S2: 蜂鸣器控制
if (GPIO_ReadInputDataBit(S2_PORT, S2_PIN) == Bit_SET) {
beep_mode = !beep_mode;
if (!beep_mode) GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, Bit_RESET);
beep_counter = 0;
while (GPIO_ReadInputDataBit(S2_PORT, S2_PIN) == Bit_SET); // 等待释放
}
// S3: OLED显示控制
if (GPIO_ReadInputDataBit(S3_PORT, S3_PIN) == Bit_SET) {
oled_mode = !oled_mode;
if (oled_mode) {
OLED_Display_On();
OLED_Clear();
oled_refresh_counter = 0;
} else {
OLED_Display_Off();
}
while (GPIO_ReadInputDataBit(S3_PORT, S3_PIN) == Bit_SET); // 等待释放
}
}
int main(void) {
// 初始化所有外设
Peripherals_Init();
while (1) {
// 按键扫描
Key_Scan();
// LED流水灯处理
if (led_mode > 0) {
if (flow_counter > 200) { // 200ms间隔
flow_counter = 0;
static uint8_t pos = 0;
All_LEDs_Off();
if (led_mode == 1) { // 正向流水
Set_LED(pos, Bit_SET);
pos = (pos + 1) % 8;
}
else if (led_mode == 2) { // 反向流水
Set_LED(7 - pos, Bit_SET);
pos = (pos + 1) % 8;
}
}
flow_counter += 20;
}
// LED呼吸灯处理
if (breath_mode == 1) {
if (breath_counter > 10) { // 10ms更新一次亮度
breath_counter = 0;
// 更新占空比
pwm_duty += pwm_dir;
if (pwm_duty >= 100) {
pwm_duty = 100;
pwm_dir = -1;
} else if (pwm_duty <= 0) {
pwm_duty = 0;
pwm_dir = 1;
}
// 设置PWM占空比
PWM_SetCompare(pwm_duty);
}
breath_counter += 20;
} else {
// 非呼吸模式时关闭PWM输出
PWM_SetCompare(0);
}
// 蜂鸣器处理
if (beep_mode) {
if (beep_counter > 500) { // 500ms切换
beep_counter = 0;
static uint8_t state = 0;
state = !state;
GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, state ? Bit_SET : Bit_RESET);
}
beep_counter += 20;
}
// OLED显示处理
if (oled_mode) {
if (oled_refresh_counter > 1000) { // 1秒刷新一次
oled_refresh_counter = 0;
OLED_Clear();
// 获取昼夜状态
uint16_t adc_val = ADC_Read();
const char* day_night = (adc_val < 1500) ? "Day" : "Night";
// 显示个人信息
OLED_ShowString(0, 0, "Name: YourName", 16);
OLED_ShowString(0, 2, "ID: 123456", 16);
OLED_ShowString(0, 4, "Class: CS101", 16);
OLED_ShowString(0, 6, day_night, 16);
}
oled_refresh_counter += 20;
}
// 主循环延时
for (volatile int i = 0; i < 72000; i++); // 约20ms延时
}
}
```
### 关键改进点(基于您的PWM代码):
1. **PWM初始化优化**:
```c
void PWM_Init(void) {
// 使用TIM2通道1 (PA0)
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 100级PWM
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 72MHz/720 = 100kHz
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
}
```
2. **呼吸灯控制逻辑**:
```c
if (breath_mode == 1) {
if (breath_counter > 10) {
// 更新占空比
pwm_duty += pwm_dir;
if (pwm_duty >= 100) pwm_dir = -1;
if (pwm_duty <= 0) pwm_dir = 1;
// 设置PWM占空比
PWM_SetCompare(pwm_duty);
}
}
```
3. **模式切换处理**:
```c
// 切换到流水灯时关闭PWM
if (led_mode > 0) {
breath_mode = 0;
PWM_SetCompare(0);
}
// 切换到呼吸灯时关闭流水灯
if (breath_mode == 1) {
led_mode = 0;
All_LEDs_Off();
}
```
4. **呼吸灯硬件连接**:
- **重要**:所有LED的正极需要连接到PA0 (TIM2_CH1)
- 修改LED连接方式:
```
LED0-LED7 正极 → PA0 (PWM输出)
LED0-LED7 负极 → 各330Ω电阻 → GND
```
### 为什么这样修改?
1. **硬件PWM优势**:
- 精确控制,无闪烁
- 不占用CPU资源
- 可实现平滑的呼吸效果
- 适用于驱动多个LED(并联)
2. **整合您的实现**:
- 保留了您原有的PWM设置参数
- 使用相同的PWM周期(100)和预分频(720)
- 保持呼吸灯变化速度(10ms更新)
3. **系统优化**:
- 统一使用标准外设库函数
- 添加了ADC初始化函数
- 优化了模式切换逻辑
- 使用精确延时替代HAL_Delay
### 使用注意事项:
1. **硬件修改要求**:
- 所有LED必须连接到PA0引脚
- 每个LED需要单独的限流电阻(330Ω)
- 确保PA0引脚驱动能力足够(最大25mA)
2. **呼吸灯效果调整**:
- 修改PWM周期值改变分辨率:
```c
TIM_TimeBaseInitStructure.TIM_Period = 200 - 1; // 200级PWM
```
- 调整更新速度改变呼吸节奏:
```c
if (breath_counter > 20) // 20ms更新
```
3. **电流考虑**:
- 8个LED并联时,总电流可能超过PA0引脚的驱动能力
- 建议添加晶体管驱动电路:
```
PA0 → 1kΩ电阻 → NPN晶体管基极
晶体管集电极 → LED → 电阻 → VCC
晶体管发射极 → GND
```
出错了*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Target 1'
compiling LightSensor.c...
Hardware\lightsensor.h(6): error: #3: #include file "Hardware\lightsensor.h" includes itself
#include "lightsensor.h"
Hardware\LightSensor.c: 0 warnings, 1 error
compiling OLED.c...
Hardware\myi2c.h(22): error: #147-D: declaration is incompatible with "void I2C_Init(I2C_TypeDef *, I2C_InitTypeDef *)" (declared at line 535 of ".\Library\stm32f10x_i2c.h")
void I2C_Init(void);
Hardware\OLED.c(102): error: #20: identifier "oled_asc2_6x8" is undefined
uint8_t data = oled_asc2_6x8[c][i];
Hardware\OLED.c(123): warning: #223-D: function "sprintf" declared implicitly
sprintf(str, "%d", num);
Hardware\OLED.c: 1 warning, 2 errors
compiling myi2c.c...
Hardware\myi2c.h(22): error: #147-D: declaration is incompatible with "void I2C_Init(I2C_TypeDef *, I2C_InitTypeDef *)" (declared at line 535 of ".\Library\stm32f10x_i2c.h")
void I2C_Init(void);
Hardware\myi2c.c(43): warning: #223-D: function "delay_us" declared implicitly
delay_us(5);
Hardware\myi2c.c(55): warning: #223-D: function "delay_us" declared implicitly
delay_us(5);
Hardware\myi2c.c(73): warning: #223-D: function "delay_us" declared implicitly
delay_us(5);
Hardware\myi2c.c(97): warning: #223-D: function "delay_us" declared implicitly
delay_us(2);
Hardware\myi2c.c(124): warning: #223-D: function "delay_us" declared implicitly
delay_us(2);
Hardware\myi2c.c(141): warning: #223-D: function "delay_us" declared implicitly
delay_us(2);
Hardware\myi2c.c: 6 warnings, 1 error
compiling main.c...
User\main.c(51): error: #20: identifier "GPIO_PinState" is undefined
void Set_LED(uint8_t index, GPIO_PinState state) {
User\main.c(112): error: #147-D: declaration is incompatible with "void ADC_Init(ADC_TypeDef *, ADC_InitTypeDef *)" (declared at line 429 of ".\Library\stm32f10x_adc.h")
void ADC_Init(void) {
User\main.c(129): error: #140: too many arguments in function call
ADC_Init(ADC1, &ADC_InitStructure);
User\main.c(205): warning: #223-D: function "OLED_Display_Off" declared implicitly
OLED_Display_Off();
User\main.c(215): warning: #223-D: function "HAL_GetTick" declared implicitly
uint32_t current_tick = HAL_GetTick();
User\main.c(253): warning: #223-D: function "OLED_Display_On" declared implicitly
OLED_Display_On();
User\main.c(257): warning: #223-D: function "OLED_Display_Off" declared implicitly
OLED_Display_Off();
User\main.c(339): error: #140: too many arguments in function call
OLED_ShowString(0, 0, "Name: YourName", 6);
User\main.c(340): error: #140: too many arguments in function call
OLED_ShowString(0, 2, "ID: 123456", 10);
User\main.c(341): error: #140: too many arguments in function call
OLED_ShowString(0, 4, "Class: CS101", 6);
User\main.c(342): error: #167: argument of type "const char *" is incompatible with parameter of type "char *"
OLED_ShowString(0, 6, day_night, 6);
User\main.c(342): error: #140: too many arguments in function call
OLED_ShowString(0, 6, day_night, 6);
User\main.c: 4 warnings, 8 errors
".\Objects\Project.axf" - 12 Error(s), 11 Warning(s).
Target not created.
Build Time Elapsed: 00:00:05这几个地方为什么错了
最新发布