CASE(例程自敲-针对lqb):
前两个电位器是调电压的,后两个是调频率的
每做一次赛题就要重新开始
希望每次重新做一道主观题时,都能重新构建工程->重新编写每个驱动程序->写逻辑层上的代码希望每写一遍,加深一遍记忆。工程建立越熟练越好,基础驱动编写越快越好。
赛前准备:
十四届解读:
- 客观15%,设计85%
- 有机器评审,需要严格按照试题初始要求去做,例如这个如果一开始显示的是PA7,那之后按键的切换顺序就会变的;就会导致没了一定分数的,这是比较可惜的,所以我们一定要严格按照试题要求
- 例如这个范围,要做就做完美:
- 注意这个时候要注意是优先点亮还是优先亮灭了,这个不是说矛盾的意思:
Provincial competition(省赛)
CUBEMX(所有文件的建立都应该把RCC的配置给配好)
系统主时钟数据手册
组成单元:(1)有内部8MHZ的高速振荡器和外部高速振荡器
(2)有三个时钟源:HIS、HSE和锁相环PLL;
(3)有一个倍频器PLLMUL和一个AHB分频器。
(主时钟详解:主时钟_百度百科)。
clock configuration:外部时钟振荡选择24MHZ.因为数据手册上面有标出:
要先在RCC中打开Crystal/Ceramic resonator振荡(如果没开的话,在框图上面点引脚时可能没有绿色的颜色出现在引脚框图上,并且引脚要先选好模式 才会变绿色,如果是黄色也就只是使能而已),才能进行设置,(记得没事别手残升级,不然可能bug多)的是图(设置PLL所产生的时钟只有在不高于80MHz时,芯片才能正常运行):
注意选择MDK-ARM
图三四为HAL库沙箱段
串口驱动安装:
一直检测不到串口:是不是因为没有mdk,不是;而且一定要选mdk534,如果是mdk411那就是keil4的版本 了
TIP:cannot open source input file,需要在魔术棒里把相关文件夹路径全都加进去。
1、cube中inputfrequency调不了,是因为RCC里面还没有选择振荡模式,所以选择了就能调时钟树了。当不同的HAL库移植时要注意配置不同的定时器通道。
2、stm32l4xx_it.c/stm32l4xx_it.h 文件 。stm32l4xx_it.h 中主要是一些中断服务函数的申明。 stm32l4xx_it.h 中是这些中断服务函数定义
文件放置
1、生成的模块底层驱动都在Application/User,而比如lcd.c/h/font.c是原本有例程的,在那里面就有,所以如果是以那个lcd例程开始,那么就不需要再添加。
2、完整的工程应该有这些东西,如果没有那个以s为后缀的启动文件,那么就容易有个error:STM32.sct(7): error: L6236E: No section matches selector - no section to be FIRST/LAST.,解决方法就是路径检查里面不要有中文,不然就容易出问题、最初如果成功建立工程的话是有三个选项的如图一,有选择直接打开工程还是直接打开文件夹还是关闭;
如果一直没有这个启动文件肯定是路径有什么中文字符,那么就可以在桌面外再建一个英文文件来试试,最后再可以把这个文件移进去。
配置名词选择:
GPIO中Maximum output speed选项:有Low(2MHz),Medium(10MHz)和High(50MHz)[针对F103系列。而就像ws2812对时序要求比较高,则需要选择High,而定时器中的IO口配置只有Alternate function push-pull与Alternate function open-drain,复用推挽输出、复用开漏输出的模式选择,因为不是专门Io口用来输出高低电平的。
gpio输入与输出模式
(general purpose input&output)
输出模式(output):
1.推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。
推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。缺点是不能实现线与。
2.开漏输出:指 漏极开路 的输出形式。 就像晶体管 集电极开路 的输出形式一样,集电极上没有接集电极电阻Rc,直接引到芯片外面成为输出端,需要在芯片外面接上一个 负载电阻 ( 上拉电阻 )才能形成完整的放大(逻辑)电路。
开漏输出最主要的特性就是高电平没有驱动能力,需要借助外部上拉电阻才能真正输出高电平
好处在于可以实现"线与"功能,所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。
输入模式(input):
1、上拉输入有上拉电阻
按键未按下时端口接高电平,即高电平 1 状态---3.3v
按键按下时端口接低电平,即低电平 0 状态-------0v
2、下拉输入有下拉电阻
按键未按下时端口接低电平,即低电平 0 状态---0v
按键按下时端口接高电平,即高电平 1 状态------3.3v
用CUBEMX配置要严格按照它的注释要求来填自己的代码
工程建立:
魔法棒-debug-use:CMSIS-DAP Debugger-下一个页面点击flash setting-Reset and Run
tip:mdk的安装配置pack installer:KEIL MDK 5添加Device/CPU_please update your device selection-CSDN博客
如果搜索不到串口,那就点一下切换:再让电脑去看一下有没有检测到新的串口。并且注意setting里面要选择PORT的SW,如果没有搜索到正确的串口,那么算法里面就没有G4的选择
自己的代码程序要写在begin和end之间,否则可能重新配置了之后就会没有。
TIP:
1.
- PLL(Phase Locked Loop): 为锁相回路或锁相环,用来统一整合时钟信号,使高频器件正常工作,如内存的存取资料等。PLL用于振荡器中的反馈技术。 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步。一般的晶振由于工艺与成本原因,做不到很高的频率,而在需要高频应用时,由相应的器件VCO,实现转成高频,但并不稳定,故利用锁相环路就可以实现稳定且高频的时钟信号。
可使用不同的时钟源来驱动系统时钟(systemclock)
- HSI/HSE(内部/外部振荡时钟源):
HSI主要由内部16M的振荡器产生,可以直接做系统时钟,也可以做PLL输入,启动速度比HSE快,但精度没有外部晶振或者陶瓷振荡器的高。而HSE主要有两个时钟源:外部时钟源/晶振,陶瓷振荡器。
- 主PLL
2.
ceramic resonator陶瓷谐振器; 陶瓷晶振;
css enable的作用:已提供了一个时钟失常恢复机制(CSS),当系统选择HSE作系工作时钟,并打开了CSS功能后,当HSE由于外部原因而停震时,系统将自动切换到内部HSI运行,并产生NMI中断
3.
toggle.切换
LED
brief总结:
详解:由PD2为控制端,锁存LED。即PD2=1(LE)时,PC8-PC15需要在上面Output才能控制LED。所以之后就可以全部把PD2和PC8-15Output,如下✔:
配置结果如下(先切换GPIO,然后再设置,根据原理图GPIOD2就是控制端的,所以设置它,达到亮灭的效果):
视频页面Finished:
detailed过程:
1.因为只有在左右两边有电平差时才能点亮灯,而一开始初始化为高电平,那么左右两边就不会亮。
2.如果在.h文件中包含了某头文件,在那个地方要引用到uchar,那么不要在.c中去定义那个uchar
3.#define后面记得不要加分号,不然就会相当于没有这个定义,然后也会出现让加()的错误,因为分号前面就是应该加()的。
4.要在专门的函数里面初始化,不然可能在那之前的时钟定义之类的还没有初始化,可能就会出现以下错误:
有效点灯:
KEY
原理:低电平导通,故在配置时将GPIO设置为输出模式即input,由外部来决定是高低,在没按下时为逻辑1高电平,按下即低电平。接GND检测低电平时相当于两组按键(1.3是一组,2.4是一组)用IO口检测电平的变化,如果没按下就还是高电平,所以初始化电平时应该选择高电平。
之后因为需要定时器定时每10ms,去扫描是否有按键按下,所以要开启定时器3,而下面程序的第一句就是if(htm->...)这个判断,那么就可以知道是否到了计时的时间。
这时候就可以到应用层了,需要将按键设置为上拉模式:
V1:功能是每次点亮两个灯,可以结合LED的原理图模块来观察
V2(长短按):
main.c:if(key_time>=70)//700ms长按键
TIP:1、uint8_t的含义:通过typedef给类型,uint8_t是用1个字节表示的;uint16_t是用2个字节表示的;uint32_t是用4个字节表示的。 用这个要记得return 0;
2、读取电平变化的函数要在stm32g4xx_hal_gpio.c里面找,最好别自己打,不然哪一个字母打错了都不知道。就像GPIO_PIN容易打成GPIO_Pin,这样就会报错。
3.../Core/Src/main.c(116): warning: #223-D: function "KEY_Scan" declared implicitly.试一下看看是不是未声明头函数。
4.tip:如果已在模块(如key.c)等外部文件中定义了那个变量,但是在main.c文件中有“../Core/Src/main.c(114): error: #20: identifier "KEY" is undefined”错误,就说明没有去声明这个已经有在外部文件中定义的变量,所以需要用extern unit8_t KEY;//声明
5. key[i]. key_time=0;//引用的是这个结构体的变量名,所以前面要加数组名
V2(结构体变量):引用的函数是在Driver.h文件中找void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
struct keys key[4]={0,0,0};//这个也就是把keys叫做key的定义而已,把这个给记住,结构体的固定用法,可以套用其他以后
struct keys//按键结构体
{
uchar jugde;
bool single;
bool sta;
};
//上面的是在在.h文件中 //下面的是在.c文件中: struct keys key[4]={0,0,0};//这个也就是把keys叫做key的定义而已
上图的第二第三张的key[i].judge_sta这个变量就是判断按键的状态,有对应case,比如:case0:判断有没有按下;case1判断按下的是哪个按键,再让对应的按键等于1,这样在主函数里也能用If判断去实现对应的功能;case2就是是否按完了后松开了;
引用了之后要打开定时器3,e.g:
HAL_TIM_Base_Start_IT(&htim3);
TIP:
1、找不到HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);这个函数也就是没有stmg4xx_hal_tim.h这个文件,那么去别的模板工程中复制这个文件的.c文件在文件夹中替换掉,那么就可以重新打开工程编译之后会有的。(Elapsed意思为消逝)
2.bool函数返回的只有true和false,而int会返回各种数字。如果你关心的不是数字的多少,而是这个数字为不为0.所以这种情况用bool会更加简洁,规范。
3.定义的top level declarator是少了分号
V3(定时器中断回调函数):
if(GPIO_Pin==GPIO_PIN_3)
{ if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==0){}}
4.GPIO_PIN_RESET除端口Pin,低电平;GPIO_PIN_SET高电平;
例:向PB8输入高电频
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)
电频反转函数
例:PA3引脚输出电频反转
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3
换屏(两个页面切换):
TIP:如果一直自己自动切换,可能就是按键也就是触发源的代码搞错了,比如就是明明就是要判断等于等于1时才让参数取反,所以是错误示范。
如果按键程序没写错,要看一下是不是定时器没使能,就无法简断去检测是否有按键按下导致IO口电平是否变化
定时器中断
(一定要记得中断的✓要选上使能):
如果仅仅是定时功能则可以使用TIM6和TIM7这两个基本定时器
定时器频率和定时工作频率:
- 定时器工作频率:外部总频率/(psc+1)。要设置成1MHZ可以在prescaler中填写80,因为在时钟配置的图中写了80MHZ,所以除起来也就是(80000000/80=1000000HZ=1MHZ),但注意是从0开始算的,所以后面要-1
- 定时频率=定时器工作频率/counter=外部总频率/counter*(psc+1),例如定时100ms=100hz,就可以在counter中填入10000,后面-1,一除下来就可以了。(1)以TIM3为例,每10ms触发一次中断,如下图填写:
输入捕获(频率):
由信号发生芯片跳线帽连起MCU的PA15和PB4进行频率的输入捕获
步骤:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)这个是在回调函数下面几个函数里有的,
(1)用if判断是否开启了定时器---->把计数器的值赋到变量中,通过一个内置函数
__HAL_TIM_SetCounter是在stm32_hal_legacy.h 文件中,该文件包含了 HAL 库提供的一些兼容传统库的宏定义。
main.c:
TIP:
1.选中的是阻塞模式,也就是要死等着程序像delay一样,而下面的就是非阻塞,则是可以符合条件就中断。两个很像,不要选错了。
2、获取输入捕获值:
背景知识点补充:
输入捕获就是当连接到定时器的引脚上产生电平变化时对应的捕获装置会立即将当前计数值复制到另一个寄存器中。你可以开启捕获中断然后在中断处理函数中读出保存的计数值。
与输入捕获不同的是PWM输入模式会将同一个输入信号(TI1或TI2)连接到两个捕获装置(IC1和IC2)。
为什么第二个页面的参数一点都没捕获到信息,是因为没开内部时钟吗?
- 对照原视频做了以下的改动:TIM1、TIM4用了内部时钟源的选项(图二),并且在生成的新代码里面有初始化的,但没有开始计时的函数,所以要自己加;TIM16加了PWM channel 1和PLUSE添加了20
- 是个通道,就要开启
3、float不能在前面加unsigned,因为float就已经是正整数了,不用这个去限制范围。
4、换回之前自编写的interrupt.c中检查为什么显示出来的数据后面不正常:
- 参数填错了,左边是自己找的,右边的才是正确的,是否激活通道,至于怎么找,如果是在那个HAL函数中的,就追溯回去找,可以找得到的。
- 如果显示是0.00除了可能是因为没有波输出之外,还可能是格式没设置好,比如duty这个是有高低位,给float有小数点就在范围内,而uint来定义的话就只能显示0.00。
5、_CH1N和_CH1的区别和控制:
e.g:TIM1是一个完整的电机控制用定时器外设,TIM1_CH1和TIM1_CH1N,用于驱动上下两个功率管。如果Deadtime为0,则 TIM1_CH1N是TIM1_CH1的反相,如果Deadtime不为0,则在TIM1_CH1N上插入了Deadtime,防止上下功率管同时ji导通。 TIM1_CH1N是TIM1_CH1的互补输出 ,用于TIM1的同步PWM模式。
I2C:
过程:因为一个EEPROM只能存一个八位的数字,而frq又是16位的,所以需要把他们拆成一个高八位和低八位的来再分别用eeprom_write(address,thing)存入到EEPROM的两个区域里面。后面:uint eep_temp=(eeprom_read(1)
配置:PB6和PB7的CUBEMX配置:
MCP1407是 单路缓冲器、MOSFET驱动器,匹配的上升下降时间和传输时延。
TIP:
- uchar frq_l=frq1&0xff;
- 别写成:uchar frq_l=frq1&&0xff;两个与符号了。区别:&是按位与,会逐个去相与,而&&是逻辑与,如果第一个就是false不成立的话比