stm32定时器扫描数码管(无锁存器)和矩阵按键

如果不使用定时器,会有很多冲突。比如数码管显示不正常,按键响应问题。

定时器时间消隐效果还不错,根据实验室另一个光伏项目的朋友用stc15代码改过来的。

数码管

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"

#define DX0 PAout(0)
#define DX1 PAout(1)
#define DX2 PAout(2)
#define DX3 PAout(3)
#define DX4 PAout(4)
#define DX5 PAout(5)
#define DX6 PAout(6)
#define DX7 PAout(7)
#define LED1  PAout(8)
#define LED2  PAout(9)
#define LED3  PAout(10)
#define LED4  PAout(11)
#define LED5  PAout(12)
#define LED6  PCout(8)
#define Beep PGout(9)

#define D0 PEout(0)
#define D1 PEout(1)
#define D2 PEout(2)
#define D3 PEout(3)



void LED_Init(void);
void Display(u8 index);


		 				    
#endif
#include "led.h"

u8 seg_tab[17] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,\
  0x88,0x83,0xC6,0xA1,0x86,0x8E,0xBF};
void LED_Init(void)
{
 
    GPIO_InitTypeDef  GPIO_InitStructure;
	  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);   
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG, ENABLE);	
		
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;				
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		//推挽输出
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 
	 GPIO_Init(GPIOA, &GPIO_InitStructure);				
	 GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12);
  
	 GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
 	 GPIO_Init(GPIOE, &GPIO_InitStructure);
	 GPIO_SetBits(GPIOE,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
  
   GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_8;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	 GPIO_Init(GPIOC, &GPIO_InitStructure);
	 GPIO_SetBits(GPIOC,GPIO_Pin_8);
   
   GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	 GPIO_Init(GPIOG, &GPIO_InitStructure);
	 GPIO_SetBits(GPIOG,GPIO_Pin_9);

  TIM_DeInit( TIM6 );                  //将TIM6寄存器重置为缺省值
	TIM_InternalClockConfig( TIM6 );   
  TIM_TimeBaseStructure.TIM_Period=10;      //扫描周期5ms  
  TIM_TimeBaseStructure.TIM_Prescaler=35999;   
  TIM_TimeBaseStructure.TIM_ClockDivision=0;
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM6,&TIM_TimeBaseStructure); 
  
  TIM_ClearFlag( TIM6, TIM_FLAG_Update ); //清除溢出中断标志位
	TIM_ARRPreloadConfig( TIM6, DISABLE ); //禁止ARR预装载缓冲器
	TIM_ITConfig( TIM6,TIM_IT_Update,ENABLE );// 打开更新事件中断
  TIM_Cmd(TIM6,ENABLE);                     //使能TIM6
                 
}

void Display(u8 index)	
{
	DX0 = seg_tab[index]&0x01;
	DX1 = (seg_tab[index]>>1)&0x01;
	DX2 = (seg_tab[index]>>2)&0x01;
	DX3 = (seg_tab[index]>>3)&0x01;
	DX4 = (seg_tab[index]>>4)&0x01;
	DX5 = (seg_tab[index]>>5)&0x01;
	DX6 = (seg_tab[index]>>6)&0x01;
	DX7 = (seg_tab[index]>>7)&0x01;
}
  • 中断服务函数
void TIM6_IRQHandler()    //数码管扫描
{ 
    if(TIM_GetITStatus(TIM6,TIM_IT_Update)!=RESET)
  {
    D0=D1=D2=D3=1;
    switch(s)
    {
      case 1:D0=0;Display(t1);break;
      case 2:D1=0;Display(t2);break;
      case 3:D2=0;Display(t3);break;
      case 4:D3=0;Display(t4);break;
    }
    s++; if(s>4) s=1;
    TIM_ClearITPendingBit(TIM6,TIM_IT_Update);    
  }
}
#include "key.h"
#include "led.h"
#include "sys.h" 
#define PORT GPIOF->IDR
//行接PF0-3,列接FD4-7
void KEY_Init() 
{ 
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);   //TIM7按键扫描
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PF口时钟
  
  TIM_DeInit( TIM7 );                  //将TIM6寄存器重置为缺省值
	TIM_InternalClockConfig( TIM7 );   
  TIM_TimeBaseStructure.TIM_Period=20;      //扫描周期10ms
  TIM_TimeBaseStructure.TIM_Prescaler=35999;   
  TIM_TimeBaseStructure.TIM_ClockDivision=0;
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM7,&TIM_TimeBaseStructure); 
  
  TIM_ClearFlag( TIM7, TIM_FLAG_Update ); //清除溢出中断标志位
	TIM_ARRPreloadConfig( TIM7, DISABLE ); //禁止ARR预装载缓冲器
	TIM_ITConfig( TIM7,TIM_IT_Update,ENABLE );// 打开更新事件中断
  TIM_Cmd(TIM7,ENABLE);                     
 
}

s8 scan_MatrixKey()
{
 u8 column;//列
 u8 row;//行
 u8 tmp;//临时变量
 s8 MatrixKey_value = 20;//初始值不能为0~15 
 static u8 key_count = 0;//按键被中断扫描的次数

 ///IO口的配置/
 //低8位为推挽输出
 GPIOF->CRL &= 0X00000000;
 GPIOF->CRL |= 0X33333333;
 //初值:低4位为低,次低4位为高
 GPIOF->ODR &= 0XFF00;
 GPIOF->ODR |= 0X00F0;
 //次低4位为上拉输入
 GPIOF->CRL &= 0X0000FFFF;
 GPIOF->CRL |= 0X88880000;
  
 tmp = PORT;//必须要
 if (tmp != 0XF0)//如果有键按下
 {
  //防止长按时,持续自增导致变量溢出
  if (key_count <= 2) 
  {
    key_count++;
  }
 }
 //若产生抖动按键被抬起则计数清0 
 else
  key_count = 0;
 
 //若按键连续2次扫描均处于按下状态
 //则认为按键确实被按下了
 if (key_count == 2)
 {
 column = tmp & 0X00F0;//获取列号 
 
 ///IO口的配置/
 //低8位为推挽输出
 GPIOF->CRL &= 0X00000000;
 GPIOF->CRL |= 0X33333333;
 //翻转:低4位为高,次低4位为低
 GPIOF->ODR &= 0XFF00;
 GPIOF->ODR |= 0X000F;//低4位为高,次低4位为低
 //低4位为上拉输入
 GPIOF->CRL &= 0XFFFF0000;
 GPIOF->CRL |= 0X00008888; 
 
 row = PORT & 0X000F;//获取行号
 switch (column | row)//column|row为按键被按下对应端口的编码
 { 
 //按键对应的码表(可以根据需求调整欲返回的键值) 
 case 0XEE: MatrixKey_value = 1; break;   //S1
 case 0XDE: MatrixKey_value = 2; break;
 case 0XBE: MatrixKey_value = 3; break;
 case 0X7E: MatrixKey_value = 4; break;
 case 0XED: MatrixKey_value = 5; break;
 case 0XDD: MatrixKey_value = 6; break;
 case 0XBD: MatrixKey_value = 7; break;
 case 0X7D: MatrixKey_value = 8; break;    //S8
 case 0XEB: MatrixKey_value = 9; break;
 case 0XDB: MatrixKey_value = 10; break;
 case 0XBB: MatrixKey_value = 11; break;
 case 0X7B: MatrixKey_value = 12; break;
 case 0XE7: MatrixKey_value = 13; break;
 case 0XD7: MatrixKey_value = 14; break;
 case 0XB7: MatrixKey_value = 15; break;
 case 0X77: MatrixKey_value = 16; break;  //S16
 default :   break;
 }
 } 
 //若没有按键被按下(已松手)则扫描次数清0
 //方便下次按下扫描计数
 if ((PORT & 0X00FF) == 0x00F0)
 {
  key_count = 0;  
 }
 return MatrixKey_value;
}

记得添加外部全局变量 

这个按键扫描函数是从别人博客上用的。

void key_FUC()
{
  key_value=scan_MatrixKey();
  if(key_value==1)            //S1模式选择
  {
    flag_mode++;
    LCD_Clear(BACK_COLOR);delay_ms(25);
    if(flag_mode==1)      {dianji(0,5999); t3=3;t4=0; }
    else if(flag_mode==2) { delay_ms(25);dianji(1,6999); t3=6;t4=0; }
    else if(flag_mode==3) { delay_ms(25);dianji(1,5999);t3=1;t4=0; }
    else { LED1=LED2=LED3=LED4=LED5=LED6=1; LED4=0;dianji(1,7999);t3=1;t4=0; }
    
    if(flag_mode>5)                  //暂停后再次按下,
    {
      
      if(flag_mode==7) flag_mode=0;   //之前遇到bug,模式走完stop后再次按S1就不显示界面了,我的算法很垃圾
      flag_mode= mode_t+1;          //stop模式按下S1后,到下一个模式
      if(flag_mode>4) flag_mode=0;  //防止立即进入模式1导致计数从10开始的bug,目前还没想修复 
    }
    
  }
  
  else if(key_value==2)       //重复进入自检,但是再次按任意键时得两次
  {
    LCD_Clear(BACK_COLOR);
    dianji(0,9999);
    flag_mode=7;
  }
  else if(key_value==3)            //停止按钮
  {
    dianji(0,9999);
    flag_mode=5;
  }
    else if(key_value==4)          
  {
   bj_set-=1;
  }
    else if(key_value==5)           
  {
   bj_set+=1;
  }
  
  else ;

}

void TIM7_IRQHandler()    //按键
{ 
    if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
  {
    scan_MatrixKey();
    key_FUC();
    TIM_ClearITPendingBit(TIM7,TIM_IT_Update);   
  }

}

这个按键里的程序是我之前练习一个单片机省赛题目写的,以后有空聊

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
STM32系列微控制器中的定时器可以用于模拟PWM(脉冲宽度调制)输出,这是一种常见的控制信号生成技术,特别适合于驱动电机、LED灯等设备。下面是一个基本步骤: 1. **配置定时器**: 首先选择一个合适的定时器,如TIM1或TIM2,它们通常都有PWM功能。设置定时器的工作模式,比如通用定时器模式(PWM mode),并配置预分频器以设定占空比范围。 2. **初始化通道**: 选择需要输出PWM的定时器通道,比如TIMx_CH1。配置该通道为PWM模式,设置自动装载值(例如,周期时间)和 Compare Match Value (CMR) 以确定高电平持续时间。 3. **设置占空比**: 通过调整比较寄存器的值来改变输出波形的占空比。一般来说,较低的数值对应较短的高电平,较高的数值对应较长的高电平。 4. **启用输出**: 开启定时器的计数器,并使能输出PWM信号的输出锁存器,使其实际驱动外部负载。 5. **软件更新 PWM**: 如果需要动态改变占空比,可以在运行时修改比较寄存器的值,然后重新启动定时器。 **示例代码片段**(仅供参考,具体取决于所使用的STM32 HAL库版本): ```c TIM_HandleTypeDef htim; // 初始化TIM1 for PWM htim.Instance = TIM1; htim.Init.Prescaler = ...; // 根据系统时钟和所需周期计算预分频值 htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = ...; // 设置周期 htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim.Init.OversamplingMode = TIM_OVERSAMPLING_DISABLE; HAL_TIM_PWM_Init(&htim); HAL_TIM_PWM_MspInit(&htim); // 初始化GPIO // 设置PWM通道 TIM_OC_InitTypeDef oc InitOC; InitOC.OCMode = TIM_OCMODE_PWM1; InitOC.Pulse = ...; // 初始占空比 InitOC.OCPolarity = TIM_OCPOLARITY_HIGH; InitOC.OCNPolarity = TIM_OCNPOLARITY active low; InitOC.OCTRigger = TIM_TRIGER_RISING; InitOC.OCPulseNumber = ...; // 对应周期内的高电平次数 HAL_TIM_PWM_ConfigChannel(&htim, &InitOC); // 开始PWM输出 HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随风飘零翼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值