电子信息工程专业打工人的蓝桥杯嵌入式竞赛时记

前言

蓝桥杯嵌入式的学习总结,本文主要针对蓝桥杯嵌入式作出总结(STM32F103RBT6型号标准库版本+STM32G431RBT6型号HAL库版本)进行讲解,由于第一年已用标准库参赛,第二年HAL库版仅为学习链接,可在文章旧版讲解的基础上对新版进行学习。如果读者时间充足,建议先学好模电数电基础知识,同时用开发板实际操作,能让学习事半功倍且能学习更深入。

一、基础入门

1.GPIO

首先GPIO最基本、最简单的作用是我们可以通过编程的方式让它作输入或者输出,而输入/输出的形式为高低电平(通常0V为低电平,3.3V为高电平)。 要让GPIO作输入或者输出,首先就需要对IO口相关的寄存器进行配置。而寄存器是中央处理器内的组成部分,寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。因此对IO口的初始化就是向相关寄存器里面写不同的值,从而确定使用哪一个IO口(IO口标号)、以及IO口工作模式(输入还是输出)、输出速度等参数。在经过初始化之后就可以正常使用IO口了,比如如果IO口设置成了某个输入模式,就可以通过调用相关函数或者直接操作相关寄存器去得到IO口的电平是高电平还是低电平。
—>单片机引脚与端口,引脚和GPIO的区别

GPIO的引脚速度

输出驱动电路响应速度,芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路,通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制降低功耗的目的。可理解为: 输出驱动电路的带宽,即一个驱动电路可以不失真地通过信号的最大频率。如果一个信号的频率超过了驱动电路的响应速度,就有可能信号失真。GPIO的引脚速度跟应用相匹配,速度配置越高,噪声越大,功耗越大。
GPIO的引脚速度跟应用匹配(推荐10倍以上)。
① USART串口,若最大波特率只需115.2k,那用2M的速度就够了,既省电也噪声小。
② I2C接口,若使用400k波特率,若想把余量留大些,可以选用10M的GPIO引脚速度。
③ SPI接口,若使用18M或9M波特率,需要选用50M的GPIO的引脚速度。

GPIO的翻转速度

GPIO的翻转速度指输入/输出寄存器的0,1值反映到外部引脚(APB2上)高低电平的速度。手册上指出GPIO最大翻转速度可达18MHz。
通过简单的程序测试,用示波器观察到的翻转时间: 是综合的时间,包括取指令的时间、指令执行的时间、指令执行后信号传递到寄存器的时间(这其中可能经过很多环节,比如AHB、APB、总线仲裁等),最后才是信号从寄存器传输到引脚所经历的时间。

引脚功能:端口复用和端口重映射

复用就是当这个GPIO作为片内外设功能引脚;一个引脚可用作实现多个功能,原理图上的引脚注释为可复用功能(或芯片数据手册可查),1.做普通IO输入输出,2.其他外设的输入输出;好处是节省外设与GPIO所用引脚数量;STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。
重映射就是将所需的片内外设复用功能从原本默认IO口重新定义到其他可实现该功能的引脚,芯片内部已经固定了只能映射到固定的地方,分部分重映射(端口有一个默认复用功能和多个可重映射功能)和完全重映射(端口有一个或多个默认复用功能和一个可重映射功能),stm32中文手册可查(通过配置该复用功能重映射哪个端口);好处是防止该复用功能默认引脚被占用,多个功能所需引脚冲突;
复用功能I/O(AFIO):在使用引脚的单个复用功能时无需打开AFIO时钟,只需使能该复用功能的时钟和需要的输入输出模式;复用功能重映射时需使能AFIO时钟。
—>端口复用和端口重映射
—>片上外设和片外外设的区别
—>什么时候需要复用IO(AFIO)
引脚功能

输入输出模式

