stm32学习笔记
STM32 命名规则
GPIO
头文件:stm32f10x_gpio.h
源文件:stm32f10x_gpio.c
重要函数:
初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
初始化一个或者多个IO口(同一组)的工作方式和速度。
1 | typedef struct |
GPIO_Init函数初始化样例:
1 | GPIO_InitTypeDef GPIO_InitStructure; |
2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。例:
1
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
例:1
GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平
2个读取输出电平函数:
uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。例如:
1
GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
例如:1
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平
4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为高电平(1)。实际操作BSRR寄存器void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为低电平(0)。实际操作的BRR寄存器。void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
这两个函数不常用,也是用来设置IO口输出电平。
1 | //按键输入: |
RCC
头文件: stm32f10x_rcc.h
源文件:stm32f10x_rcc.c
时钟使能配置:
RCC_LSEConfig()
、RCC_HSEConfig()
、RCC_HSICmd()
、RCC_LSICmd()
、RCC_PLLCmd()
……时钟源相关配置:
RCC_PLLConfig ()
、RCC_SYSCLKConfig()
、RCC_RTCCLKConfig()
…分频系数选择配置:
RCC_HCLKConfig()
、RCC_PCLK1Config()
、RCC_PCLK2Config()
…外设时钟使能:
RCC_APB1PeriphClockCmd():
//APB1线上外设时钟使能
RCC_APB2PeriphClockCmd();
//APB2线上外设时钟使能
RCC_AHBPeriphClockCmd();
//AHB线上外设时钟使能其他外设时钟配置:
RCC_ADCCLKConfig ();
RCC_RTCCLKConfig();
状态参数获取参数:
RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus()
RCC中断相关函数 :
RCC_ITConfig()
、RCC_GetITStatus()
、RCC_ClearITPendingBit()
…
NVIC中断优先级分组
中断管理方法:
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
中断优先级分组函数:
1
2
3
4
5
6
7
8
9void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
中断参数初始化函数:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
1
2
3
4
5
6
7typedef struct
{
uint8_t NVIC_IRQChannel; //设置中断通道
uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级
uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级
FunctionalState NVIC_IRQChannelCmd; //使能/使能
} NVIC_InitTypeDef;例:
1
2
3
4
5
6
7NVIC_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寄存器
中断优先级设置步骤:
- 系统运行后先设置中断优先级分组。调用函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
(整个系统执行过程中,只设置一次中断分组。) - 针对每个中断,设置对应的抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); - 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
- 系统运行后先设置中断优先级分组。调用函数:
串口
1 | void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能 |
波特率计算方法
串口配置的一般步骤
- 串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
- 串口复位:USART_DeInit(); 这一步不是必须的
- GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
- 串口参数初始化:USART_Init();
- 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init(); USART_ITConfig();
- 使能串口:USART_Cmd();
- 编写中断处理函数:USARTx_IRQHandler();
- 串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据 - 串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef USARTx, uint16_t USART_IT);
外部中断
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//设置IO口与中断线的映射关系例:
1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等1
2
3
4
5
6
7typedef struct
{
uint32_t EXTI_Line; //指定要配置的中断线
EXTIMode_TypeDef EXTI_Mode; //模式:事件 OR中断
EXTITrigger_TypeDef EXTI_Trigger;//触发方式:上升沿/下降沿/双沿触发
FunctionalState EXTI_LineCmd; //使能 OR失能
}EXTI_InitTypeDef;1
2
3
4
5EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位外部中断的一般配置步骤:
- 初始化IO口为输入。
GPIO_Init();
- 开启IO口复用时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
- 设置IO口与中断线的映射关系。
void GPIO_EXTILineConfig();
- 初始化线上中断,设置触发条件等。
EXTI_Init();
- 配置中断分组(NVIC),并使能中断。
NVIC_Init();
- 编写中断服务函数。
EXTIx_IRQHandler();
- 清除中断标志位
EXTI_ClearITPendingBit();
- 初始化IO口为输入。
通用定时器
定时器参数初始化:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
1
2
3
4
5
6
7
8typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;1
2TIM_TimeBaseStructure.TIM_Period = 4999; TIM_TimeBaseStructure.TIM_Prescaler =7199; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);定时器使能函数:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
定时器中断使能函数:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
状态标志位获取和清除:
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
定时器中断实现步骤
- 能定时器时钟。
RCC_APB1PeriphClockCmd();
- 初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
- 开启定时器中断,配置NVIC。
void TIM_ITConfig(); NVIC_Init();
- 使能定时器。
TIM_Cmd();
- 编写中断服务函数。
TIMx_IRQHandler();
- 能定时器时钟。
PWM输出
初始化:
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
1
2
3
4
5
6
7
8
9
10
11typedef struct
{
uint16_t TIM_OCMode; //PWM模式1或者模式2
uint16_t TIM_OutputState; //输出使能 OR失能
uint16_t TIM_OutputNState;
uint16_t TIM_Pulse; //比较值,写CCRx
uint16_t TIM_OCPolarity; //比较输出极性
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;1
2
3
4
5TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure. TIM_Pulse=100;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
设置比较值函数:
void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Compare2);
使能输出比较预装载:
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
使能自动重装载的预装载寄存器允许位:
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
PWM输出配置步骤:
- 使能定时器3和相关IO口时钟。
使能定时器3时钟:RCC_APB1PeriphClockCmd(); 使能GPIOB时钟:RCC_APB2PeriphClockCmd();
- 初始化IO口为复用功能输出。函数:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,
所以需要开启AFIO时钟。同时设置重映射。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
- 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
- 初始化输出比较参数:TIM_OC2Init()
- 使能预装载寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
- 使能定时器。TIM_Cmd();
- 不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();
- 使能定时器3和相关IO口时钟。
输入捕获
初始化函数:
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
1
2
3
4
5
6
7
8typedef struct
{
uint16_t TIM_Channel; //捕获通道1-4
uint16_t TIM_ICPolarity; //捕获极性
uint16_t TIM_ICSelection; //映射关系
uint16_t TIM_ICPrescaler; //分频系数
uint16_t TIM_ICFilter; //滤波器
} TIM_ICInitTypeDef;1
2
3
4
5TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM5_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM5, &TIM5_ICInitStructure);通道极性设置独立函数:
void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
获取通道捕获值
uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx);
输入捕获的一般配置步骤:
- 初始化定时器和通道对应IO的时钟。
- 初始化IO口,模式为输入:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
- 初始化定时器ARR,PSC
TIM_TimeBaseInit(); - 初始化输入捕获通道
TIM_ICInit(); - 如果要开启捕获中断,
TIM_ITConfig(); NVIC_Init();
- 使能定时器:TIM_Cmd();
- 编写中断服务函数:TIMx_IRQHandler();
DMA
DMA,全称Direct Memory Access,即直接存储器访问。
DMA的出现就是为了解决批量数据的输入/输出问题。DMA是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。这样数据的传送速度就取决于存储器和外设的工作速度。
初始化函数:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
2个使能函数:
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
//使能DMA通道void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
//使能DMA通道中断
2个传输数据量函数
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//设置DMA通道的传输数据量(DMA处于关闭状态)uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
//获取当前DMA通道传输剩余数据量(DMA处于开启状态)
DMA的一般步骤
- 使能DMA时钟。调用函数:
RCC_AHBPeriphClockCmd()
; - 初始化DMA通道参数。调用函数:
DMA_Init()
; - 使能串口DMA发送,串口DMA使能函数。调用函数:
USART_DMACmd()
; - 使能DMA1通道,启动传输。调用函数:
DMA_Cmd()
; - 查询DMA传输状态。调用函数:
DMA_GetFlagStatus()
; - 获取/设置通道当前剩余数据量。调用函数:
DMA_GetCurrDataCounter()
;DMA_SetCurrDataCounter()
。
- 使能DMA时钟。调用函数: