STM32-F407ZGT6

STM32-F407ZGT6

144引脚 1024K字节闪存储存器 LQFP封装 -40°C~85°C工作范围

开发板介绍

开发板介绍

开发板介绍

image-20210404103005743

image-20210404103005743

image-20210404103005743

STM32内核

ARM内核的32位MCU系列

–Cortex-M内核

–标准的ARM架构

uCortex-M4采用ARMv7-ME架构

image-20210404103355273

image-20210404103355273

image-20210404103355273

Cortex-M4与M3对比

image-20210404103505452

image-20210404103505452

image-20210404103505452

ARMv7架构定义了三大分工明确的系列:

“A”系列:面向尖端的基于虚拟内存的操作系统和用户应用

“R”系列:针对实时系统;

“M”系列:对微控制器。

学习方法

掌握调试工具:JTAG

image-20210404102642577

image-20210404102642577

image-20210404102642577

多使用JTAG调试代码,深入理解代码执行流程。在基础不够扎实的时候,不要走马换花的看,要做到深入理解代码涵义。

库函数和寄存器对比学习

项目中大多数用库函数。但是学习,如果你只会看几个函数的话,你根本没有学懂,遇到问题很难自己解决,所以必要了解一下寄存器配置原理,加深理解。

尤其前面几个章节实验,最好了解寄存器配置,加深对STM32本质的理解。

STM32最小系统

供电

复位

时钟:外部晶振(2个)

Boot启动模式选择

下载电路(串口/JTAG/SWD)

后备电池

连接串口

image-20210404151322239

image-20210404151322239

image-20210404151322239

RXD和TXD是连接usb_232 PA9和PA10用来烧录程序

串口1(PA1)才能作为串口下载,也可以用于串口通信

PA2和PA3 只能用来串口通信,不能下载

下载程序flyMcu使用

搜索串口,F4波特率最高设置为76800 其他按照下图配置

image-20210404152655145

image-20210404152655145

image-20210404152655145

下载程序是用Jlink

配置:

enterDebug

enterDebug

enterDebug

Setting

Setting

Setting

select

select

select

下载程序

load

load

load

STM32启动模式

image-20210404153858964

image-20210404153858964

image-20210404153858964

由下图控制

image-20210404153911198

image-20210404153911198

image-20210404153911198

STM32GPIO

7组每组16 从GPIOA.0GPIOA.15 ~~GPIOG.0GPIOG.15

每组IO口有10个寄存器

每个IO口都可以作为中断输入

IO口

4种模式:输入浮空,输入上拉,输入下拉,模拟输入

4种输出模式:开漏输出,开漏复用功能,推挽式输出。推挽式复用功能

4种最大输出速度:2Mhz,25Mhz,50Mhz,100Mhz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
一个端口模式寄存器(GPIOx_MODER)

一个端口输出类型寄存器(GPIOx_OTYPER)

一个端口输出速度寄存器(GPIOx_OSPEEDR)

一个端口上拉下拉寄存器(GPIOx_PUPDR)

一个端口输入数据寄存器(GPIOx_IDR)

一个端口输出数据寄存器(GPIOx_ODR)

一个端口置位/复位寄存器(GPIOx_BSRR)

一个端口配置锁存寄存器(GPIOx_LCKR)

两个复位功能寄存器(低位GPIOx_AFRL & GPIOx_AFRH)

端口模式寄存器

image-20210405164406767

image-20210405164406767

image-20210405164406767

端口输出类型寄存器

image-20210405164434228

image-20210405164434228

image-20210405164434228

高16位未使用

端口输出速度寄存器

image-20210405164457134

image-20210405164457134

image-20210405164457134

端口上拉/下拉寄存器

image-20210405164518948

image-20210405164518948

image-20210405164518948

端口输入数据寄存器

image-20210405164546230

image-20210405164546230

image-20210405164546230

端口输出数据寄存器

QQ图片20210405165937

QQ图片20210405165937

QQ图片20210405165937

端口上输出0或1是有影响的

端口置位/复位寄存器

image-20210405164613372

image-20210405164613372

image-20210405164613372

端口位写0的话不会对端口有影响

写1就会置为1

端口配置锁存寄存器(待写)

端口位复用功能寄存器(待写)

跑马灯

重要源文件:

misc.c

stm32f4xx_rcc.c 一定要加

stm32f4xx_gpio.c 用于串口

stm32f4xx_usart.c SYSTEM里的usart.c有用到

顶层头文件

stm32f4xx.h

使用的头文件

1
#include "delay.h"

使用的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
重要函数:
1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

2个读取输出电平函数:
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置低电平

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置高电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);



//1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO口的4个配置寄存器。
(GPIOx->MODER, GPIOx->OSPEEDR,GPIOx->OTYPER,GPIOx->PUPDR)

GPIOx: GPIOA~GPIOK(最多11组,也就是16X11=176个IO)

//使能IO口时钟。调用函数RCC_AHB1PeriphClockCmd();



led.c

1
2
3
4
5
6
7
8
9
10
11
12
13
GPIO_InitTypeDef GPIO_InitStructre;  //定义结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //开启GPIO的时钟线,之后就可以对IO口进行操作,否则任何操作都是无效操作

GPIO_InitStructre.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStructre.GPIO_OType=GPIO_OType_PP;
//GPIO_OType_PP 推挽 GPIO_OType_OD 开漏
GPIO_InitStructre.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructre.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStructre.GPIO_Speed=GPIO_Speed_2MHz;

GPIO_Init(GPIOF,&GPIO_InitStructre);//IO口初始化
GPIO_SetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10); //设置电平
//GPIO_Pin_9|GPIO_Pin_10 如果IO口初始化参数一样,就这样设置

main.c

1
delay_init(168);

寄存器操作版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//|=与&=
RCC->AHB1ENR |= (1<<5);//启动时钟
GPIOF->MODER &= ~(3<<9*2); //清零
GPIOF->MODER |= 1<<(2*9);//模式设置

GPIOF->OTYPER &= ~(1<<9); //清零
GPIOF->OTYPER |= 0<<9;//类型设置

GPIOF->OSPEEDR &= ~(3<<(9*2));//清零
GPIOF->OSPEEDR |=2<<(9*2);//速度设置

GPIOF->PUPDR &= ~(3<<(9*2));//清零
GPIOF->PUPDR |=1<<(2*9);//上下拉设置

GPIOF->ODR |= 1<<9; //置为1

位操作版本

1
2
3
4
5
6
7
8
PFout(9)=1;  //到sys.h中查看 通过将寄存器的位映射为地址,通过操作地址达到操作位的作用  (读-改-写)
PFout(10)=1;
delay_ms(500);
PFout(9)=0;
PFout(10)=0;
delay_ms(500);
//PFout(n) 输出F组的第N个IO
//PFin(n) 输入F组的第N个IO

蜂鸣器

位置:F8引脚

蜂鸣器电路,接在三极管的C端

蜂鸣器电路图

蜂鸣器电路图

蜂鸣器电路图

三极管

三极管

三极管

按键输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//对应时钟使能
//然后初始化


//按键核心代码
u8 KEY_Scan(u8 mode)
{
static int flag=1;
if(mode==1) flag=1; //0不支持连按
if(flag&&(Key0==0||Key1==0||Key2==0||WK_UP==1))
{
delay_ms(10);
flag=0;

if(Key0==0) return 1;
if(Key1==0) return 2;
if(Key2==0) return 3;
if(WK_UP==1) return 4;

}else if(Key0==1&&Key1==1&&Key2==1&&WK_UP==0) flag=1;
return 0;
}

时钟系统

时钟频率的计算

PLLclock计算(常用)

image-20210429201803486

image-20210429201803486

image-20210429201803486

STM32F4开发指南-库函数版本_V1

STM32F4开发指南-库函数版本_V1

STM32F4开发指南-库函数版本_V1

image-20210429203507847

image-20210429203507847

image-20210429203507847

SystemInit函数解读

初始化之后的状态:
SYSCLK(系统时钟) =168MHz
AHB总线时钟(HCLK=SYSCLK) =168MHz
APB1总线时钟(PCLK1=SYSCLK/4) =42MHz
APB2总线时钟(PCLK2=SYSCLK/2) =84MHz
PLL主时钟 =168MHz

获取系统时钟函数

初始化之后可以通过变量SystemCoreClock获取系统变量。如果SYSCLK=168MHz,那么变量SystemCoreClock=168000000。

Systick定时器

24位倒时

睡眠时也能工作

4个Systick寄存器

1
2
3
4
CTRL    SysTick 控制和状态寄存器  LOAD(开关)
SysTick 自动重装载除值寄存器 VAL
SysTick 当前值寄存器 CALIB
SysTick 校准值寄存器

SysTick 控制和状态寄存器- CTRL

image-20210429211531521

image-20210429211531521

image-20210429211531521

1
2
3
对于STM32,外部时钟源是 HCLK(AHB总线时钟)的1/8
内核时钟是 HCLK时钟
配置函数:SysTick_CLKSourceConfig();

SysTick 重装载数值寄存器- LOAD

image-20210429211659749

image-20210429211659749

image-20210429211659749

SysTick 当前值寄存器- VAL

image-20210429211712874

image-20210429211712874

image-20210429211712874

固件库中的Systick相关函数:

1
2
3
4
5
6
7
SysTick_CLKSourceConfig()    //Systick时钟源选择  misc.c文件中

SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断
//core_cm3.h/core_cm4.h文件中
Systick中断服务函数:

void SysTick_Handler(void);

端口复用

概念:本来是GPIO,复用完就作为串口1的发送接收引脚

复用器一次只允许一个外设的复用功能(AF)连接到对应的IO口

每个IO引脚都有一个复用器,该复用器采用16路复用功能输入(AF0到AF15),可通过GPIOx_AFRL(针对引脚0-7)和GPIOx_AFRH(针对引脚8-15)寄存器对这些输入进行配置,每四位控制一路复用。

AFRL寄存器

image-20210503214607884

image-20210503214607884

image-20210503214607884

对于ADC和DAC,在GPIOx_MODER寄存器中将所需I/O配置为模拟通道

端口复用配置过程

1.GPIO端口时钟使能

2.复用外设时钟使能

3.端口模式配置为复用功能。 GPIO_Init()函数

4.配置GPIOx_AFRL或者GPIOx_AFRH寄存器,将IO连接

到所需的AFx。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 ①
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 ②

//USART1端口配置③
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

//串口1对应引脚复用映射 ④
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1

完全重映射

1
2
3
4
5
6
7
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);  //完全重映射
/*
TIM2_CH1_ETR -> PA15
TIM2_CH2 -> PB3
TIM2_CH3 -> PB10
TIM2_CH4 -> PB11
*/

禁用JTAG

1
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); 	// 改变指定管脚的映射,JTAG-DP 禁用 + SW-DP 使能,禁用JTAG

NVIC中断优先级管理

STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级。

位置:STM32F4xx中文参考手册》P234 表45和46

中断管理方法

image-20210503233951039

image-20210503233951039

image-20210503233951039

抢占优先级 & 响应优先级区别

1.高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
2.抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
3.抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
4.如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

中断优先级设置

初始化函数:

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

分组函数:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

最好只设置一次

代码:

1
2
3
4
5
6
7
NVIC_InitTypeDef   NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器

串口通信

STM32的串口通信接口

UART:通用异步收发器
​ USART:通用同步异步收发器

STM32串口异步通信需要定义的参数

① 起始位

② 数据位(8位或者9位)

③ 奇偶校验位(第9位)

④ 停止位(1,15,2位)

⑤ 波特率设置

image-20210503235553407