驱动能力:就是指输出电流的能力。对于驱动大负载(即负载内阻越小,负载越大)时,例如IO输出为5V,驱动的负载内阻为10ohm,于是根据欧姆定律可以正常情况下负载上的电流为0.5A(推算出功率为2.5W)。显然一般的IO不可能有这么大的驱动能力,也就是没有办法输出这么大的电流。于是造成的结果就是输出电压会被拉下来,达不到标称的5V。当然如果只是数字信号的传递,下一级的输入阻抗理论上最好是高阻,也就是只需要传电压,基本没有电流,也就没有功率,于是就不需要很大的驱动能力。
FT:容忍5V输入
浮空输入(GPIO_Mode_IN_FLOATING):电平会完全取决于外部电路而与内部电路无关,无外部电路接入时,IO脚浮空会使得电平不确定,由输入数据寄存器(只读)读取。
上拉输入 (GPIO_Mode_IPU):无外部电路接入时,IO脚上拉使电平始终为高电平,读取外部电路低电平明显,无法判断高电平的输入,由输入数据寄存器(只读)读取。
下拉输入 (GPIO_Mode_IPD):无外部电路接入时,IO脚上拉使电平始终为低电平,读取外部电路高电平明显,无法判断低电平的输入,由输入数据寄存器(只读)读取。
模拟输入 (GPIO_Mode_AIN):不经过施密特触发器,将外部信号直接传输到数模转换通道上,片上外设(ADC)直接读取IO脚输入。
推挽输出(GPIO_Mode_Out_PP):上PMOS下NMOS,降低功耗带载能力强,跳变速度快,但无法完成“线与”功能,由输出数据寄存器(可读可写)读取位寄存器后输出给IO脚。
复用推挽输出(GPIO_Mode_AF_PP):片上外设功能的推挽输出。
开漏输出(GPIO_Mode_Out_OD):可以根据外部电路需要多少V的高电平来接入外部上拉电压,实现线与的功能,IIC一个低电平,可以拉低整个总线。
复用开漏输出(GPIO_Mode_AF_OD):片上外设功能的开漏输出
GPIOx_IDR是端口的输入数据寄存器,GPIOx_ODR是端口的输出寄存器
输入输出图
—>深刻理解stm32输入输出模式
—>施密特触发器详解

引脚设置

寄存器赋值输入可以使用“|”操作,但读取和定时器通道只能单个位进行操作。
—>位带操作
—>寄存器编程
IO设置

2.新建工程(F103+G431)

赛点资源数据包 -> 8-液晶驱动参考例程 -> (复制)CT117E-LCD文件夹 -> (粘贴)考生文件夹
好处:大大节省建立工程的时间,不用专门考虑工程格式,不用担心建立工程时的小细节浪费比赛时间,方便练习。
工程文件改名:粘贴后清空子文件夹Output内全部内容 -> 子文件夹Project内除后缀《.uvproj工程和.uvopt文件》其他文件全部删除 -> 剩下两个文件分别命名后进入工程为.axf 文件改名。(.uvproj工程文件为工程开辟空间,是工程核心;.uvopt文件为工程保存工程配置,关联各文件内容,缺少.uvopt文件MDK缺失结构体变量提示功能,非工程链接;uvopt和uvproj都是keil工程文件,共同说明这你工程里有哪些文件,文件有没有被编译过,工程目录树是怎么组织的等等信息)
.axf 文件改名:工程中Options for Target(魔术棒)-> Output -> Name of Executable -> 指定文件命名后编译 -> 考生文件夹Output子文件夹 -> 找到.axf后打包提交
注:该方法只适用于蓝桥杯嵌入式旧版比赛

3.必备宏定义(F103)

工程中Configure target options -> C/C++ -> Define选项STM32F10x_MD宏定义作用为使system_stm32f10x.c文件执行设置默认系统时钟72MHz。
startup_stm32f10x_cl.s(启动文件) → SystemInit() → SetSysClock () → SetSysClockTo72(),其中所有函数在system_stm32f10x.c文件。
启动复位
默认系统时钟
工程中Configure target options -> C/C++ -> Define选项USE_STDPERIPH_DRIVER宏定义作用为使stm32f10x.h文件执行包含所有外设头文件。
stm32f10x.h(通用头文件) → #include “stm32f10x_conf.h” → 包含所有外设头文件。
头文件包含
—>#预编译详解

4.注意库函数(F103)

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);	/*外设时钟使能*/
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);	/*复位所有IO口*/
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);			/*对GPIOx_ODR写入,系列IO设置*/
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);		/*对GPIOx_BSRR写入,单位置1*/
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);	/*对GPIOx_BRR写入,单位置0*/
void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);	/*输出比较模式更改比较值后在下次更新事件生效或立即生效*/
void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);	/*设置输入捕获极性,上升沿或下降沿*/
/*TIM_OCxPolarityConfig()函数用作输入捕获极性配置时(keil不可跳转到该宏定义),uint16_t TIM_OCPolarity参数可取以下值*/
#define  TIM_ICPolarity_Rising             ((uint16_t)0x0000)
#define  TIM_ICPolarity_Falling            ((uint16_t)0x0002)
/*TIM_OCxPolarityConfig()函数用作输出极性配置时(默认),uint16_t TIM_OCPolarity参数可取以下值*/
#define TIM_OCPolarity_High                ((uint16_t)0x0000)
#define TIM_OCPolarity_Low                 ((uint16_t)0x0002)

5.时钟系统及滴答定时器

/**
  *1Mhz的频率,每秒钟可以执行1.25M指令。嵌入式stm32默认上电系统时钟8M,每个指令的执行周期为1s/8*1.25M=0.1us
  */

默认系统时钟

—>stm32的指令周期
—>时钟系统
uint32_t SysTick_Config(uint32_t ticks)函数在core_cm3.h头文件定义,void SysTick_Handler(void)中断函数在stm32f10x it.c源文件定义。
—>SysTick(滴答定时器)

6.中断

—>stm32中断优先级的使用及注意事项

7.其他编程注意事项

1、判断语句条件中==优先级大于&、|、^、&&、||;加减乘除优先级大于移位运算
2、整型常量(数值)默认设置为int类型,char和short类型与整型常量运算过程中以int类型为范围;常数后加“.”或“.0”表示该常数为double型。
unsigned int类型:0 ~ (2^32 - 1 —> 4294967295);
—>数值范围注意事项
float类型:小数点后有效数字6~7位;
double类型:小数点后有效数字15~16位;
3、浮点数注意事项:
常数后缀:
U:unsigned
L:long / double
F:float
float数据类型赋值运算时常数后需加F
浮点数注意事项
4、—>计算机负数计算
5、—>keil中code和const的区别
6、—>主机字节序
7、—>STM32堆栈区
8、—>字节和字长区别
9、程序中数组绝对不能溢出
10、函数中的局部变量存储在栈中,栈空间大小1~2KB,一个函数(包括main函数)一次性不能设置太大的数组,大数组设置为全局变量(静态存储区)
10、—>寄存器,锁存器,触发器,存储器的区别
11、—>浅论各种调试接口
12、—>C语言中的volatile关键字

二、开发学习

标准库版本

1.LED

上电默认输出低电平;与锁存器相连,使能端(PD2)高电平有效;引脚为PC8~15,APB2总线控制时钟,推挽输出控制亮灭。
锁存器的作用(蓝桥杯嵌入式中至与LED功能相连)
锁存器(Latch)是一种对 脉冲电平敏感的 存储单元 电路,它们可以在特定输入脉冲电平作用下改变状态。锁存,就是把信号暂存以维持某种电平状态。锁存器的最主要作用是缓存,其次完成高速的控制器与慢速的外设的不同步问题,再其次是解决驱动的问题,最后是解决一个 I/O 口既能输出也能输入的问题。锁存器是利用电平控制数据的输入,它包括不带使能控制的锁存器和带使能控制的锁存器。
控制LED亮灭:先配置相应I/O口,再使能锁存器(写入状态)后关闭锁存器(缓存本次状态)。

/**
  *初始化LED,LED全灭
  *LED1~8 ==> GPIOC8~15,低电平亮高电平灭
  *74HC573锁存器使能端 ==> GPIOD2,高电平使能
  */
LED与LCD冲突解决方法

1、编写LED程序(无法保留其他LED状态)
LED函数
点亮函数点亮前关闭所有。(LED初始化不能先于LCD初始化)
2、更改LCD程序(最优方案)
LED和LCD
LED和LCD
三个函数添加这三句代码即可。
—>LED的配置详解

2.蜂鸣器

蜂鸣器与JTAG接口的复位脚共用PB4,但默认为JTAG接口的复位脚(内部上拉,上电输出高电平);APB2总线控制时钟,推挽输出控制开关;非比赛考点

/**
  *初始化蜂鸣器,关闭蜂鸣器,默认上电为JTRST功能,蜂鸣器关闭
  *Buzzer ==> GPIOB4,低电平打开高电平关闭
  *使能端口复用功能,禁止JTRST功能
  */

3.独立按键

APB2总线控制时钟,上拉输入读取。
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin):读取端口输入电平状态,高电平返回1低电平返回0。
三行代码扫描函数状态机扫描函数必须和定时器配合,定时器每10ms执行按键扫描。标志位可放于SysTick_Handler()定时,按键处理必须紧跟按键扫描,即在同一时间段执行一次。
按键操作:按下、松开、复合、长按、双击。程序中同时有长按与短按判断时,短按使用上升沿触发。

/**
  *初始化KEY,KEY设置上拉输入,输入速率无需配置,默认复位状态,速率10MHz
  *KEY1:GPIOA0; KEY2:GPIOA8; KEY3:GPIOB1; KEY4:GPIOB2
  *扫描KEY,按下端口读取低电平,松开端口读取高电平
  */
/**
  *扫描KEY,按下端口读取低电平,松开端口读取高电平
  *KEY1:0x01; KEY2:0x02; KEY3:0x04; KEY4:0x08
  *g_rising判断单双击,g_key_state判断长短按与单双键
  */

4.LCD

