stm32单片机学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39642794/article/details/82112757

一些重要的知识:
0x0d(回车符\r) 0xoa(换行符\n)
stm32f4xx.h:包含了stm32f4的寄存器结构体的定义(类似于c51的reg52.h)f4顶层头文件 main必须要加这个顶层文件 可以加“sys.h”因为它里面
包含stm32f4xx.h头文件
sys.h:位带操作的头文件
#include"sys.h"


//读取io口的状态
频率,是单位时间内完成周期性变化的次数

没解决的问题:分频因子

在stm32最小系统中 “common.h”代替了 “sys.h”

1.时钟源与时钟:
在 STM32F4 中,有 5 个最重要的时钟源,为 HSE、LSE(前二个是外部时钟源)、PLL,HSI,PLL(内部时钟源)
stm32f4时钟系统初始化是在system_stm32f4xx.c中的SystemInit()函数中完成。
对于系统时钟关键寄存器设置主要是在SystemInit()函数中调用SetSysClock()函数来设置;
最后我们总结一下 SystemInit()函数中设置的系统时钟大小:
SYSCLK时钟源为PLL
AHB 时钟,APB2 高速时钟,APB1 低速时钟,这些时钟都是来源于 SYSCLK 系统时钟,AHB最大时钟为168MHz, APB2高速时钟最大频率为84MHz,而APB1低速时钟最大频
率为 42MHz
SYSCLK(系统时钟) =168MHz
AHB 总线时钟(HCLK=SYSCLK) =168MHz
APB1 总线时钟(PCLK1=SYSCLK/4) =42MHz
APB2 总线时钟(PCLK2=SYSCLK/2) =84MHz
PLL 主时钟 =168MHz
函数选择:
一类是外设时钟使能函数,一类是时钟源选择和分频因子配置函数,还有一类是外设复位函数
时钟源的选择以及时钟使能等函数都是在 RCC 相关固件库文件 stm32f4xx_rcc.h 和 stm32f4xx_rcc.c 中声明和定义的
5个外设时钟使能函数 5个函数分别用来使能5个总线下面挂载的外设时钟(AHB1总线,AHB2总线,AHB3总线,APB1总线,APB2总线,要使能某个外设,调用对应的总线外设时钟使能函数)
一类是外设时钟使能函数
{
时钟使能函数:
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
时钟源使能函数:
void RCC_HSICmd(FunctionalState NewState);
void RCC_LSICmd(FunctionalState NewState);
void RCC_PLLCmd(FunctionalState NewState);
void RCC_PLLI2SCmd(FunctionalState NewState);
void RCC_PLLSAICmd(FunctionalState NewState);
void RCC_RTCCLKCmd(FunctionalState NewState);
}
比如我们要使能 GPIOA,我们只需要在 stm32f4xx_rcc.h 头文件里面搜索 GPIOA,就可以搜
索到对应的时钟使能函数的第一个入口参数为 RCC_AHB1Periph_GPIOA,从这个宏定义标识
符一眼就可以看出,GPIOA 是挂载在 AHB1 下面。同理,对于串口 1 我们可以搜索 USART1,
找到标识符为 RCC_APB2Periph_USART1,那么很容易知道串口 1 是挂载在 APB2 之下

GPIOA~GPIOC 是挂载在 AHB1 下面,TIM2~TIM4是挂载在 APB1 下面,TIM1 和 TIM8 是挂载在 APB2 

一类是时钟源选择和分频因子配置函数:
{
比如我们要设置系统时钟源为 HSI,那么我们可以调用系统时钟源配置函数:
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
具体配置方法如下:
void RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);//配置时钟源为 HSI
又如我们要设置 APB1 总线时钟为 HCLK 的 2 分频,也就是设置分频因子为 2 分频,那么
如果我们要使能 HSI,那么调用的函数为:
void RCC_PCLK1Config(uint32_t RCC_HCLK);
具体配置方法如下:
RCC_PCLK1Config(RCC_HCLK_Div2);
}