image-20210503235553407

image-20210503235553407

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
void My_USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO寄存器
USART_InitTypeDef USART_InitStructure; //串口寄存器
NVIC_InitTypeDef NVIC_InitStructure; //优先级寄存器

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
//使能IO
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);//端口复用A9->
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//端口复用A10->
//IO配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口配置
USART_InitStructure.USART_BaudRate=115200;//波特率设置
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;

USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1 ,ENABLE);//使能串口


USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//使能相关中断

//优先级配置
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);


}

void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)){


res=USART_ReceiveData(USART1);//接受数据,从DR读取接受到的数据

USART_SendData(USART1,res); //发送数据到串口,DR

}
/*
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
*/

状态寄存器

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

image-20210504000831841

image-20210504000831841

image-20210504000831841

数据寄存器

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

image-20210504000938955

image-20210504000938955

image-20210504000938955

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

image-20210504001007646

image-20210504001007646

image-20210504001007646

波特率计算公式

image-20210504001138065

image-20210504001138065

image-20210504001138065

串口配置一般步骤

① 串口时钟使能:RCC_APBxPeriphClockCmd();
GPIO时钟使能:RCC_AHB1PeriphClockCmd();
② 引脚复用映射:
GPIO_PinAFConfig();
③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF
④串口参数初始化:USART_Init();
⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init();
USART_ITConfig();
⑥使能串口:USART_Cmd();
⑦编写中断处理函数:USARTx_IRQHandler();
⑧串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
FlagStatus USART_GetFlagStatus();
void USART_ClearITPendingBit();

Printf支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle;
};

FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{ x = x; }

//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif

外部中断

头文件 exti.h

1
2
3
4
5
6
7
GPIOx.0映射到EXTI0

GPIOx.1映射到EXTI1



GPIOx.15映射到EXTI15

IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数

image-20210504001736923

image-20210504001736923

image-20210504001736923

中断服务函数列表

用于写中断后的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXTI0_IRQHandler

EXTI1_IRQHandler

EXTI2_IRQHandler

EXTI3_IRQHandler

EXTI4_IRQHandler

EXTI9_5_IRQHandler

EXTI15_10_IRQHandler
/*
EXTI0_IRQHandler()
{
...

}
*/

外部中断的一般配置步骤

①使能SYSCFG时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
②初始化IO口为输入。
GPIO_Init();
③设置IO口与中断线的映射关系。
void SYSCFG_EXTILineConfig();
④初始化线上中断,设置触发条件等。
EXTI_Init();
⑤配置中断分组(NVIC),并使能中断。
NVIC_Init();
⑥编写中断服务函数。
EXTIx_IRQHandler();
⑦清除中断标志位
EXTI_ClearITPendingBit();

独立看门狗

功能:在启动正常运行的时候,系统不能复位。在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。

独立看门狗(IWDG)由专用的低速时钟(LSI)驱动,即使主时钟发生故障它仍有效。

键寄存器

预分频寄存器IWDG_PR:0~2位有效。具有写保护功能,要操作先取消写保护

重装载寄存器IWDG_RLR:0~11位有效。具有写保护功能,要操作先取消写保护

image-20210504002306393

image-20210504002306393

image-20210504002306393

预分频寄存器

image-20210504002357377

image-20210504002357377

image-20210504002357377

重载寄存器

image-20210504002432525

image-20210504002432525

image-20210504002432525

状态寄存器

image-20210504002451419

image-20210504002451419

image-20210504002451419

独立看门狗超时时间

image-20210504002506848

image-20210504002506848

image-20210504002506848

1
2
溢出时间计算:
Tout=((4×2^prer) ×rlr) /32 (M4)

时钟频率LSI=32K, 一个看门狗时钟周期就是最短超时时间。最长超时时间= (IWDG_RLR寄存器最大值)X看门狗时钟周期

IWDG独立看门狗操作库函数

1
2
3
4
5
6
7
8
9
10
11
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//取消写保护:0x5555使能