资料包提供例程,变量通过sprintf函数整理后通过显示函数展示在LCD,需包含stdio.h头文件。2.4寸显示屏单行显示最大值为20英文字符,sprintf函数调用数组大小不能超过20,格式化字符串不大于20个英文字符(包括空格和字符串结尾的结束符
LCD显示中sprintf((char*)string, “%s”);"%s"格式符的最大长度为19,其他格式符时最大长度为20。
sprintf函数使用:"%02d:%02d:%02d"可用于在数值前显示0,2表示%d占2位,02表示%d占两位,且数值不足两位时前面添0补足(数值长度大于数字则该用法无效)。
在官方给的参考资料里面介绍了分辨率是240x320,lcd一共分为10行,20列,所以一个字符的高实际上是24*16,所以我们可以看到lcd.h宏定义里面每一行之间的差值就是24,那么每一列之间的差值就是16,实际上表示一列的时候是反着来的,320表示第一列,320-16表示第二列,那么320-(16 * i)表示第i列。
—>点阵图、矢量图、像素图、位图图像、位元块传输
—>sprintf()函数的用法

LCD字符高亮颜色显示

void LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii);
数字显示:LCD_DisplayChar(Linex, 320 - (16 * y), variable / 10 + 48);
LCD_DisplayChar(Linex, 320 - (16 * y + 1), variable % 10 + 48);
Ascii码:数字0-9 —> 码值48-57,大写字母A-Z —> 码值65-90,小写字母a-z —> 码值97-122

void STM3210B_LCD_Init(void);										/*LCD初始化*/
void LCD_SetTextColor(vu16 Color);									/*LCD文本颜色*/
void LCD_SetBackColor(vu16 Color);									/*LCD文本区域背景颜色*/
void LCD_ClearLine(u8 Line);										/*清屏指定行*/
void LCD_Clear(u16 Color);											/*指定颜色清屏*/
void LCD_SetCursor(u8 Xpos, u16 Ypos);								/*锁定显示起点坐标*/
void LCD_DrawChar(u8 Xpos, u16 Ypos, uc16 *c);						/*画字符*/
void LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii);				/*LCD展示字符*/
void LCD_DisplayStringLine(u8 Line, u8 *ptr);						/*指定行展示字符串*/
void LCD_SetDisplayWindow(u8 Xpos, u16 Ypos, u8 Height, u16 Width);	/*设置显示区域大小*/
void LCD_WindowModeDisable(void);									/*退出窗口模式(全屏)*/			
void LCD_DrawLine(u8 Xpos, u16 Ypos, u16 Length, u8 Direction);		/*画线*/
void LCD_DrawRect(u8 Xpos, u16 Ypos, u8 Height, u16 Width);			/*画矩形框*/
void LCD_DrawCircle(u8 Xpos, u16 Ypos, u16 Radius);					/*画圆*/
void LCD_DrawMonoPict(uc32 *Pict);									/*画单色图片*/
void LCD_WriteBMP(u32 BmpAddress);									/*显示BMP位图*/
void LCD_DrawBMP(u32 BmpAddress);									/*画BMP位图*/
void LCD_DrawPicture(const u8* picture);							/*画图片*/

5.TIMER