2.IO简介:
组FWLib下面存放的是ST官方提供的固件库函数
组CORE下面存放的是固件库必须的核心文件和启动文件
组SYSTEM是A~~~提供的共用代码,包括Systick延时函数,IO口位带操作,以及串口相关函数
组HARDWARE下面存放的是每个实验外设驱动代码,他的实现是通过调用FWLib下面的固件库文件实现的
组USER下面存放的主要是用户的代码。stm32f4xx_it.c里面存放的是中断服务函数,system_stm32f4xx.c文件用户不需要修改,还有主函数main.c
IO端口对应的库函数以及相关定义在文件stm32f4xx_gpio.h和stm32f4xx_gpio.c
4种输入模式:       输出:
输入浮空                开漏输出(输出强低电平,做电流型)
输入上拉                推挽输出(输出强高低电平,连接数字器件)
输入下拉                推挽复用
模拟输入                开漏复用
GPIO相关的函数和定义在stm32f4xx_gpio.c文件和头文件stm32f4xx_gpio.h文件中
GPIO_Init(参数1(哪一个io口),参数2,(结构体))
 结构体.第二个成员(GPIO_Mode)
{
GPIO_Mode_IN (0x00)是用来设置为复位状态的输入,GPIO_Mode_OUT( 是通用输出模式,
GPIO_Mode_AF 是复用功能模式,GPIO_Mode_AN 是模拟输入模式
}
结构体.第四个成员输出类型设置(GPIO_OType)
{
如果需要设置为输出推挽模式,那么选择值 GPIO_OType_PP,如果需要设置为输出开漏模式,那么设置值为 GPIO_OType_OD
}
结构体.第五个成员(GPIO_PuPd)IO 口的上下拉
{
GPIO_PuPd_NOPULL 为不使用上下拉,GPIO_PuPd_UP 为上拉,GPIO_PuPd_DOWN 为下拉
}
IO重要函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取输入电平(读取一个io口或者几个io口)
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读出输出电平(同上)
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)||uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);//一组io口的状态
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//输出高
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//输出低
GPIO相关函数 操作步骤为:
1) 使能 IO 口时钟。调用函数为 RCC_AHB1PeriphClockCmd ()。
2) 初始化 IO 参数。调用函数 GPIO_Init();
3) 操作 IO。操作 IO 的方法就是上面我们讲解的方法。
 assert_param(参数);//参数有效性进行判断 必须熟练掌握右键go to definition of查看变量函数定义的使用方法

按键输入:
KEY0、KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的,并且外部都没有上下拉电阻

串口:stm32f4xx_usart.h 和 stm32f4xx_usart.c
IO:,一个 GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用
对于外设复用功能的配置, 除了  ADC 和 和 DAC  要将 IO  配置为模拟通道之外其他外设功能一律要配置为复用功能模式
N_USART1_RX 和 USART_REC_LEN 都是在 usart.h 文件里面定义的,当需要使用串口接收的时候,我们只要在 usart.h 里面设置 EN_USART1_RX 为 1 就可以了。不使用的时候,设置,
EN_USART1_RX 为 0 即可
1) 串口时钟使能,GPIO 时钟使能。
2) 设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
3) GPIO 初始化设置:要设置模式为复用功能。
4) 串口参数初始化:设置波特率,字长,奇偶校验等参数。
5) 开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。
6) 使能串口。
7) 编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。
函数:
GPIO_PinAFConfig();配置相应的引脚复用器映射,stm32f4xx_gpio.c中
GPIO端口模式设置:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF(复用功能);
USART_Cmd(USART1, ENABLE); //使能串口
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//发送函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//接受函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)//读取串口状态//RXNE(读数据寄存器非空)以及 TC(发送完成)(参数2)
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)//使能相应的中断
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)// 获取相应中断状态


中断管理函数主要在misc.c文件里面void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);分组函数
STM32 NVIC中断优先级管理:
{
组0 0 位抢占优先级(0),4 位响应优先级(0~15)
组1 1 位抢占优先级,3 位响应优先级
。。。
组4 4位抢占优先级 , 0位响应优先级
STM32F40xx/STM32F41xx 的 92 个中断里面,包括 10 个内核中断和 82 个可屏蔽中断,具有 16 级可编程的中断优先级,而我们常用的就是这 82 个可屏蔽中断
抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级
中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断
}
中断初始化函数NVIC_Init:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个
中断对应的名字。例如 USART1_IRQn。NVIC_IRQChannelPreemptionPriority:定义这个中断的
抢占优先级别。NVIC_IRQChannelSubPriority:定义这个中断的子优先级别,也叫响应优先级。//io5~9 用EXTI9_5_IRQn io10~15  EXTI15_10_IRQn 
NVIC_IRQChannelCmd:该中断通道是否使能

外部中断实验: stm32f4xx_exti.h 和 stm32f4xx_exti.c 文件中
GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.0,GPIOI.0
1)  使能 IO  口时钟,初始化 IO 
2)  开启 SYSCFG  时钟,设置 IO  口与中断线的映射关系。
接下来,我们要配置 GPIO 与中断线的映射关系,那么我们首先需要打开 SYSCFG 时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 时钟
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);//GPIO 与中断线的映射关系//stmf4xx_syscfg.c中
EXTI3_IRQHandler()中断函数在startup_stm32f40_41xxx.s

定时器中断实验:stm32f4xx_tim.h 和 stm32f4xx_tim.c 文件
定时器的时钟来源:
1)内部时钟(CK_INT)
2)外部时钟模式 1:外部输入脚(TIx)
3)外部时钟模式 2:外部触发输入(ETR),仅适用于 TIM2、TIM3、TIM4
4)内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。

这里的 CK_INT
时钟是从 APB1 倍频的来的,除非 APB1 的时钟分频数设置为 1(一般都不会是 1),否则通用
定时器 TIMx 的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx
的时钟就等于 APB1 的时钟。这里还要注意的就是高级定时器以及 TIM9~TIM11 的时钟不是来
自 APB1,而是来自 APB2 的
因为系统初始化 SystemInit 函数里面已经初始化 APB1 的时钟为 4 分频,所
以 APB1 的时钟为 42M,而从 STM32F4 的内部时钟树图(图 4.3.1.1)得知:当 APB1 的时钟
分频数为 1 的时候,TIM2~7 以及 TIM12~14 的时钟为 APB1 的时钟,而如果 APB1 的时钟分频
数不为 1,那么 TIM2~7 以及 TIM12~14 的时钟频率将为 APB1 时钟的两倍,因为系统时钟等于168,168/42=4,APB2高速时钟最大频率为84MHz.,
同理,APB1和APB2分频系数都不为1,所以这里定时器的频率为APB2和APB1的两倍

//

有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5,TIM9~TIM14 等通用定时器,还有 TIME6 和 TIME7 等基本定时器

函数:
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)//读取中断状态寄存器的值
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)//固件库中清除中断标志位
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能定时器
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//允许更新中断 参数二:定时器中断的类型
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)//使能TIM3

typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
第一个参数TIM_Prescaler用来设置分频系数
第二个参数计数方式
第三个自动重载计数周期值
第四个用来设置时钟分频因子//

高级定时器(TIM1,TIM8) 位数计数:16  计数器模式:向上,向下,向上/向下(中央对齐模式) 捕获/比较通道(4) 带可编程死区的互补输出
通用定时器(TIM2,TIM5) 位数计数:32                     同上                                                             同上                   通用/定时计数 pwm输出 输入捕获 输出比较
通用定时器(TIM3,TIM4)                 16                     同上                                                              同上                    同上
通用定时器(TIM9~TIM14)              16                    向上                                                                2                       同上
基本定时器(TIM6,TIM7)                  16                   向上/向下                                                         0                       主要应用于驱动DAC
向上计数模式 计数器从0计数到自动加载值
向下计数模式 自动加载值到0
中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数

Tout= ((arr+1)*(psc+1))/Tclk;
其中:
Tclk:TIM3 的输入时钟频率(单位为 Mhz)。
Tout:TIM3 溢出时间(单位为 us)。
arr:自动重装值。 psc:时钟预分频数
因为他们都是16位 ,因为这两位取1~65535之间的任意数值。

PWM输出实验: stm32f4xx_tim.h 和 stm32f4xx_tim.c
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9 复用为定时器 14//在函数gpio.c中可以找到
函数:
PWM 通道
设置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare2);
理所当然,对于其他通道,分别有一个函数名字,函数格式为 TIM_SetComparex(x=1,2,3,4)。

TIM_OC1Init(TIM14, &TIM_OCInitStructure); //初始化外设 TIM1 4OC1
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能预装载寄存器
//每设一个通道,就要同时一次。
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE 使能
TIM_ARRPreloadConfig(TIM14,ENABLE);//使能自动重装载的预装载寄存器允许位
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能预装载寄存器

CCER(寄存器):CC1P位:0:高电平有效  1:低电平有效
PWM(110)模式1:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平,向下计数时则相反
PWM(111)模式2:在向上计数时,同上相反。
结构体成员1)TIM_OCMode决定是模式一(TIM_OCMode_PWM1)还是模式二(TIM_OCMode_PWM2)
                 2)TIM_OCPolarity 决定输出记性是低还是高 (TIM_OCPolarity_Low:低电平为有效,TIM_OCPolarity_High :高电平有效)
占空比:高电平在一整个电平周期内占的比例

输入捕获:
除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。STM32F4 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿
/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获
 TIMx_ARR 和 TIMx_PSC,这两个寄存器用来设自动重装载值和 TIMx 的时钟分频

函数:void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)//初始化输入捕获参数
成员1(TIM_Channel)设置通道 输入捕获通道X就对应着通道X 为TIM_Channel_1(通道1)
成员2 (ICPolarity)捕获极性 TIM_ICPolarity_Rising,上升沿捕获
单独设置通道 1 捕获极性的函数为:
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling)
成员3(ICSelection)映射 配置IC1直接映射在Tl1上,选择TIM_ICSeletion_DirectTI
成员4 (TIM_ICPrescaler) 用 来 设 置 输 入 捕 获 分 频 系 数 几次上升沿或者几次下降沿开始捕获
成员5  (TIM_ICFilter) 设置滤波器长度,这里我们不使用滤波器
连续采样到 8 次通道 1 的电平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)
TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断和捕获中断

if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET){}//判断是否为更新中断
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET){}//判断是否发生捕获事件
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);//清除中断和捕获标志位//TIM_IT_CC1捕获通道1 //TIM_IT_Update(定时器溢出)

void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx,uint16_t TIM_OCPolarity);通道极性设置独立函数//设置是上升沿还是下降沿捕获
uint32_t TIM_Getpture1(TIM_TypeDef *TIMx);//获取当前的捕获值

ADC实验:
STM32F4 其 ADC 的规则通道组最多包含 16 个转换,而注入通道组最多包含 4 个通道
规则通道转换期间可产生DMA请求
STM32F4xx 系列一般都有 3 个 ADC
一个ADC有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号
规则通道组(最多16个) 注入通道组(最多4个 相当于中断)
可以软件和外部触发
单次转换模式:ADC执行一次转换(完成一个通道,置位)
连续转换模式(DMA):ADC结束一个转换后立即启动一个新的转换(完成一组通道,才被置位)
扫描转换模式:
不要让ADC时钟超过36MHz 否则可能不准(所以给pclk2为84MHz 至少给4分频)
数据对齐:因为16位的寄存器要存储12位的数据,所以就涉及到右对齐和左对齐
转化时间=采样时间+12个周期

IIC实验:
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据(而不是电平信号)。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据(同上)。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障

IIC是半双工通信方式
SDA是数据线(一个数据线做收发使用,所以是半双工)
SCL是时钟线
SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

IIC协议:
1.空闲状态
2.开始信号
3.停止信号
4.应答信号ACK

5.数据的有效性
6.数据的传输

猜你喜欢

转载自blog.csdn.net/qq_39642794/article/details/82112757