目录
step2.选择时基单元时钟源(这里定时中断选择内部时钟源)
step4.配置输出中断控制(使能更新中断,输出至NVIC)
step5.配置NVIC(在NVIC中打开TIM中断通道,分配优先级)
step1.配置时基单元(时钟源选择、预分频器PSC、计数器CNT计数模式、自动重载器ARR)
step2. 配置输出比较单元(捕获比较器CCR、输出比较模式、极性选择、输出使能)
step3. 配置GPIO(复用推免输出模式->输出PWM波)
测频率 :输入信号上升沿触发捕获单元的通道1从模式自动清零CNT)
step1. 配置GPIO(上拉/浮空输入模式,GPIO复用为TIM2输入)
step3.配置输入捕获单元(输入滤波器、边沿极性选择、直连通道/交叉通道、分频器)
step4.从模式触发源(上升沿信号TI1FP1),从模式触发操作(reset--CNT清零)
测占空比:输入信号TI1FP1上升沿触发捕获单元的通道1(从模式自动清零CNT);输入信号TI1FP2上升沿触发捕获单元的通道2
step2. 配置ADC通用寄存器(ADC模式,CCLK分频器-stm32f4的ADC时钟不超过36Mhz)
step3.配置模拟多路开关(“点菜”:选择输入通道,配置通道转换顺序,采样周期)
step4.配置ADC转换(单次/连续,扫描/非扫描,通道数,触发控制-外部事件触发/软件触发,数据对齐方式)
(step7. 若软件触发ADC,则需要调用库函数软件触发ADC采样。 )
step1.配置DMA(外设寄存器地址、数据长度、是否递增;存储器地址、数据长度、是否递增;传输方向;DMA传输模式选择-单次/循环;传输数据数目;优先级;FIFO模式...)
step4.ADC配置(通用配置,转换模式配置-扫描连续模式,ADC通道转换顺序和采样时间配置)
step2. 配置USART(波特率、字长、停止位、校验位、USART模式、硬件流控制)
GPIO
GPIO输出
#include "led.h"
#include "Delay.h"
void led_init(void)
{
// enable rcc
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
// init GPIOF9,10
GPIO_InitTypeDef *GPIO_InitStruct;
GPIO_InitStruct->GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct->GPIO_Pin = led0 | led1;
GPIO_InitStruct->GPIO_Speed = GPIO_Medium_Speed;
GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, GPIO_InitStruct);
}
void led_on(void)
{
GPIO_WriteBit(GPIOF, led0, Bit_RESET);
GPIO_WriteBit(GPIOF, led1, Bit_RESET);
}
void led_off(void)
{
GPIO_WriteBit(GPIOF, led0, Bit_SET);
GPIO_WriteBit(GPIOF, led1, Bit_SET);
}
void led_shining(void)
{
led_on();
Delay_ms(1000);
led_off();
Delay_ms(1000);
}
void led_turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOF, led0) == 0){
led_off();
}
else{
led_on();
}
}
GPIO输入
#include "led.h"
#include "Delay.h"
void led_init(void)
{
// enable rcc
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
// init GPIOF9,10
GPIO_InitTypeDef *GPIO_InitStruct;
GPIO_InitStruct->GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct->GPIO_Pin = led0 | led1;
GPIO_InitStruct->GPIO_Speed = GPIO_Medium_Speed;
GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, GPIO_InitStruct);
}
void led_on(void)
{
GPIO_WriteBit(GPIOF, led0, Bit_RESET);
GPIO_WriteBit(GPIOF, led1, Bit_RESET);
}
void led_off(void)
{
GPIO_WriteBit(GPIOF, led0, Bit_SET);
GPIO_WriteBit(GPIOF, led1, Bit_SET);
}
void led_shining(void)
{
led_on();
Delay_ms(1000);
led_off();
Delay_ms(1000);
}
void led_turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOF, led0) == 0){
led_off();
}
else{
led_on();
}
}
#include "stm32f4xx.h" // Device header
#include "led.h"
#include "key.h"
int main(void)
{
led_init();
key_init();
led_off();
while(1)
{
if(key_read() == 1){
led_turn();
}
}
}
EXIT外部中断
PS: STM32F103和STM32F407中,实现IO口与中断线映射的外设不一样。
stm32f103中是AFIO,stm32f407中是SYSCFG
涉及的外设:RCC、GPIO、AFIO(SYSCFG)、EXIT(不需要开启时钟)、NVIC(不需要开启时钟,在内核里,RCC只管内核外的外设)
stm32f407中断通道有23个
EXIT外设可产生中断或事件:
中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这样是
软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号
传输,属于硬件级的。(减小CPU资源占用)
step1.配置GPIO、
AFIO(stm32f103)
SYSCFG(stm32f407)
step2.配置EXIT
step3.配置NVIC
step4.编写中断函数
TIM定时器
基本定时器
通用定时器
可定时器级联增大定时时长
高级定时器
1. 定时中断
step1.开启时钟
step2.选择时基单元时钟源(这里定时中断选择内部时钟源)
step3.配置时基单元(预分频器、自动重装器、计数模式)
step4.配置输出中断控制(使能更新中断,输出至NVIC)
step5.配置NVIC(在NVIC中打开TIM中断通道,分配优先级)
step6.使能定时器
2. 输出比较
输出PWM
输出PWM波主要用到TIM的两个模块:时基单元(配置计数器 )、输出比较单元(配置PWM输出模式)
主要原理:通过比较CNT与CCR的值,实现高低电平翻转。通过配置ARR与CCR的值,实现输出不同频率的PWM波。
step0. RCC开启时钟(TIM、GPIO)
step1.配置时基单元(时钟源选择、预分频器PSC、计数器CNT计数模式、自动重载器ARR)
step2. 配置输出比较单元(捕获比较器CCR、输出比较模式、极性选择、输出使能)
step3. 配置GPIO(复用推免输出模式->输出PWM波)
PS:stm32f407中要通过GPIO_PinAFConfig显式得将引脚绑定为TIM输出通道
step4.运行控制,使能TIM
3.输入捕获
测频率 :输入信号上升沿触发捕获单元的通道1从模式自动清零CNT)
step0. 开启时钟RCC(GPIO、TIM)
step1. 配置GPIO(上拉/浮空输入模式,GPIO复用为TIM2输入)
step2.配置时基单元(内部时钟源驱动下自增计数)
step3.配置输入捕获单元(输入滤波器、边沿极性选择、直连通道/交叉通道、分频器)
step4.从模式触发源(上升沿信号TI1FP1),从模式触发操作(reset--CNT清零)
step5.使能TIM。
测占空比:输入信号TI1FP1上升沿触发捕获单元的通道1(从模式自动清零CNT);输入信号TI1FP2上升沿触发捕获单元的通道2
ADC
输入通道
转换模式(单次/连续转换,非扫描/扫描模式)
触发控制
需要通过AFIO选择触发信号源是:外部事件(GPIO外部信号/定时器 ) or 软件控制(由ADC控制寄存器CR2某位控制)。
PS:通过TIM的TRGO事件外部触发ADC转换(当然外部事件触发ADC采样的话,需要设置为非连续模式,等待外部信号触发),再结合DMA对ADC数据寄存器进行数据搬运,就可以实现对输入模拟电压采样啦。
数据对齐
ADC是12位的,但是数据寄存器是16位,故存在数据对齐问题;
转换时间
校准
(stm32f4内部自动校准)
初始化步骤:
step0. 配置RCC(GPIO, ADC)
step1.配置GPIO(模拟输入)
step2. 配置ADC通用寄存器(ADC模式,CCLK分频器-stm32f4的ADC时钟不超过36Mhz)
APB2:84MHz, CCLK:4分频
84 / 4 = 21MHz < 36MHz
step3.配置模拟多路开关(“点菜”:选择输入通道,配置通道转换顺序,采样周期)
有几个输入,就用几个通道,即调用几次该函数。
step4.配置ADC转换(单次/连续,扫描/非扫描,通道数,触发控制-外部事件触发/软件触发,数据对齐方式)
(step5.模拟看门狗+中断配置)
step6.使能ADC
(step7. 若软件触发ADC,则需要调用库函数软件触发ADC采样。 )
DMA
支持字节(8bit)、半字(16bit)、字(32bit)访问。
M2M(memory to memory) (stm32f407上M2M合并在传输方向里定义)
---1:软件触发,常用于存储器至存储器的转运;
---0:硬件触发,常用于外设至存储器的数据搬运(ADC,串口..)
自动重装器:使能循环模式,控制是否连续搬运,即传输计数器自减为0时,则重装,继续下一轮搬运。
PS:M2M下不能使能自动重装器,or DMA停不下来
传输计数器:搬运的数据数目
开关控制:使能DMA,进入准备搬运状态 ,等待触发
存储器A->存储器B 数据转运
step0.RCC开启DMA时钟
step1.配置DMA(外设寄存器地址、数据长度、是否递增;存储器地址、数据长度、是否递增;传输方向;DMA传输模式选择-单次/循环;传输数据数目;优先级;FIFO模式...)
(step2.使能前,清除DMA数据流传输完成标志位)
step3. 使能DMA数据流,开始数据传输
外设寄存器->存储器 数据转运
ADC扫描循环,DMA自动重装 (ADC每个通道完成转换后,硬件触发DMA事件,实现ADC数据寄存器16bit 至 存储器32bit的数据搬运,避免ADC数据寄存器的数据覆盖)
这里DMA的传输计数器置7,自动重装器使能。
step0.RCC配置(GPIO、ADC、DAM)
step1.GPIO配置(模拟输入)
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
step2.DAM配置( 循环模式,P2M方向)
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
step3.使能DMA
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);
step4.ADC配置(通用配置,转换模式配置-扫描连续模式,ADC通道转换顺序和采样时间配置)
PS:软件触发ADC->连续模式;若外部信号上升沿触发->非连续模式。
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
step5. 使能ADC 的DMA请求
/* Regular Channels DMA Configuration functions *******************************/
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_DMARequestAfterLastTransferCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
step6.使能ADC
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
step7.软件触发,开启ADC转换
void ADC_SoftwareStartConv(ADC_TypeDef* ADCx);
ps:这里还可以通过TIM定时触发ADC每次转换,ADC单通道转换后再触发DAM数据搬运
TIM2的TRGO更新事件 触发 ADC采样(非扫描、非连续) 每次转换完成 触发 DMA搬运数据(连续模式),实现以6.4khz采样频率,采样2048个点。
STM32 TIMER_TRGO触发+ADC采集 + DMA传输 + 中断均方根处理 实现三相电压显示_stm32定时器触发adc采样程序-CSDN博客
USART
异步 全双工 单端 串行通信
物理层:"RS-232"电平标准,“TTL”标准
协议层:数据包由【起始位、数据部分、校验位、停止位】构成,且通讯双方需规定相同的传输速率(波特率,注:2进制码元,波特率与比特率数值相同)
串口时序(小端模式:低位先行)
usart外设
波特率发生器
初始化步骤
step0.RCC配置(GPIO、USART)
step1.GPIO复用配置(Tx复用输出,Rx复用输入)
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
step2. 配置USART(波特率、字长、停止位、校验位、USART模式、硬件流控制)
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
step3.ITconfig配置接收中断
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
step4.配置NVIC
step5.使能USART
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
step6. 数据收发
(接收数据则调用接收函数,发送则调用发送函数,查看串口状态则调用获取标志位函数)
/* Data transfers functions ***************************************************/
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
多字节数据包接收
串行收发时,单字节的数据包的起始位和停止位的添加与解析,是由USART外设的硬件电路自动完成的。但在实际应用中,我们往往需要发送多字节的数据包,如何在接收的时候判断数据帧的起始终止呢?这就需要发送端与接收端定义通信协议:指定数据帧的帧头、帧尾(or添加表示数据长度的信息帧),这样接收方就可以从数据帧中解析出有效数据。
PS:usart协议是给每次发送的一个字节数据(8 bit or 9 bit,区别是否有校验位)添加起始位与停止位,这里定义的通信协议是给发送的数据帧(多字节)添加帧头、帧尾(长度自定义)。
(发送) 给数据帧添加帧头、帧尾
(接收)状态机模式分析
IIC
同步 单双工 单端 串行通信
异步通信对时序要求严格,如usart通信则依赖其硬件电路,难以用软件模拟;且由于异步没有时钟线,若发送端串行发送过程中,发生其他事件中断占用CPU资源,接收方不知道,导致接收错误(接收超时)。
异步:时序要求严格,依赖硬件电路
同步:多一根时钟线,可软件模拟
硬件电路
为什么要上拉电阻并设置为开漏输出?
-- GPIO输出模式有2种:推挽和开漏。
推挽内接VCC电源,IIC多从机模式下,总线上会连接许多设备,若一个设备输出低电平(内部接地),一个设备输出高电平(内部接VCC),则会发生短路。
而开漏模式内部通过一个三极管开关接地,输出低电平时,开关导通,输出高电平时,开关断开,故需要外接电源,提供驱动。为了避免短路,又提供外部驱动,故需要上拉电阻。(但这也一定程度地限制了IIC的速度,低电平拉至高电平速度慢)
时序基本单元
SCL由主机控制,从机仅在发送数据、应答时,主机释放SDA的控制,从机控制SDA.
起始、终止条件
主机发送一个字节
主机接收一个字节
应答位时基单元
完整时序图
只有在起始和终止处,SDA状态会在SCL高电平时发送变换。 其余时候,无论主机or从机均在SCL低电平时发送数据,高电平时读取数。(高位先行)
主机写时序
- 【s-slave address-W】起始信号S产生后,所有从机就开始等待主机紧接下来广播的从机地址信号。(I2C 总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。根据I2C 协议,从机地址可以是7 位或10 位)在地址位之后,是传输方向的选择位(0 :主机向从机写数据,1:主机从从机读数据,主机释放SDA)
- 【A】广播完地址,从机接收到匹配的地址后,从机返回一个应答(ACK) 或非应答(NACK) 信号。
- 【DATA-A/NA】主机接收到应答信号后,主机开始正式向从机传输一个字节数据(DATA),每传输一个字节数据,等待从机返回应答信号ACK。(重复此过程,以实现N字节数据的传输)
- 【P】 当数据传输结束时,主机向从机发送一个停止传输信号(P),表示不再传输数据。
主机读时序
- 【s-slave address-R】起始信号后,主机在总线上广播从机地址,指明主机写数据
- 【A】对应地址从机应答
- 【DATA】从机向SCK数据线上,传输1字节数据DATA,等待主机应答
- 【A/NA】主机应答(A:收到数据,NA:希望从机停止发送数据)
- 【P】主机发送停止信号
复合格式时序
I2C 通讯更常用的是复合格式,以实现在从机的指定地址下进行读写。
该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过SLAVE_ADDRESS 寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
IIC外设
主机发送流程:
写控制寄存器CR、数据寄存器DR,触发发送,检查状态寄存器SR(事件event是否发生,即标志位是否置1)
主机接收流程:
初始化步骤
step0.RCC配置(GPIO、IIC)
step1,复用GPIO(开漏输出)
step2.配置IIC
step3.使能IIC
发送接收的流程,读取事件标志位什么的看不下去啊....
SPI
同步 全双工 单端 串行通信
硬件电路
PS:当从机没有被选择时(即SS引脚为高电平时),从机的MISO由推免输出转换为高阻态输出,防止MISO总线上从机输出冲突。
移位示意图(模式1)
以字节为单位,主机的时钟线控制主机和从机的移位寄存器 。
SCK上升沿时,主机的移位寄存器移出高位至MOSI总线,从机的移位寄存器移出高位至MISO;
SCK下降沿时,MOSI总线上的bit移至从机移位寄存器低位,MISO总线上的bit移至主机的移位寄存器低位。
重复8个CLK,主机和从机完成一个字节的数据交换。
PS:主机只写,从机只读,则忽略MISO总线数据,这时MISO实际为高阻态0xFF(SPI的资源浪费)
SPI时基单元
起始终止
字节交换(常用模式0)
由时钟极性CPOL与时钟相位标志位CPHA控制,共4种模式。
CPOL:控制空闲时刻,时钟线状态(0-低电平,1-高电平)
CPHA:控制采样时刻,在SCK的第(1 or 2)个边沿采样(0-第一个边沿,1->第二个边沿)
模式0比模式1快一点