分频寄存器(PSC)数值为0表示1分频,设置PSC寄存器值:需要的分频数-1;计数器达到自动重装载值后再加1则产生溢出,自动重装载寄存器(ARR)中再次写入自动重装载值,设置自动重装载寄存器(ARR)数值:需要的计数值-1。
通用定时器:定时器2~5,各4通道;
高级定时器:定时器1和8,各4通道;
基本定时器:定时器6和7,直接连接驱动DAC,没有引脚复用;
TIM_RepetitionCounter对高级定时器有用;
定时器在发生中断源事件时进入中断服务函数,更新中断(溢出,TIM_IT_Update)、捕获/比较事件(CNT = CCRx,TIM_IT_CCx等,事件产生使TIMx_EGR寄存器置位;只有更新事件(UEV,发生更新中断)或TIMX_EGR寄存器的UG位置位触发定时器计数初始化,所有寄存器被更新;
预装载寄存器:只有发生更新事件才将数据更新至影子寄存器(真正的装载寄存器)。
设置中断源:void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

/**
  *定时器2初始化
  *预分频系数(计数时钟频率 = f / (PSC + 1)):PSC - 1; 自动重装值(计数溢出重回0后产生中断):ARR - 1;向上计数法,时钟1分频; 定时器中断配置并使能
  *中断优先级分组,中断通道选择并使能,中断等级设置
  *默认复位APB1时钟:36MHz(2分频),通用定时器时钟:72MHz(APB1预分频系数不等于1)
  */
/**
  *定时器2中断
  *定时器溢出:定时器中断标志位置1,需软件手动清0
  */

—>stm32定时器中断类型
—>STM32定时器中的更新操作与更新事件
—>STM32中断服务函数的编写注意事项1
—>STM32中断服务函数的编写注意事项2
—>stm32定时器误区

6.实时时钟

蓝桥杯嵌入式开发板的RTC只能使用LSI即内置RC振荡器。实时时钟可不用开启中断使用。
RTC不在APB1和APB2时钟总线上,需单独配置时钟使能;LSI时钟40KHz,4000分频(系数3999)得计数频率1s。
RTC时间存储以定时器计数总值存储,每计数加一为时间1s,60s = 1min,3600s = 1h,0x0001517F为23:59:59,0x00015180为00:00:00;
void RTC_SetCounter(uint32_t CounterValue):设置初始时间(单位秒)hour * 3600 + minute * 60 + second;
RTC中断服务函数为RTC_IRQHandler(),不是RTCAlarm_IRQHandler();

/**
  *RTC初始化
  *配置RTC中断,开启触发每秒中断
  *备份寄存器(BKP)与电源管理(PWR)时钟使能,使能BKP,复位BKP寄存器,
  *使能LSI时钟,等待LSI使能完毕,配置LSI为RTC时钟源,RTC时钟使能,
  *等待APB1和RTC时钟同步,等待对RTC操作完成,设置RTC频率,等待完成,设置计数值(初始时间),等待完成
  */
/*实时时钟获取,计算多少个3600秒(时)、除去多少个3600秒后余多少个60秒(分)、除去多少个60秒后余多少个秒(秒)*/
/*每秒触发中断,判断是否计数达到23时59分59秒*/

—>stm32之备份寄存器(BKP)应用(侵入检测中断)
—>STM32电源管理(PWR)

7.USART

数据发送使用字符串发送函数,或者重定义fput函数然后使用printf,只要程序中有printf重映射函数必须勾选Use MicroLIB选项,否则程序无法运行。
用串口调试助手给单片机以字符串的形式发送数据时,结尾是不默认添加’\0’。
半主机模式就是通过仿真器实现开发板在电脑上的输入和输出,单片机程序运行应脱离仿真器关闭半主机模式。
数据接收开启串口接收中断
USART_FLAG_TC和USART_FLAG_TXE区别
TC和TXE在复位默认置1,但TXE在对发送寄存器(USART_DR)写操作后被清零,TC要先读状态并对寄存器写操作才进行清零,两者都可软件清零,所以上电后使用TC来判断字符串传输时,如果未清零(软件清零或软件序列清零)会导致首字母被覆盖而不显示,而TXE在首字母转移到发送寄存器后立即清零不会被覆盖。除了复位首字母问题,程序运行过程中会被自动被特性规则清零,不必手动清零。

/*添加该内容,不必勾选Use MicroLIB选项*/
#pragma import(__use_no_semihosting)
struct __FILE
{
	int handle;
};
FILE __stdout; 
_sys_exit(int x)
{
	x = x;
}
/***********************************/
/**
  *初始化USART2,IO口配置(APB2总线时钟),串口配置(APB1总线时钟),中断配置
  *发送端TX2 ==> PA2,复用推挽输出,50MHz;接收端RX2 ==> PA3,浮空输入
  *波特率:f_baudrate, 字节长度:8bit, 停止位:1位, 校验位:无, 模式:接收和发送, 硬件数据流控制:无
  *中断通道:串口2中断, 最高抢占和响应优先级
  */
/**
  *发送字符串
  *发送标志位使能,发送字符数据,返回字符
  *USART_FLAG_TC表示传输完毕,USART_FLAG_TXE表示发送缓冲区空,无特殊情况均使用USART_FLAG_TXE效率快
  *注意:while和if后的括号中无论是it+还是++i,都先执行括号内的自增减,再执行大括号内的语句
  */
/*重定义fputc函数(printf函数调用)*/
/**
  *串口2接收中断服务函数
  *注意:接收字符串结尾必须加换行符(\r\n)
  */

sprintf
—>对原子哥USART实验中printf重定向进行分析
—>USART与UART的区别
—>串口接受不定长数据方法
—>printf重映射解释
—>printf函数重定义
—>printf输出串口助手、防卡死办法
—>上电发送字符串首字母问题

8.24C02

写数据的时候需要注意,E2PROM是先写到缓冲区(发送完Stop),然后再“搬运到”到掉电非易失区(进入写入周期),这个过程让它应答是没有响应(时间需要最大10ms,不用芯片时间不同,开发板上M24C02时间为5ms),直到写入周期完成。
AT24C02是Ateml公司的2KB的电可擦除存储芯片,M24C02是ST公司的2KB的电可擦除存储芯片,区别在于AT24C02支持8字节页写、M24C02支持16字节页写
页写入:一次性连续写入最多8字节(24C02一页为8字节,起始地址为0x00,注意超过页边界会自动从头覆盖)到缓冲区后,再等待一个写入周期,多字节“搬运到”到掉电非易失区;
连续写和连续读时地址自动加1,一页8字节,翻页需要重新指定新的页地址。
SCL -> GPIOB6,SDA -> GPIOB7

/**
  *I2CWaitAck()已修改
  *启动I2C总线;开启写操作0xA0,写入指定的地址;再次启动I2C总线;开启读操作0xA1,读取指定地址内数据,每次发送字节后需等待I2C总线应答,停止I2C总线
  *连续读地址自动加1直到最后一个地址
  */
/**
  *启动I2C总线;开启写操作0xA0,写入指定的地址,向指定地址写入数据,每次发送字节后需等待I2C总线应答,停止I2C总线
  *连续写地址自动加1直到页边缘,AT24C02缓冲区到非易失区需要等待5ms
  */

—>AT24C02详细介绍
—>页写入
—>IIC通信协议总结
—>E2PROM读写函数
—>不同数据类型读写

9.ADC

STM32的ADC的输入时钟不得超过14MHz
ADC转换就是输入模拟的信号量转换成数字量。读取数字量必须等转换完成后,完成一个通道的读取叫做采样周期。采样周期一般来说=转换时间+读取时间,而转换时间=采样时间+12.5个时钟周期。采样时间是你通过寄存器告诉STM32采样模拟量的时间,设置越长越精确
STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期
为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降
单次模式下需要软件或外部事件再触发,ADC才进行下一次转换
STM32的ADC转换时间是12.5个ADC时钟.
然后采样时间可以设置为1.5~239.5个ADC时钟.
采样频率=ADC时钟/(采样时间+12.5)
A/D转换器

/**
  *初始化ADC,使能ADC,校准ADC
  *GPIOB0 ==> 电位器R37,模拟输入,独立模式,单通道单次转换,软件触发,数据右对齐
  *复位校准和开始校准需等待完成
  */
/**
  *获取电压值
  *PB0:ADC通道8,0 ~ 3.3V,12位ADC(2^12 - 1 = 4095)
  *ADC_FLAG_EOC,读取ADC规则数据寄存器(ADC_DR)后自动清除
   */
/**
  *获取温度值,
  *内部温度传感器:ADC1通道16,12位ADC(2^12 = 4096),采样时间大于17.1us,温度值 = (1.43 - Vsense)V / 4.478mV/℃ + 25
  */

—>为什么STM32的ADC转换时钟不能大于14MHz
—>STM32的单双ADC单双通道配置
—>STM32的ADC配置详解

10.PWM & TIM_OC

PWM模式(TIM_OCMode_PWMx):可输出多种波形, ARR设置频率,CCR设置占空比,占空比可达到0%或100%。STM32的每个通用定时器都有4个输入捕获的通道,分别是TIMx_CH1、TIMx_CH2、TIMx_CH3、TIMx_CH4,单通道频率和占空比可以任意设置,但同意定时器四个通道只能输出频率相同、占空比可变的PWM,起始相位不能设置。复用推挽输出
PA1->TIM2_CH2、PA2->TIM2_CH3(USART2)、PA3->TIM2_CH4、PA6->TIM3_CH1、PA7->TIM3_CH2、
TIM_CtrlPWMOutputs(TIMx,ENABLE)函数只对高级定时器有效。

/**
  *定时器2通道2PWM模式初始化
  *复用推挽输出,定时器设置为1us计数,重装载值为1M/pwm频率,pwm周期为1/f_freq(f_freq >= 16),占空比为f_duty
  *TIM2_CH2:PA1,输出捕获x(TIM_OCx)意思是定时器通道x,每次TIM_CNT = TIM_CCRx和TIM_CNT = TIM_ARR时,输出电平极性反向
  */
  void PWM_Init(u32 f_freq, u8 f_duty)
 /**
  *定时器2通道2输出比较模式初始化
  *复用推挽输出,定时器设置为1us计数,重装载值为1M/pwm频率,pwm周期为1/f_freq(f_freq >= 16),占空比为f_duty
  *TIM2_CH2:PA1,输出捕获x(TIM_OCx)意思是定时器通道x,每次TIM_CNT = TIM_CCRx时,输出电平极性反向
  */
  /**
  *输出捕获中断服务函数
  *通过不断更改TIM_Pulse(CCR1)值改变频率和占空比
  */

PWM模式
输出比较模式(TIM_OCMode_Toggle): 只能输出方波,单通道 ARR设置频率,CCR设置每个通道的初始相位,频率和起始相位可以任意设置,占空比不能设置。输出频率为理论计算值一半;使用定时器捕获中断时,同一定时器的四个通道可以分别输出频率和占空比可变的PWM,但占空比不能达到0%或100%。
输出比较模式
TIM_OCMode_Toggle

修改占空比:void TIM_SetComparex(TIM_TypeDefTIMx,uint16_t Compare2);(x=1,2,3,4)。
修改频率:void TIM_SetAutoreload(TIM_TypeDef
TIMx, uint16_t Autoreload);
CCRx值改变时立即生效(Disable)或发生更新事件后生效(Enable):TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);(x=1,2,3,4),比较输出可调PWM必须CCRx值改变时立即生效。
—>PWM配置
—>PWM频率与占空比详解
—>STM32输出比较模式和PWM模式比较
—>Keil5中的虚拟示波器进行软件仿真
虚拟示波器只能仿真程序,遇到LCD初始化或LCD相关函数会进入死循环,无法仿真后续程序引脚电平变化。