void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);//设置预分频系数:写PR

void IWDG_SetReload(uint16_t Reload);//设置重装载值:写RLR

void IWDG_ReloadCounter(void);//喂狗:写0xAAAA到KR

void IWDG_Enable(void);//使能看门狗:写0xCCCC到KR

FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);//状态:重装载/预分频 更新

独立看门狗操作步骤

① 取消寄存器写保护:

IWDG_WriteAccessCmd();

② 设置独立看门狗的预分频系数,确定时钟:

IWDG_SetPrescaler();

③ 设置看门狗重装载值,确定溢出时间:

IWDG_SetReload();

④ 使能看门狗

IWDG_Enable();

⑤ 应用程序喂狗:

IWDG_ReloadCounter();

窗口看门狗

窗口看门狗由从APB1时钟分频后得到时钟驱动。通过可配置的时间窗口来检测应用程序非正常的过迟或过早操作。

独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。

image-20210504002906239

image-20210504002906239

image-20210504002906239

2种情况之一时产生看门狗复位

①当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。

② 当计数器的数值从0x40减到0x3F时【T6位跳变到0】。

如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位

窗口看门狗超时时间

image-20210504003037163

image-20210504003037163

image-20210504003037163

image-20210504003042213

image-20210504003042213

image-20210504003042213

image-20210504003048243

image-20210504003048243

image-20210504003048243

控制寄存器WWDG_CR

image-20210504003143608

image-20210504003143608

image-20210504003143608

1
2
void WWDG_Enable(uint8_t Counter);//启动并设置初始值
void WWDG_SetCounter(uint8_t Counter);//喂狗

配置寄存器WWDG_CFR

image-20210504003222479

image-20210504003222479

image-20210504003222479

1
2
3
void WWDG_EnableIT(void);//使能提前唤醒中断
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
void WWDG_SetWindowValue(uint8_t WindowValue);

状态寄存器WWDG_SR

image-20210504003250942

image-20210504003250942

image-20210504003250942

1
2
FlagStatus WWDG_GetFlagStatus(void);
void WWDG_ClearFlag(void);

窗口看门狗的一般配置步骤

① 使能看门狗时钟:

RCC_APB1PeriphClockCmd();

② 设置分频系数:

WWDG_SetPrescaler();

③ 设置上窗口值:

WWDG_SetWindowValue();

④ 开启提前唤醒中断并分组(可选):

WWDG_EnableIT();

NVIC_Init();

⑤ 使能看门狗:

WWDG_Enable();

⑥ 喂狗:

WWDG_SetCounter();

⑦编写中断服务函数

WWDG_IRQHandler();

wwdg.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{

NVIC_InitTypeDef NVIC_InitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE); //使能窗口看门狗时钟

WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT.
WWDG_SetPrescaler(fprer); //设置分频值
WWDG_SetWindowValue(wr); //设置窗口值
// WWDG_SetCounter(WWDG_CNT);//设置计数值
WWDG_Enable(WWDG_CNT); //开启看门狗

NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn; //窗口看门狗中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02; //抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级为3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能窗口看门狗
NVIC_Init(&NVIC_InitStructure);

WWDG_ClearFlag();//清除提前唤醒中断标志位
WWDG_EnableIT();//开启提前唤醒中断
}



//窗口看门狗中断服务程序
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT); //重设窗口看门狗值
WWDG_ClearFlag();//清除提前唤醒中断标志位
LED1=!LED1;
}

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
KEY_Init(); //初始化按键
LED0=0; //点亮LED0
delay_ms(300);
WWDG_Init(0x7F,0X5F,WWDG_Prescaler_8); //计数器值为7f,窗口寄存器为5f,分频数为8

while(1)
{
LED0=1; //熄灭LED灯
}
}

通用定时器

STM32F40x系列总共最多有14个定时器

image-20210504211715441

image-20210504211715441

image-20210504211715441

4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:

① 输入捕获

② 输出比较

