【嵌入式-stm32电位器控制以及旋转编码器控制LED亮暗】

嵌入式-stm32电位器控制LED亮暗

源码框架取自江协科技,在此基础上做扩展开发。

任务1

本文主要介绍利用stm32f103C8T6实现电位器控制PWM的占空比大小来改变LED亮暗程度,按键实现使用定时器非阻塞式,其中一个按键切换3个LED的控制状态,另一个按键是重置当前的LED为熄灭状态。

代码1

Key.c

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "oled.h"
#include "PWM.h"
#include "AD.h"
#include "Key.h"
#include <stdio.h>

extern uint16_t ADValue;			//定义AD值变量
uint8_t Key_Num;
/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
  */
void Key_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
}  



// 定义模式枚举  
typedef enum {  
    MODE_PWM_CH2 = 0,  
    MODE_PWM_CH3,  
    MODE_PWM_CH4,  
    MODE_MAX  
} PWM_MODE;  

// 全局变量  
volatile PWM_MODE currentMode = MODE_PWM_CH2;  
volatile uint16_t pwmValue = 0;  
volatile uint8_t resetFlag = 0;  
volatile uint8_t systemActive = 0;  //新增系统激活标志

// 初始化显示函数  
void Initial_Display(void) {  
    // 清屏  
    OLED_Clear();  
    
		// 显示初始状态  
   OLED_ShowString(1, 1, "System Ready");  
   OLED_ShowString(2, 1, "Active KEY1 ");  
   
   // 初始化时关闭所有LED  
   PWM_SetCompare2(0);  
   PWM_SetCompare3(0);  
   PWM_SetCompare4(0);   
}  
  
uint8_t Key_GetNum(void)
{
	uint8_t Temp;           
	Temp = Key_Num;         //读取按键键值
	Key_Num = 0; 					  //清零,防止重复触发
	return Temp;
}

uint8_t Key_GetState(void)
{
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == 0)
	{
		return 1;
	}
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_10) == 0)
	{
		return 2;
	}
	return 0;   //无按键按下
}

void Key_Tick(void)         
{
	static uint8_t Count; //静态计数器,记录中断次数
	static uint8_t CurrState, PrevState;
	
	Count++;
	if(Count >= 20)   //20ms执行一次按键扫描(中断周期为1ms)
	{
		Count = 0;
		PrevState = CurrState;         //保存前一次按键状态
		CurrState = Key_GetState();    //读取当前按键状态
		
		//检测按键释放动作(下降沿)
		if(CurrState == 0 && PrevState != 0)
		{
			Key_Num = PrevState;    //记录按键值(1或者2)
		}
	}
}


// 设置PWM的函数  
void SetPWM(uint16_t value) {  
    switch (currentMode) {  
        case MODE_PWM_CH2:  
            PWM_SetCompare2(value);  
            break;  
        case MODE_PWM_CH3:  
            PWM_SetCompare3(value);  
            break;  
        case MODE_PWM_CH4:  
            PWM_SetCompare4(value);  
            break;  
    }  
}  

// 更新显示模式函数  
void Update_ModeDisplay(void) {  
    // 清除原有模式显示  
    OLED_Clear();  
    
    // 根据当前模式显示  
    switch (currentMode) {  
        case MODE_PWM_CH2:  
            OLED_ShowString(1, 1, "Mode: CH2");  
            break;  
        case MODE_PWM_CH3:  
            OLED_ShowString(1, 1, "Mode: CH3");  
            break;  
        case MODE_PWM_CH4:  
            OLED_ShowString(1, 1, "Mode: CH4");  
            break;  
    }  
		 // 显示初始PWM值  
   OLED_ShowString(2, 1, "PWM:   0");  
}  

/*OLED显示70.5%函数*/
void ShowPwm_Percent(uint8_t Line, uint8_t Colum, uint16_t pwmValue)
{
	char str[16];
	uint16_t integer = pwmValue / 10;  //整数部分如70
	uint16_t decimal = pwmValue % 10;  //小鼠部分如5
	sprintf(str, "%4d.%1d%%",integer,decimal);
	OLED_ShowString(Line,Colum,str);
}

// 按键控制函数  
void Key_control(void) {  
    uint8_t keyNum = Key_GetNum();  
    
    // 处理按键1:模式切换  
    if (keyNum == 1) {  
			  // 重置标志清零  
        resetFlag = 0; 
				if(systemActive == 0)
				{
					systemActive = 1;
					currentMode = MODE_PWM_CH2;
					Update_ModeDisplay();
				}
 
        else
				{
        // 切换模式  
        currentMode++;  
        if (currentMode >= MODE_MAX) {  
            currentMode = MODE_PWM_CH2;  
        }  
        
        // 更新模式显示  
        Update_ModeDisplay(); 
			}				
    }  
    
    // 处理按键2:重置为全暗  
    if (keyNum == 2) {  
        // 设置重置标志  
        resetFlag = 1;  
        
        // 将当前通道设置为0  
        SetPWM(0);  
        pwmValue = 0;  
        
        // 显示PWM值  
        OLED_ShowNum(2, 5, pwmValue, 3);  
    }  
    
    // 仅在非重置状态下读取ADC和设置PWM  
    if (resetFlag == 0 && systemActive) {  
        // 读取ADC并设置PWM  
        //uint16_t adcValue = AD_GetValue();  
        pwmValue = (AD_GetValue() * 1000)/ 4095 ;  
        
        // 设置当前通道PWM  
        SetPWM(pwmValue);  
        
        // 显示PWM值 
				 OLED_ShowNum(3, 1, pwmValue, 4);  // 直接显示pwmValue的值			
			   ShowPwm_Percent(2, 4, pwmValue);
        //OLED_ShowNum(2, 5, pwmValue, 3);  
    }  
}  

Key.h

c 复制代码
#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);
uint8_t Key_GetNum(void);
void Key_control(void);
void Initial_Display(void);
void SetPWM(uint16_t value);
void Key_Tick(void);
uint8_t Key_GetState(void);

#endif

Timer.c

c 复制代码
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM3, ENABLE);
}

/*
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

Timer.h

c 复制代码
#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

PWM.c

c 复制代码
#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */

void TIM2_PWM_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
	  GPIO_InitTypeDef GPIO_InitStruct;	
    // 打开定时器2时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);																
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		

	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //GPIO采用复用推挽输出模式
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_2|GPIO_Pin_1; //TIM2同时产生三路PWM波 在管脚123 a11
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  //GPIO速度50MHZ
	
	GPIO_Init(GPIOA,&GPIO_InitStruct);  //初始化函数 让刚刚配置的参数 输入到对应寄存器里面
    // 配置定时器2为PWM模式
    TIM_TimeBaseStructure.TIM_Period = 999; // PWM周期
    TIM_TimeBaseStructure.TIM_Prescaler = 720; // 72MHz/(71+1) = 1MHz,计数频率为1MHz
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    // 配置TIM2通道2为PWM模式
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC2Init(TIM2, &TIM_OCInitStructure);
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);

    // 使能TIM2
    TIM_Cmd(TIM2, ENABLE);
}
/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~1000
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */

void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2 ,Compare );	//设置CCR1的值
}
void PWM_SetCompare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2 ,Compare );	//设置CCR1的值
}
void PWM_SetCompare4(uint16_t Compare)
{
	TIM_SetCompare4(TIM2 ,Compare );	//设置CCR1的值
}

PWM.h

c 复制代码
#ifndef __PWM_H
#define __PWM_H


void TIM2_PWM_Init(void);

void PWM_SetCompare2(uint16_t Compare);
void PWM_SetCompare3(uint16_t Compare);
void PWM_SetCompare4(uint16_t Compare);


#endif

main.c

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "sys.h"
#include "AD.h"
#include "PWM.h"
#include "Timer.h"

/*全局变量*/
uint16_t ADValue;			//定义AD值变量

int main(void)
{

	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Key_Init();			//按键初始化
	AD_Init();			//AD初始化
	TIM2_PWM_Init();	//定时器2PWM初始化
	Timer_Init();
	/*OLED显示静态字符*/
	Initial_Display();
	while (1)
	{
		//KeyNum=Key_GetNum();	//获取键码值
		Key_control();			//按键PWM控制
	}
}

//中断服务函数
//每次TIM3溢出时触发中断,调用Key_Tick()进行按键扫描
//清除中断标志,避免重复进入中断
void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
	{
		Key_Tick();
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

实验现象1

以下是通过电位器控制PWM输出大小的值进而调暗LED

通过网盘分享的文件:电位器改变PWM输出控制LED

链接: https://pan.baidu.com/s/1JrevfJ2GTsBqLyRb4Do39g 提取码: 6688

任务2

旋转编码器控制LED亮暗:

1、LED亮度控制:旋转编码器调节PWM占空比,控制LED亮度。

2、状态显示:OLED实时显示当前PWM占空比(格式为XX.X%)。

3、模式切换:通过独立按键切换PWM输出通道(如CH2、CH3、CH4)。

4、系统激活与重置:按键控制系统的启动和重置。

接线图片来自江协议科技

代码2

1、模块化代码架构

编码器驱动:通过外部中断检测旋转方向,更新计数值。

PWM生成:配置定时器(如TIM2)的PWM模式,动态调节占空比。

OLED显示:格式化显示占空比和模式信息。

主控制逻辑:整合按键、编码器和PWM功能,实现状态机控制。

Key.c

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "oled.h"
#include "PWM.h"
#include "AD.h"
#include "Key.h"
#include "Encoder.h"
#include <stdio.h>

uint8_t Key_Num;
signed Key_Encoder_Count = 0;
/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
  */
void Key_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
}  



// 定义模式枚举  
typedef enum {  
    MODE_PWM_CH2 = 0,  
    MODE_PWM_CH3,  
    MODE_PWM_CH4,  
    MODE_MAX  
} PWM_MODE;  

// 全局变量  
volatile PWM_MODE currentMode = MODE_PWM_CH2;  
volatile uint16_t pwmValue = 0;  
volatile uint8_t resetFlag = 0;  
volatile uint8_t systemActive = 0;  //新增系统激活标志

// 初始化显示函数  
void Initial_Display(void) {  
    // 清屏  
    OLED_Clear();  
    
		// 显示初始状态  
   OLED_ShowString(1, 1, "System Ready");  
   OLED_ShowString(2, 1, "Active KEY1 ");  
   
   // 初始化时关闭所有LED  
   PWM_SetCompare2(0);  
   PWM_SetCompare3(0);  
   PWM_SetCompare4(0);   
}  
  
uint8_t Key_GetNum(void)
{
	uint8_t Temp;           
	Temp = Key_Num;         //读取按键键值
	Key_Num = 0; 					  //清零,防止重复触发
	return Temp;
}

uint8_t Key_GetState(void)
{
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == 0)
	{
		return 1;
	}
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_10) == 0)
	{
		return 2;
	}
	return 0;   //无按键按下
}

void Key_Tick(void)         
{
	static uint8_t Count; //静态计数器,记录中断次数
	static uint8_t CurrState, PrevState;
	
	Count++;
	if(Count >= 20)   //20ms执行一次按键扫描(中断周期为1ms)
	{
		Count = 0;
		PrevState = CurrState;         //保存前一次按键状态
		CurrState = Key_GetState();    //读取当前按键状态
		
		//检测按键释放动作(下降沿)
		if(CurrState == 0 && PrevState != 0)
		{
			Key_Num = PrevState;    //记录按键值(1或者2)
		}
	}
}


// 设置PWM的函数  
void SetPWM(uint16_t value) {  
    switch (currentMode) {  
        case MODE_PWM_CH2:  
            PWM_SetCompare2(value);  
            break;  
        case MODE_PWM_CH3:  
            PWM_SetCompare3(value);  
            break;  
        case MODE_PWM_CH4:  
            PWM_SetCompare4(value);  
            break;  
    }  
}  

// 更新显示模式函数  
void Update_ModeDisplay(void) {  
    // 清除原有模式显示  
    OLED_Clear();  
    
    // 根据当前模式显示  
    switch (currentMode) {  
        case MODE_PWM_CH2:  
            OLED_ShowString(1, 1, "Mode: CH2");  
            break;  
        case MODE_PWM_CH3:  
            OLED_ShowString(1, 1, "Mode: CH3");  
            break;  
        case MODE_PWM_CH4:  
            OLED_ShowString(1, 1, "Mode: CH4");  
            break;  
    }  
		 // 显示初始PWM值  
   OLED_ShowString(2, 1, "PWM:   0");  
}  