11.TIM_IC

同一定时器可以不同通道不同功能。
修改捕获的触发方式使用的函数是:
TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Rising);
输出比较也适用于输入捕获,因为他们操作的实际上是同一个地方
捕获/比较极性配置

/**
  *TIM3_CH2:PA7,浮空输入,上升沿触发中断
  */
/**
  *捕获到上升沿定时器清零,设置下次捕获下降沿触发
  *捕获到下降沿记录时间,设置下次捕获上升沿触发
  *捕获到上沿记录时间,单周期脉冲捕获结束等待捕获开始
  */

—>STM32定时器中的输入捕获滤波器

12.DMA

—>DMA详解

13.数码管

/**
  *初始化PA1~3
  *PA1:SER(数据输入),每输入一位数据,SCK上升沿有效一次,直至八位输入完毕
  *PA2:RCLK(移位寄存器与存储寄存器(锁存)的开关),上升沿时移位寄存器数据进入存储寄存器,下降沿存储寄存器数据不变
  *PA3:SCK(数据传输控制),上升沿时数据寄存器数据移位,下降沿移位寄存器数据不变
  */
/**
  *共阴数码管
  *数据传输方向:SER1(PA1) -> DH'1 -> SER2 -> DH'2-> SER3 -> DH'3,传输数据顺序先DS3再DS2后DS1
  */