③ PWM 生成(边缘或中间对齐模式)

④ 单脉冲模式输出

可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):

①更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)

②触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

③输入捕获

④输出比较

⑤支持针对定位的增量(正交)编码器和霍尔传感器电路

触发输入作为外部时钟或者按周期的电流管理

计数器模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式

image-20210504212028234

image-20210504212028234

image-20210504212028234

内部时钟选择

image-20210504212300402

image-20210504212300402

image-20210504212300402

计数器当前值寄存器CNT

image-20210504212422395

image-20210504212422395

image-20210504212422395

TIMx->CNT 获取当前值

预分频寄存器TIMx_PSC

image-20210504212439222

image-20210504212439222

image-20210504212439222

自动重装载寄存器(TIMx_ARR)

image-20210504212454628

image-20210504212454628

image-20210504212454628

控制寄存器1(TIMx_CR1)

image-20210504212506320

image-20210504212506320

image-20210504212506320

DMA中断使能寄存器(TIMx_DIER)

image-20210504212523100

image-20210504212523100

image-20210504212523100

image-20210504212625091

image-20210504212625091

image-20210504212625091

image-20210504212551406

image-20210504212551406

image-20210504212551406

image-20210504212634593

image-20210504212634593

image-20210504212634593

image-20210504212646983

image-20210504212646983

image-20210504212646983

高级定时器做PWM注意事项

要加这句代码

1
TIM_CtrlPWMOutputs(TIM8,ENABLE);    //MOE 主输出使能

OLED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
u8 OLED_GRAM[128][8];

//OLED初始化
void OLED_Init(void)

//功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])

//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)

//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 chr[],u8 Char_Size)


//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)


//m^n函数
u32 oled_pow(u8 m,u8 n)


//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)


//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)

//开启OLED显示
void OLED_Display_On(void)

//fill_picture
void fill_picture(unsigned char fill_Data)


//OLED写命令
void Write_OLED_Command(unsigned char OLED_Command)

ESP8266

AP模式:可以将ESP8266作为热点,可以让其他的设备连接上它;
STA模式:可以连接上当前环境下的WIFI热点。、

HC-05蓝牙模块

灯的状态

模块自带了一个状态指示灯:STA。该灯有3种状态
此时STA慢闪(1秒亮1次),模块进入AT状态
模块配对成功,此时STA双闪(一次闪2下,2秒闪一次)。

进入AT模式的两种方式

①上电同时/上电之前将KEY设置为VCC,上电后,模块即进入AT指令状态。

② 模块上电后,通过将KEY接VCC,使模块进入AT状态。

方法1进入AT状态后,模块的波特率为:38400(8位数据位,1位停止位)。方法2进入AT状态后,模块波特率和通信波特率一致

指令结构

1
2
AT+<CMD><=PARAM>  设置参数格式
AT+<CMD> ? 查询参数格式

其中CMD(指令)和PARAM(参数)都是可选的,不过切记在发送末尾添加回车符(\r\n)(串口调试助手勾选发送新行就不用添加回车符),否则模块不响应,比如我们要查看模块的版本:

1
2
3
4
5
串口发送:AT+VERSION?\r\n

模块回应:+VERSION:2.0-20100601

OK

修改模块主从指令

1
2
3
AT+ROLE=0或1,该指令来设置模块为从机或主机

AT+ROLE? 来查看模块的主从状态

设置记忆指令

AT+CMODE=1,该指令设置模块可以对任意地址的蓝牙模块进行配对,模块默认设置为该参数。

AT+CMODE=0,该指令设置模块为指定地址配对,如果先设置模块为任意地址,然后配对,接下去使用该指令,则模块会记忆最后一次配对的地址,下次上电会一直搜索该地址的模块,直到搜索到为止。

修改通信波特率指令

1
AT+UART= <Param1>,<Param2>,<Param3>

该指令用于设置串口波特率、停止位、校验位等。

Param1为波特率,可选范围为:4800、9600、19200、38400、

