stm32f4开发笔记

初始化

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上的。

时钟树

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

 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 );

猜你喜欢

转载自blog.csdn.net/the_k_is_on_the_way/article/details/79815572