扩展板SEG

—>蓝桥杯嵌入式扩展板学习之数码管

14.ADC按键

/**
  *初始化ADC1_CH5和PA5
  *PA5连接AKEY
  */
/**
  *读取ADC值
  *通过按键按下分压值不同判断哪个按键按下
  */
  /*三行代码按键扫描法*/

ADC按键
—>蓝桥杯嵌入式扩展板学习之ADC按键

15.DS18B20

/*初始化PA6(TDQ),上拉输入保证充足供电*/
/**
  *读取DS18B20温度值
  *复位,写入OW_SKIP_ROM(0xCC),跳过ROM,写入DS18B20_CONVERT(0xCC),开始转换,等待750ms
  *复位,写入OW_SKIP_ROM(0xCC),跳过ROM,写入DS18B20_READ(0xBE),开始分别读取高八位和低八位(先低后高)
  *高八位(高五位符号位,后三位整数部分2^6~2^4),低八位(高四位整数部分2^0~2^3,后四位小数部分2^-1~2^-4),温度值 = 总16位 * 0.0625(==> / 16)
  */

DS18B20指令
上拉电阻
—>蓝桥杯嵌入式扩展板学习之DS18B20

16.光敏电阻

光照越强阻值越小,阻值越大电压越大;
RP7电位器电阻过大可能造成暗度无影响,TRDO恒为低电平,RP7电位器电阻过小可能造成亮度无影响,TRDO恒为高电平。

/**
  *初始化PA3、PA4和ADC1_CH4
  *PA3连接TRDO,输出数字量0或1;PA4连接TRAO,输出模拟量ADC转换值
  */
/**
  *获取ADC转换值
  *0:返回电压值(参考电压VDD = 3.3V);1:返回电阻值(参考电阻R46 = 10K)
  */

光敏电阻
—>蓝桥杯嵌入式扩展板学习之光敏电阻

17.ADCx2双通道

/**
  *初始化ADC1_CH4与ADC1_CH5
  *PA4(AO1->PR5):ADC1_CH4;PA5(AO2->PR6):ADC1_CH5
  */
/*获取电压值(按先后顺序单通道采集),PR5\PR6(MAX:10K)与100分压*/

—>蓝桥杯嵌入式扩展板学习之ADCx2

18.DTH11

unsigned int dht11_read(void)函数:返回32位值,高十六位(高8位整数,低8位小数)为湿度值(%),低十六位(高8位整数,低8位小数)为温度值(摄氏度),由于温湿度的分辨率原因,读取时只需要整数部分即可。
采样周期必须大于2秒

/*初始化PA7(HDQ)*/
/*获取温湿度值,高低16位分别湿度值和温度值*/

DHT11
—>蓝桥杯嵌入式扩展板学习之DHT11

19.TLC551测量

扩展板电位器控制产生频率与产生占空比。

/**
  *TIM2_CH2:PA1(PULS1->RP3),TIM2_CH3:PA2(PULS2->RP4),TIM3_CH1:PA6(PWM1->RP1),TIM3_CH2:PA7(PWM2->RP2)
  *浮空输入,上升沿触发中断
  *定时器每个通道需要单独配置
  */
/**
  *定时器多通道输入捕获使用分时捕获
  *捕获到上升沿定时器清零,设置下次捕获下降沿触发
  *捕获到下降沿记录时间,设置下次捕获上升沿触发
  *捕获到上沿记录时间,单周期脉冲捕获结束等待捕获开始
  */

—>蓝桥杯嵌入式扩展板学习之TLC551测量

20.LIS302DL

/**
  *I2C总线初始化
  *更改为RCC_APB2Periph_GPIOA时钟使能
  *宏定义I2C_PORT改为GPIOA,SDA_Pin改为GPIO_Pin_5,SCL_Pin改为GPIO_Pin_4
  */
/**
  *写入LIS302DL寄存器函数
  *写指令:0x38,将数据写入特定地址
  */
/**
  *配置LIS302DL
  *向CTRL_REG1寄存器(0x20)写入指令0x47:配置加速度采集频率200Hz,普通功耗模式,灵敏度为18mg/LSB,非自检模式,使能XYZ轴加速度采集
  */
/**
  *读取LIS302DL寄存器函数
  *写指令:0x38,将数据写入特定地址;读指令:0x38,将数据从特定地址读取
  */
/**
  *读取坐标加速度
  *判断STATUS_REG寄存器(0x27)的数据更新标志位是否为1(0x08),更新则读取XYZ轴加速度相对值(0~255)
  *X轴加速度输出寄存器地址:0x29,Y轴:0x2B,Z轴:0x2D
  */

—>蓝桥杯嵌入式扩展板学习之LIS302DL

蓝桥杯嵌入式旧板资料包免费下载

蓝桥杯嵌入式旧板程序代码免费下载

本人通过许多大牛博客自学在大二获得蓝桥杯嵌入式国赛三等奖,但自身知识体系沉淀不足,借此博客创作过程继续深入学习扩展知识,借鉴蓝桥杯嵌入式相关的大牛介绍,让自己的技术更进一步。
—>蓝桥杯嵌入式推荐博客1
—>蓝桥杯嵌入式推荐博客2

************************************************

HAL库版本

—>STM32CUBEMX代码格式优化

定时器

G4系列通用定时器TIM2和TIM5的重装载值可达到32位
—>HAL库定时器配置

串口

每次中断只能接收一个字符

输入捕获

输入通道:需要被测量的信号从定时器的外部引脚 TIMx_CH1/2/3/4 进入,通常叫 TI1/2/3/4,在后面的捕获讲解中对于要被测量的信号我们都以 TIx 为标准的叫法
捕获通道:捕获通道就是输入捕获整体结构图中的 IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器 CCR1/2/3/4,当发生捕获的时候,计数器 CNT 的值就会被锁存到捕获寄存器。
输入通道是用来输入信号的,捕获通道是用来捕获输入信号的通道,一个输入通道的信号可以同时输入给两个捕获通道。
PWM 输入模式一个输入通道(TIx)会占用两个捕获通道(ICx),所以一个定时器在使用 PWM 输入的时候最多只能使用两个输入通道(TIx),如下图。
输入捕获直接和间接模式
PWM 信号由输入通道 TI1 进入,信号会被分为两路,一路是 TI1FP1,另外一路是 TI2FP2。其中一路是测周期,另一路是测占空比。由此,定时器的输入捕获有两个功能,直接捕获模式和间接捕获模式;
直接模式:只能捕获本身通道的脉冲信号;
间接模式:可以捕获此定时器每个通道的脉冲信号。
auto-reload precload=Disable:自动重装载寄存器写入新值后,计数器立即产生计数溢出,然后开始新的计数周期
auto-reload precload=Enable:自动重装载寄存器写入新值后,计数器完成当前旧的计数后,再开始新的计数周期
—>输入捕获功能配置

ADC

—>adc精度

RTC

读取时间需要将时间和日期一起读取出来。
HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);


# 总结 蓝桥杯嵌入式语言相关博客网络资料已经较为齐全,以上仅为本人所学的知识总结,本文未涉及未学习到的知识点,其中链接网址仅作记录使用,如有侵权或内容有误,敬请联系斧正。若读者觉得本文对学习有所帮助,不妨Give a like,Respect!!!
  • 8
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值