/*OLED显示70.5%函数*/
void ShowPwm_Percent(uint8_t Line, uint8_t Colum, uint16_t pwmValue)
{
	char str[16];
	uint16_t integer = pwmValue / 10;  //整数部分如70
	uint16_t decimal = pwmValue % 10;  //小鼠部分如5
	sprintf(str, "%4d.%1d%%",integer,decimal);
	OLED_ShowString(Line,Colum,str);
}

// 按键控制函数  
void Key_control(void) {  
    uint8_t keyNum = Key_GetNum();  
    
    // 处理按键1:模式切换  
    if (keyNum == 1) {  
			  // 重置标志清零  
        resetFlag = 0; 
				if(systemActive == 0)
				{
					systemActive = 1;
					currentMode = MODE_PWM_CH2;
					Update_ModeDisplay();
				}
 
        else
				{
        // 切换模式  
        currentMode++;  
        if (currentMode >= MODE_MAX) {  
            currentMode = MODE_PWM_CH2;  
        }  
        
        // 更新模式显示  
        Update_ModeDisplay(); 
			}				
    }  
    
    // 处理按键2:重置为全暗  
    if (keyNum == 2) {  
        // 设置重置标志  
        resetFlag = 1;  
        
        // 将当前通道设置为0  
        SetPWM(0);  
        pwmValue = 0;  
        
        // 显示PWM值  
        OLED_ShowNum(2, 5, pwmValue, 3);  
    }  
    
    // 仅在非重置状态下读取ADC和设置PWM  
    if (resetFlag == 0 && systemActive) {  
				
				Key_Encoder_Count += Encoder_Get();
				if(Key_Encoder_Count < 0)
				{
					Key_Encoder_Count = 0;
				}
				if(Key_Encoder_Count > 100)
				{
					Key_Encoder_Count = 100;
				}
        pwmValue = (Key_Encoder_Count * 10) ;  
        
        // 设置当前通道PWM  
        SetPWM(pwmValue);  
        
        // 显示PWM值 
				 OLED_ShowNum(3, 1, pwmValue, 4);  // 直接显示pwmValue的值			
			   ShowPwm_Percent(2, 4, pwmValue);
        //OLED_ShowNum(2, 5, pwmValue, 3);  
    }  
}  

Key.h

c 复制代码
#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);
uint8_t Key_GetNum(void);
void Key_control(void);
void Initial_Display(void);
void SetPWM(uint16_t value);
void Key_Tick(void);
uint8_t Key_GetState(void);

#endif

main.c

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "sys.h"
#include "AD.h"
#include "PWM.h"
#include "Timer.h"
#include "Encoder.h"

int main(void)
{

	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Key_Init();			//按键初始化
	TIM2_PWM_Init();	//定时器2PWM初始化
	Timer_Init();
	Encoder_Init();
	/*OLED显示静态字符*/
	Initial_Display();
	while (1)
	{
		Key_control();			//按键PWM控制
	}
}

//中断服务函数
//每次TIM3溢出时触发中断,调用Key_Tick()进行按键扫描
//清除中断标志,避免重复进入中断
void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
	{
		Key_Tick();
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

实验现象2

问题与解决

一上电程序卡死,原因是Timer3的中断服务函数忘记清除相应的标志位。

总结

旋转编码器和电位器控制LED亮暗的区别
核心逻辑在于旋转编码器时中断服务函数检测旋转方向,更新计数值,而电位器时ADC采样。

相关推荐
清风66666617 分钟前
基于单片机的双档输出数字直流电压源设计
单片机·mongodb·毕业设计·nosql·课程设计
牛马大师兄39 分钟前
STM32独立看门狗IWDG与窗口看门狗WWDG知识梳理笔记
笔记·stm32·单片机·嵌入式硬件·嵌入式·看门狗
夜月yeyue1 小时前
STM32 Flash 访问加速器详解(ART Accelerator)
linux·单片机·嵌入式硬件·uboot·bootloard
A9better1 小时前
嵌入式开发学习日志37——stm32之USART
stm32·嵌入式硬件·学习
国科安芯5 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计5 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux12 小时前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘12 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin12 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远202113 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos