历史上的今天
返回首页

历史上的今天

今天是:2025年08月21日(星期四)

正在发生

2020年08月21日 | STM32F4学习笔记(基础介绍篇)

2020-08-21 来源:eefocus

这里我们学习的开发板芯片具体型号是STM32F407ZGT6,采用工作频率为168 MHz的Cortex™-M4内核,性能较强。
本篇包含的内容:

  1. 固件库简介

  2. 开发环境的简介

  3. 开发板的基础知识

MDK5(提取1234)进行软件的开发:
软件的组成结构如下:
在这里插入图片描述
一般来说,第一次打开软件表观认识最多的应该是IDE Editer也就是代码编辑界面,其次如果首次使用某款芯片,会提示安装相关支持库,会使用到包安装器,对于软件其他部分可以在使用中慢慢感受(只需要明白MDK5不是简单的代码编辑器而已,是一个软件集合)。


1、工程模板的建立:
工程模板的建立是为了代码组织的清晰以及软件维护的方便。
我们先看下ST官方库给出的工程模板包含的文件


  - Template/system_stm32f4xx.c   STM32F4xx 系统时钟配置文件

  - Template/stm32f4xx_conf.h     固件库配置文件

  - Template/stm32f4xx_it.c       中断处理文件

  - Template/stm32f4xx_it.h       中断处理头文件

  - Template/main.c               Main program

  - Template/main.h               Main program header file

我们观察下对应工程模板在MDK5上的头文件路径设置:
在这里插入图片描述
第一步就是要搞明白,一个工程包含使用了哪些文件,我们对这些文件分类管理,创建出属于自己的工程模板。
通过对比正点原子、st官方模板、cubeMx创建的模板,可以知道,完整的工程需要:


1、core_cm4.h //内核功能的定义,比如NVIC相关寄存器的结构体和Systick配置;

2、core_cm4_simd.h //包含与编译器相关的处理

3、core_cmFunc.h //内核核心功能接口头文件

4、core_cmInstr.h //包含一些内核核心专用指令


5、startup_stm32f40_41xxx.s //devices vector table for MDK-ARM toolchain

6、stm32f4xx.h //contains all the peripheral register's definitions, bits definitions 

//and memory mapping for STM32F4xx devices

7、system_stm32f4xx.c //contains the system clock configuration for STM32F4xx devices,

//关键函数SystemInit(),系统启动文件startup_stm32f40_41xxx.s里面调用

8、stm32f4xx_conf.h //里面包含了我们需要使用的标准库,对于需要使用的库需要在其中加以说明include,

//stm32f4xx.h里面可以看到对其include

9、stm32f4xx_it.c //provides template for all exceptions handler and 

          // peripherals interrupt service routine.

10、main.c //主程序

11、led.c //对于关键使用硬件的驱动配置文件

对此我们对上面的文件分类存放管理:
在这里插入图片描述
我们创建这样一个目录结构后,导入相关文件,使用MDK5以该文件夹创建工程,然后将相关头文件路径进行设置好,一个工程模板就完成了(自己最好找到一个工程模板,将上述文件找到,并理清各个文件间的调用关系)。


2、程序的下载调试:
程序的下载方式:USB、串口(通常使用USB转成串口)、JTAG、SWD等。

  • 使用串口下载时,首先需要电脑安装相关的驱动(CH340驱动),然后使用flymcu选择相关hex文件下载到开发板中。

  • 使用JLINK下载:串口下载较为缓慢(每次都要全片擦除)且不能使用调试工具实时跟踪调试,使用JLINK、ULINK、STLINK等可以快速下载并实现程序跟踪调试。使用JLINK下载调试时需要注意设置为SW调试模式(JTAG模式会占用较多I/O口使得部分外设无法使用),相关驱动的安装不做详述。

程序的调试:
(注意:开发板上B0与B1要设置到GND以便代码下载后自动运行)
按下debug键进入仿真调试模式(Load键会直接下载执行而不进入调试模式)
在这里插入图片描述

这里
一般说来,有系统总线AHB和外设总线APB,在STM32F4中,总线矩阵实现8条主控总线和7条被控总线之间的互连并完成相互间访问的仲裁(可以当成交警,指挥疏导交通运输)。不同速率要求的外设会挂载在不同的总线上组成相互之间的通信网络。
在这里插入图片描述
2、时钟系统:
学习之前首先想一下为什么要时钟(看数电)?STM32时钟为什么那么多?时钟树是什么?
首先考虑到不同外设需要的时钟频率以及时钟精度并不一致,我们对于不同的外设使用不同的时钟可以有效降低能耗并提高系统稳定性。
时钟树就是对时钟源进行一系列的倍频和分频得到不同的频率时钟信号(如图一棵树,由根部向上分出枝干)。

2.1 系统时钟SYSCLK的初始化:
系统时钟的配置是在SystemInit()函数中完成的(system_stm32f4xx.c文件),配置完寄存器后该函数会调用SetSysClock函数。
SystemInit()调用是在启动文件(startup_stm32f40_41xxx.s文件)中设置的:系统启动后,进入main之前会调用SystemInit()对系统时钟进行初始化,比如对外部晶振的时钟进行分频倍频得到不同的时钟源初始值。


2.2 时钟的使能和配置:
系统初始化完成后可能会需要修改某些时钟的默认配置或者使能外设时钟以便使用相关外设,这便需要使用到 stm32f4xx_rcc.h 文件。


对于时钟的很多操作都是定义在 stm32f4xx_rcc.h 文件里,该文件的函数大致分类如下:

  • 时钟使能函数(外设使能与时钟源使能)

  • 外设复位函数(与上面的使能相对应,使用类似)

  • 时钟源选择和分频因子配置函数

下面对这几类函数做简单讲解:
+++ 首先我们从功耗角度考虑假定:任何时钟,你不设置它,默认为关闭。
+++ 然后明确基本知识:

  • 我们有5条总线AHB1、AHB2、AHB3、APB1、APB2,下面分别挂载了不同的外设(具有不同的时钟)。

  • 我们有5大类时钟源(形象点:5个时钟树相互交叉在一起)

在这里插入图片描述
做个不严谨的比喻:我们可以认为时钟源就是一个大的水源,外设时钟是一个个小水库,有的水库可以有多个水源的水补充(比如系统时钟),水库周围的人(外设)要用水,就要打开水库(使能外设时钟),想喝多少(分频因子设置函数),喝哪里的水(时钟源选择函数)都可以选择。


我们不可能一直开着水闸(浪费,所以设置一堆使能函数);
不同的人不同用途对水的要求不一样(所以可以选择水库的水的来源,给你一堆时钟源配置函数)。


现在你在回头看看这三个函数类别,是不是有点理解了?
挨着顺序举个例:


我们使用串口1,首先就要打开时钟:


RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);  //串口1挂载在APB2总线,函数使用要对应其挂载的总线。


然后再对串口1做相关的初始化参数配置就可以使用串口1了(不做展开)。

时钟复位函数用法一致,不做举例。

对于时钟源的开关,我们也有相关函数可以控制(5大类,6个函数):


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


时钟源的选择(配置):


void        RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);  //配置系统时钟的来源,可以查看相关函数定义了解细节


有的传入参数是分频,有的是时钟源,这样Config函数根据参数一般分为时钟源设置和分频设置或者兼具,具体可以查看函数定义,一般函数定义会有对参数有效性判断的函数,进入这个函数就可以清晰明白函数的真正用途(结合时钟树可以加深理解)。


void RCC_PCLK1Config(uint32_t RCC_HCLK)

{

  uint32_t tmpreg = 0;


  /* Check the parameters */

  assert_param(IS_RCC_PCLK(RCC_HCLK));   //查看这里的IS_RCC_PCL函数定义,如下

......  // 省略部分内容

}


#define RCC_HCLK_Div1                    ((uint32_t)0x00000000)

#define RCC_HCLK_Div2                    ((uint32_t)0x00001000)

#define RCC_HCLK_Div4                    ((uint32_t)0x00001400)

#define RCC_HCLK_Div8                    ((uint32_t)0x00001800)

#define RCC_HCLK_Div16                   ((uint32_t)0x00001C00)

#define IS_RCC_PCLK(PCLK) (((PCLK) == RCC_HCLK_Div1) || ((PCLK) == RCC_HCLK_Div2) ||

                           ((PCLK) == RCC_HCLK_Div4) || ((PCLK) == RCC_HCLK_Div8) ||

                           ((PCLK) == RCC_HCLK_Div16))

//看名字就可以知道这里是提高分频设置的


3、I/O引脚复用与映射:
一个芯片会有很多引脚,一块板子上面有很多外设,外设是需要与芯片引脚相连实现特定功能的,而我们又需要GPIO的存在(像我们点个灯啊,简单输入输出高低电平啥的),如果各自都单独使用,引脚会存在浪费情况(毕竟不是所有外设同时使用),所以一个引脚我们设置成可以复用为GPIO和多个内置外设的功能引脚。


具体一个引脚作为什么功能用,可以通过寄存器控制参数(这里就讲讲相关设置的函数就行了,具体可以自己查阅相关寄存器功能列表和函数实现)。


举个使用GPIOA.9以及GPIOA.10作为串口1的复用例子(串口需要收发就要两个引脚):


/*1、如同前面所说,使用一个外设就要首先使能时钟,这里我们使能GPIO时钟以及串口的外设时钟*/

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); 


/*2、初始化GPIOA的Pin9和Pin10引脚,配置为复用功能*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  //这里配置为复用功能,其他还有输入输出等选项

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOA的两个引脚


/*3、使用GPIO_PinAFConfig配置复用功能为串口1,具体是什么功能要看手册或者查源码(编辑器自动查看)*/

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);  //配置Pin9为TX端

GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);  //配置Pin10为RX端


这样我们就完成了GPIO的复用环节了,可能大家注意到我们对GPIOA做了初始化(步骤2),但没有对串口初始化,因为这里只是讲解初始化GPIOA作为复用功能,对于串口的使用(如何配置,后续再做讲解)。


4、NVIC中断优先级管理:
4.1 一些简单的补充:
中断: 计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。STM32将中断分为内核中断和外部中断,可以就简单区别为内核中断是最高有限级的,不能打断(比如复位,非屏蔽中断,硬件错误等),而外部中断是我们主要操作对象,如外设发出的中断请求(IQR)我们可以设置优先级,可以直接屏蔽等。


优先级: 分为抢占优先级和响应优先级。顾名思义,抢占的意思就是可以强制打断正在执行的中断响应(第一时间得到处理),而响应优先即就是简单的谁级别高谁就优先得到响应(排队排的快),但是不能打断正在执行的中断(别人已经再接收服务,自己来晚了你就不能让别人下来)。


中断向量表: 一个中断向量占据4字节空间,是指中断服务程序入口地址的偏移量与段基值。中断向量表占据一个特定存储空间,它的作用就是按照中断类型号从小到大的顺序存储对应的中断向量。在中断响应过程中,CPU通过从接口电路获取的中断类型号(中断向量号)计算对应中断向量在表中的位置,并从中断向量表中获取中断向量,将程序流程转向中断服务程序的入口地址。


4.2 言归正传:
STM32F40xx/STM32F41xx总共有92个中断,包含10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级。


Nested Vectored Interrupt Controller (NVIC)相关的寄存器定义在core_cm4.h文件内,可以看到里面很多位是保留的,M4内核提供的中断数量以及相关功能在STM32F40xx/STM32F41xx中只使用到一部分。我们对于中断的控制就是通过对下面这些寄存器操作实现的。


/** brief  Structure type to access the Nested Vectored Interrupt Controller (NVIC).

 */

typedef struct

{

  __IO uint32_t ISER[8];                 /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register           */

       uint32_t RESERVED0[24];

  __IO uint32_t ICER[8];                 /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register         */

       uint32_t RSERVED1[24];

  __IO uint32_t ISPR[8];                 /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register          */

       uint32_t RESERVED2[24];

  __IO uint32_t ICPR[8];                 /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register        */

       uint32_t RESERVED3[24];

  __IO uint32_t IABR[8];                 /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register           */

       uint32_t RESERVED4[56];

  __IO uint8_t  IP[240];                 /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */

       uint32_t RESERVED5[644];

  __O  uint32_t STIR;                    /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register     */

}  NVIC_Type;

推荐阅读

史海拾趣

FCI / Amphenol公司的发展小趣事

Amphenol公司成立于1932年,最初由Arthur J. Schmitt和Gordon K. Klapmeier共同创办,专注于电磁线缆的制造。然而,随着技术的进步和市场需求的变化,Amphenol公司逐渐将重心转向电缆连接器的制造。凭借高品质的产品和服务,Amphenol公司的电缆连接器在市场上获得了广泛的认可,并逐渐发展成为全球最大的电缆连接器制造商之一。

DL Instruments LLC公司的发展小趣事

随着产品的不断推出和市场的逐步认可,DL Instruments开始积极拓展其市场份额。公司不仅在美国本土市场取得了良好的业绩,还逐渐将产品推向国际市场。通过多年的努力,DL Instruments在电子行业中建立了良好的品牌形象,成为精密仪器制造领域的佼佼者。

Freqtech Ohg公司的发展小趣事

随着业务的不断增长,Freqtech Ohg公司开始实施全球化战略。公司首先在欧洲建立了研发中心,随后在亚洲市场设立了生产基地和销售网络。通过整合全球资源,Freqtech不仅降低了生产成本,还更快速地响应了不同地区客户的需求。同时,公司积极参与国际展会和技术交流,不断提升品牌知名度和影响力,为公司的全球化发展奠定了坚实基础。

Honda Tsushin Kogyo Co Ltd公司的发展小趣事

在电子行业快速发展的同时,Honda Tsushin Kogyo也面临着来自技术革新、市场竞争等方面的挑战。为了保持领先地位,公司可能积极应对这些挑战,通过转型升级来实现可持续发展。例如,公司可能加大了对新能源、物联网等新兴领域的投入,研发出符合市场需求的新产品。同时,公司还注重提升生产效率和降低成本,以应对激烈的市场竞争。

Eclipse Magnetics公司的发展小趣事

Honda Tsushin Kogyo Co. Ltd.的成立背景虽未详细公开,但考虑到其专注于电子连接器领域,可以推测该公司可能是在日本电子工业兴起的背景下成立的。在成立初期,公司可能面临了激烈的市场竞争和技术挑战。为了站稳脚跟,Honda Tsushin Kogyo致力于研发高质量的电子连接器产品,并逐步赢得了市场的认可。公司可能通过不断提升产品质量、优化生产工艺以及拓展销售渠道等方式,实现了初期的稳步发展。

FLEX LTD公司的发展小趣事

在电子行业快速发展的同时,Honda Tsushin Kogyo也面临着来自技术革新、市场竞争等方面的挑战。为了保持领先地位,公司可能积极应对这些挑战,通过转型升级来实现可持续发展。例如,公司可能加大了对新能源、物联网等新兴领域的投入,研发出符合市场需求的新产品。同时,公司还注重提升生产效率和降低成本,以应对激烈的市场竞争。

问答坊 | AI 解惑

单片机与TA8435的步进电机细分控制方法

1 步进电机 步进电动机是纯粹的数字控制电动机,它将电脉冲信号转变成角位移,即给一个脉冲,步进电机就转一个角度,因此非常适合单片机控制。在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响, ...…

查看全部问答>

SAW器件封装技术概述

1 引言 现今,随着整机尺寸的减小和元器件的高度集成,促进了元器件的小型化、薄型化。由于SAW(Surface Acoustic Wave)器件小型化并且有着出众的滤波功能,它们存许多领域的应用中扮演着非常重要的角包。小型化和多功能化是SAW器件发展的主要动力 ...…

查看全部问答>

二相与五相步进电机的差异

 概述:步进电机主要是依相数来做分类,而其中又以二相、五相步进电机为目前市场上所广泛采用。二相步进电机每转最细可分割为400等分,五相则可分割为 1000等分, 所以表现出来的特性以五相步进电机较佳、 加减速时间较短、 动态惯性较低。 二相/ ...…

查看全部问答>

ALTERA 的家庭网络产品方案

本帖最后由 jameswangsynnex 于 2015-3-3 20:02 编辑 针对消费娱乐的家庭网络连接 利用多媒体家庭联网技术,各种多媒体设备 ( 例如电视机、便携式媒体播放器、照相机和蜂窝电话等) 以及数据设备 (例如PC和PDA) 可以在安全的家庭网络环境中共享音 ...…

查看全部问答>

如何不让导航仪连接上电脑识别为U盘,而是识别为同步移动设备

目前我手上有一台导航仪,用的是wince5.0的系统,现在我想在这台导航仪上开发一个程序。 目前的问题是,我无法直接在该导航仪上调试我的程序,只能将程序编译好后再复制到导航仪上运行,这样调试起来非常不方便。 因为我的导航仪目前是只要通过US ...…

查看全部问答>

菜鸟请教各位高手!!!!!

软件工程中的嵌入式软件与系统是个怎么样的专业 它的就业前景如何啊??????????…

查看全部问答>

推荐一本用PowerPC开发通信设备的书,我还是第一次见同类型的书

嵌入式设计及通信设备开发详解——基于MPC82XX处理器 卓越的链接 http://www.amazon.cn/mn/detailApp?qid=1238487283&ref=SR&sr=13-1&uid=168-0888845-7367438&prodid=bkbk941360 当当的链接 http://product.dangdang.com/product.aspx?pro ...…

查看全部问答>

WinCE中如何禁止/开启Edit自动换行功能

如题,我想实现PC上记事本的自动换行功能。…

查看全部问答>

一个非常奇怪的keil c51串口中断问题!

一个非常奇怪的keil c51串口中断问题! 比如我的程序如下: uchar b[10]; #define a4 0x03 void fun1(void) interrupt 4 { fun2(2); } fun2(uchar a2) { while(a2--) { .... } fun3(a4); } void fun3(uchar a3)//这个函数无论有没 ...…

查看全部问答>

开关电源工程调试技术

《开关电源工程调试技术》可以看一下…

查看全部问答>