57600、115200、230400、460800、921600、1382400;

Param2为停止位选择,0表示1位停止位,1表示2位停止位;

Param3为校验位选择,0表示没有校验位(None),1表示奇校验

(Odd),2表示偶校验(Even)。

比如我们发送:AT+UART=9600,0,0,则是设置通信波特率为9600,1位停止位,没有校验位,这也是我们模块的默认设置。

修改密码指令

1
AT+PSWD=<password>

该指令用于设置模块的配对密码,password必须为4个字节长度。

修改蓝牙模块名字

1
AT+NAME=<name>

该指令用于设置模块的名字,name为你要设置的名字,必须为ASCII字符,且最长不能超过32个字符。模块默认的名字为ATK-HC05。比如发送:AT+NAME=GUANG ZHOU,即可设置模块名字为“GUANG ZHOU”。

Usmart调试组件

Usmart文件说明

usmart.c负责与外部互交等。

usmat_str.c主要负责命令和参数解析。

usmart_config.c主要由用户添加需要由usmart管理的函数。

usmart.h和usmart_str.h是两个头文件,其中usmart.h里面含有几个用户配置宏定义,可以用来配置usmart的功能及总参数长度(直接和SRAM占用挂钩)、是否使能定时器扫描、是否使用读写函数等。

添加USMART包到工程,以及设置头文件路径

添加需要调用的函数添加到usmart_config.c文件中

主函数调用usmart_dev.init函数初始化Usmart

通过助手发送命令,调用再usmart中注册过的函数

1
2
3
4
5
6
7
8
9
10
11
delay_init(168);
uart_init(115200);
usmart_dev.init(SystemCoreClock/1000000);
LED_Init();
while(1) //这个要加串口通讯才能用,我也不知道为啥
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
delay_ms(500);
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
delay_ms(500);
}

这段函数写在main函数上面

1
2
3
4
5
6
7
void light_led(void)  //外部实现
{
GPIO_SetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
delay_ms(500);
GPIO_ResetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
delay_ms(500);
}
1
2
3
4
5
extern void light_led(void); //启用外部定义,就是上面那个函数,不用再led头文件里声明,因为这个已经算是声明了,就差实现了
struct _m_usmart_nametab usmart_nametab[]=
{
(void*)light_led,"void light_led(void)", //这是格式,记住就好
};

USMART系统命令

?: 获取帮助信息

help: 获取帮助信息

list: 可用的函数列表

id: 可用函数的ID列表

hex: 参数16进制显示,后跟空格+数字即执行进制转换

dec: 参数10进制显示,后跟空格+数字即执行进制转换

runtime 1,开启函数运行计时;0,关闭函数运行计时;

请按照程序编写格式输入函数名及参数并以回车键结束.

RTC时钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x5050)
{
RCC_LSEConfig(RCC_LSE_ON); //LSE时钟需要加
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET)//检查指定的RCC标志位设置与否,等待低速晶振就绪
{
retry++;
delay_ms(10);
}
if(retry==0)return 1; //LSE 开启失败
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟

//配置RTC

RTC_InitStructure.RTC_AsynchPrediv = 0x7F;//RTC异步分频系数(1~0X7F)
RTC_InitStructure.RTC_SynchPrediv = 0xFF;//RTC同步分频系数(0~7FFF)
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//RTC设置为,24小时格式
RTC_Init(&RTC_InitStructure);


RTC_Set_Time(11,28,00,RTC_H12_AM); //设置时间
RTC_Set_Date(21,7,4,7); //设置日期

RTC_WriteBackupRegister(RTC_BKP_DR0,0x5050); //标记已经初始化过了
}

硬件随机数

1
2
3
u8  RNG_Init(void);			//RNG初始化
u32 RNG_Get_RandomNum(void);//得到随机数
int RNG_Get_RandomRange(int min,int max);//生成[min,max]范围的随机数

ADC模数转换

IIC

24c02

SPI

W25Qxx


#stm32