欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stm32f4开发笔记

程序员文章站 2022-06-11 15:36:24
...

初始化

GPIO配置

配置流程

通常为:
1.GPIOx口时钟使能
如:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
2.配置初始化结构体,见下
3.初始化GPIO口

GPIO_InitTypeDef结构体分析

源码:

typedef struct
{
  uint32_t GPIO_Pin;              /*!< Specifies the GPIO pins to be configured.
                                       This parameter can be any value of @ref GPIO_pins_define */

  GPIOMode_TypeDef GPIO_Mode;     /*!< Specifies the operating mode for the selected pins.
                                       This parameter can be a value of @ref GPIOMode_TypeDef */

  GPIOSpeed_TypeDef GPIO_Speed;   /*!< Specifies the speed for the selected pins.
                                       This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOOType_TypeDef GPIO_OType;   /*!< Specifies the operating output type for the selected pins.
                                       This parameter can be a value of @ref GPIOOType_TypeDef */

  GPIOPuPd_TypeDef GPIO_PuPd;     /*!< Specifies the operating Pull-up/Pull down for the selected pins.
                                       This parameter can be a value of @ref GPIOPuPd_TypeDef */
}GPIO_InitTypeDef;

这里可说的不多,基本上通过字面意思和查找相应的结构体定义都能理解,这里着重说一下GPIO_OType和GPIO_PuPd。

先看GPIO_OType:

typedef enum
{
  GPIO_OType_PP = 0x00,
  GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;

 OD,开漏输出就是不输出电压,低电平时接地,高电平时不接地。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机电压低的时候。
 PP,推挽输出就是单片机引脚可以直接输出高电平电压。低电平时接地,高电平时输出单片机电源电压。这种方式可以不接上拉电阻。但如果输出端可能会接地的话,这个时候输出高电平可能引发单片机运行不稳定,甚至可能烧坏引脚。

GPIO_PuPd:

typedef enum
{
  GPIO_PuPd_NOPULL = 0x00,
  GPIO_PuPd_UP     = 0x01,
  GPIO_PuPd_DOWN   = 0x02
}GPIOPuPd_TypeDef;

两点:1,输入信号有三种,低电平、高电平和不确定信号,上拉/下拉可以简单的理解为把不确定信号变为高/低电平信号。2,当外设电压高于板子电压时,可以使外设开漏输出,同时GPIO接上拉电阻,此时外设输出的高电平电压就会等于上拉电阻电源电压,就能正常读取高低电平信号。
Tips:矩阵键盘,4输入4输出,输入端通常为推挽输出,输入端通常接上拉电阻,毕竟电压一样。

pwm配置

输出频率的计算

以stm32f4为例:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    { /* 可能TIM1 有不同的默认值,但是结构体的默认值一定是0呀.. */
        TIM_TimeBaseInitTypeDef r1 = {
            .TIM_Prescaler = 99,
            // .TIM_CounterMode = TIM_CounterMode_Up,
            .TIM_Period = motor_pwm_arr // 1M/motor_pwm_arr
            // , .TIM_ClockDivision = TIM_CKD_DIV1
        };
        TIM_TimeBaseInit(TIM1, &r1);
    }

 这是配置pwm的部分代码,当然这还不足以让我完成频率的计算,至少,我们还需要知道APB2上的TIM1的输入频率。

 首先,通过查看system_stm32f4xx.c文件,我们可以看到不同型号板子的时钟配置,以stm32f411xx为例。

  *=============================================================================
  *                Supported STM32F411xx/STM32F410xx devices
  *-----------------------------------------------------------------------------
  *        System Clock source                    | PLL (HSI)
  *-----------------------------------------------------------------------------
  *        SYSCLK(Hz)                             | 100000000
  *-----------------------------------------------------------------------------
  *        HCLK(Hz)                               | 100000000
  *-----------------------------------------------------------------------------
  *        AHB Prescaler                          | 1
  *-----------------------------------------------------------------------------
  *        APB1 Prescaler                         | 2
  *-----------------------------------------------------------------------------
  *        APB2 Prescaler                         | 1
  *-----------------------------------------------------------------------------
  *        HSI Frequency(Hz)                      | 16000000
  *-----------------------------------------------------------------------------
  *        PLL_M                                  | 16
  *-----------------------------------------------------------------------------
  *        PLL_N                                  | 400
  *-----------------------------------------------------------------------------
  *        PLL_P                                  | 4
  *-----------------------------------------------------------------------------
  *        PLL_Q                                  | 7
  *-----------------------------------------------------------------------------
  *        PLLI2S_N                               | NA
  *-----------------------------------------------------------------------------
  *        PLLI2S_R                               | NA
  *-----------------------------------------------------------------------------
  *        I2S input clock                        | NA
  *-----------------------------------------------------------------------------
  *        VDD(V)                                 | 3.3
  *-----------------------------------------------------------------------------
  *        Main regulator output voltage          | Scale1 mode
  *-----------------------------------------------------------------------------
  *        Flash Latency(WS)                      | 3
  *-----------------------------------------------------------------------------
  *        Prefetch Buffer                        | ON
  *-----------------------------------------------------------------------------
  *        Instruction cache                      | ON
  *-----------------------------------------------------------------------------
  *        Data cache                             | ON
  *-----------------------------------------------------------------------------
  *        Require 48MHz for USB OTG FS,          | Disabled
  *        SDIO and RNG clock                     |
  *-----------------------------------------------------------------------------
  *=============================================================================

 若APBx的的预分频系数为1,则定时器的时钟输入频率等于APBx的频率,否则为时钟输入频率的两倍,,我们可以看到stm32f411xx的APB2预分频系数为1,故TIM1的时钟输入频率等于APB2的时钟频率。

 我们查看时钟树可以看到APBx是挂在AHB1上的。

stm32f4开发笔记

 通过查看stm32f411re数据手册的block diagram可以看到AHB1为100MHz,而APB2的预分频系数为1,故APB2的输出时钟频率为100MHz,由此得到TIM1的输入时钟频率为100MHz。
stm32f4开发笔记

 pwm输出频率计算公式如下:

pwm输出频率 = 定时器输入频率 / (预分频系数 + 1) / (定时器计数值 + 1)

 根据需要设置预分频系数(TIM_Prescaler)和定时器计数值(TIM_Period)即可,定时器计数值最大为0xFFFF,以上述代码为例,我们需要的pwm输出频率为400Hz,100MHz / 100 / 2500 正好400Hz。

占空比的计算

占空比的计算则较为简单,以TIM1的通道1为例,有

占空比 = TIM1->CCR1 / (定时器计数值 + 1)

中断相关

中断请求服务函数

中断标志位、状态、类型判断清除等

定时器中断状态判断函数 :

ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
参数1:定时器 参数2:中断类型


外部中断标志位判断函数 :

ITStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx)
参数1:定时器


定时器中断标志位清除函数:

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
参数1:定时器 参数2:中断标志位
注:两函数源码几乎是一样的,效果相同,可能是为了保持代码风格一致性


外部中断状态判断函数 :

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
参数1:外部中断线


外部中断标志位判断函数 :

ITStatus EXTI_GetFlagStatus(uint32_t EXTI_Line)
参数1:外部中断线
后者只判断标志位,前者既看标志位也看外部中断屏蔽寄存器EXT_IMR。


外部中断标志位清除函数:

void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
void EXTI_ClearFlag(uint32_t EXTI_Line)
参数1:外部中断线
注:两函数源码几乎是一样的,效果相同,可能是为了保持代码风格一致性

其他

特殊函数

assert

assert宏的原型定义在<assert.h>中,如果expression为false,则终止程序执行,原型:

#include <assert.h>
void assert( int expression );