yyj的STM32学习笔记
-
task1
任务描述
使用定时器让LED以1s为周期闪烁
解析
上述要求涉及gpio、定时器以及中断
- 配置led的过程为开启外设时钟,设置gpio为推挽输出模式
- 配置定时器时,本次使用了TIM6基本定时器,需要开启对应外设时钟,设置定时器ARR无缓冲,使能更新中断,设置预分频器(PSC)和自动重装载器(ARR)
- 配置中断需要先设置中断优先级分组,然后使用NVIC_Init函数初始化TIM6对应中断,然后配置中断服务函数,中断服务函数在开启文件中有声明和分配位置,注意在中断服务函数开头将更新中断标志位置零
- gpio的输出控制通过对端口输出寄存器(ODR)进行写实现
- 定时器的时间计数使用一全局变量实现
总结
- 读数据手册很重要
- 细心,可以避免许多不必要的bug,节约debug的时间
-
可以把代码也贴一下
-
1、是同相放大器、反相放大器。“xiang”指的是相位,不是方向。
2、为什么要设置为推挽输出?
3、如何计算定时器的周期?
4、在这个过程中遇到了什么问题?又是如何解决的?
-
- 错别字,以后会在发布前看一遍,并且打算专门开一个运放专题新帖
- 推挽输出和开漏输出相比,能真正地输出高电平,而开漏输出需要借助上拉电阻
- 在配置时钟树时,定时器的周期为72MHz,在本例中,将预分频器(PSC)设置为71,对定时器进行72分频,即1MHz,将自动重装载器(ARR)设置为999,即每1000个周期产生一次中断,则最终产生中断的频率为1KHz,周期为1ms
- 遇到的问题大多数是工具的应用方面的,对于一个stm32项目的创建、初始化、调试、下载,刚开始时会因为遗漏某些细节而导致错误,而这种错误往往是不容易排查的
比如,刚开始时由于没有配置时钟树导致程序不能正常运行,编译程序时由于缺少某些文件(.s之类的)而导致编译出错等等
除此之外,还有一些不够细心导致的错误
比如,开始焊放大器时,由于线路规划时粗心,画错了一个电阻的位置,导致出错
-
task2
任务描述:
- 编写按键程序,按下时LED亮,抬起时LED灭
- 使用PWM波驱动舵机
解析
- 按键程序最初想法是在主函数的while(1)循环中扫描GPIO的IDR寄存器,不过后来的想法是在定时器中断里每隔一段时间(如10ms)扫描一次IDR寄存器
- PWM波形使用通用定时器进行输出,每个通用定时器有四个通道,通过将CCRx的值与CNT的值做比较,输出高低电平,通过控制频率和占空比即可控制舵机
- 本次使用的舵机型号为SG90相关参数如下:
- 本次使用STM32CubeMX进行配置和初始化,配置如下:
-
task3
任务描述:
完成单个按键状态检测,能够利用指示灯识别单击、双击、长按
解析
- 按键按下状态的检测不能直接使用中断,由于机械按键会在按下、抬起时产生抖动,会触发多次中断,故需要进行消抖处理,抖动一般在10ms以内,本次使用延时消抖,即在检测到按下或抬起后在一段时间后再次检测,确认其确实发生了按下或者抬起
- 按键检测以时间片的原则进行,使用TIM6计时器每隔10ms检测一次按键状态,使用两个变量分别储存按下、抬起的时间
- 使用一个变量记录所处的状态,当检测到按键状态发生变化时,根据现在所处的状态和变量的值进行动作
- 本次设定为2s以下为单击,2s以上为长按,第一次单击后1s内再次单击为双击
代码
由于代码由cube生成,比较繁琐,故只贴自己写的部分
宏
#define MAX 999 #define TIME_CLICK_PRESS 200 //点击和长按分界线 #define TIME_WAIT 100 #define FLAG_PLAIN 0 //普通状态 #define FLAG_FIRST_HIT 1 //第一次被按下 #define FLAG_WAIT_SECOND_HIT 2//等待第二次被按下 #define FLAG_SECOND_HIT 3 //第二次被按下 #define KEY_TIME_INCREASE(x) (((x) >= MAX) ? MAX : ((x) + 1)) //代替自增操作,防止溢出,对变量的值做限制
变量
uint32_t POSITIVE = 0, NEGATIVE = 2; //按下、抬起时间 uint16_t FLAG = FLAG_PLAIN; //状态
主要函数
uint16_t if_KeyHit(void) //检测按键状态 { if(GPIOA->IDR & 0x1) { return 0; } else { return 1; } } void TimeSlicer(void) //管理时间变量和状态转换 { if (if_KeyHit()) //检测到按键按下 { if (POSITIVE > 1) //正常继续按下状态 { POSITIVE = KEY_TIME_INCREASE(POSITIVE); StateSwitch(); NEGATIVE = 0; } else if (POSITIVE == 1) //确认切换为按下 { POSITIVE = 2; StateSwitch(); NEGATIVE = 0; } else //处于抬起状态,需确认是否按下 { NEGATIVE = KEY_TIME_INCREASE(NEGATIVE); POSITIVE = 1; } } else //检测到按键抬起 { if (NEGATIVE > 1) //正常继续抬起状态 { NEGATIVE = KEY_TIME_INCREASE(NEGATIVE); StateSwitch(); POSITIVE = 0; } else if (NEGATIVE == 1) //确认抬起状态 { NEGATIVE = 2; StateSwitch(); POSITIVE = 0; } else //处于按下状态,需确认是否抬起 { POSITIVE = KEY_TIME_INCREASE(POSITIVE); NEGATIVE = 1; } } } void StateSwitch(void) //状态操作和转换 { switch (FLAG) { case FLAG_PLAIN: if(if_KeyHit()) //按键按下 { FLAG = FLAG_FIRST_HIT; } break; case FLAG_FIRST_HIT: if(!if_KeyHit()) //按键松开 { if (POSITIVE > TIME_CLICK_PRESS) //长按 { FLAG = FLAG_PLAIN; } else //第一次为点击 { FLAG = FLAG_WAIT_SECOND_HIT; State_0(); } } else { if(POSITIVE > TIME_CLICK_PRESS) { State_3(); } } break; case FLAG_WAIT_SECOND_HIT: if(!if_KeyHit()) //按键没有按下 { if (NEGATIVE > TIME_WAIT) //等待时间已过,为单击 { State_1(); FLAG = FLAG_PLAIN; } } else //按键按下 { FLAG = FLAG_SECOND_HIT; } break; case FLAG_SECOND_HIT: if(!if_KeyHit()) //按键抬起 { if(POSITIVE < TIME_CLICK_PRESS) //第二次为点击 { State_2(); FLAG = FLAG_PLAIN; } else //第二次为长按 { State_1(); State_3(); FLAG = FLAG_PLAIN; } } break; default:; } } void State_0(void) //用LED灯指示检测到的按键操作类型 { GPIOF->ODR |= (0x1 << 8); GPIOF->ODR |= (0x1 << 9); } void State_1(void) { GPIOF->ODR &= ~(0x1 << 8); GPIOF->ODR |= 0x1 << 9; } void State_2(void) { GPIOF->ODR |= 0x1 << 8; GPIOF->ODR &= ~(0x1 << 9); } void State_3(void) { GPIOF->ODR &= ~(0x1 << 8); GPIOF->ODR &= ~(0x1 << 9); }
总结
这次开始用的板子有点问题,导致找了两天bug也没找出来,最后换了块板子,效果还不错,硬件的问题太折磨人了555