前言
本文章主要记录了我学习STM32F103C8T6的过程,通过标准库进行开发,内容部分学习了江科大的视频,代码和注释基本上是自己手打,配图来自江科大中文参考手册。
笔记
STM32是STMicroelectronics(意法半导体)推出的一系列32位ARM Cortex-M微控制器,广泛应用于嵌入式系统开发。
关于该单片机的主要外设:
三种主要开发方式
1.使用标准库(Standard Peripheral Library): 这是ST官方提供的一种开发方式,它提供了一套标准的库函数,用于访问和控制STM32的外设和功能。 开发者可以直接调用这些库函数来实现对GPIO、ADC、USART等外设的控制。 这种方式的优点是简单易用,代码可读性好,但可能在性能上不如直接操作寄存器的方式。
2.使用硬件抽象层(Hardware Abstraction Layer, HAL)库: HAL库是ST在较新的STM32系列中推荐使用的开发方式,它提供了一个硬件抽象层,使得代码更加可移植。 HAL库提供了一组统一的API,可以用于不同的STM32系列,使得代码可以在不同的STM32芯片之间移植。 这种方式的优点是代码可移植性高,易于维护,但可能会因为抽象层的存在而牺牲一些性能。
3.直接操作寄存器(Register Level Programming): 这是最底层的开发方式,开发者需要直接编写代码来操作STM32的寄存器。 这种方式可以提供最大的灵活性和控制力,允许开发者充分利用STM32的性能。 优点是性能最优,可以精确控制硬件行为,但缺点是代码复杂,可读性差,且容易出错。
关于GPIO
GPIO(General Purpose Input Output)通用输入输出口
可配置为8种输入输出模式
引脚电平:0V~3.3V,部分引脚可容忍5V
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
在STM32中,所有的GPIO口都是挂载中PB2外设总线上的,STM32内部的寄存器都是32位的,但GPIOX端口只有16位,所以只有低16位对应有端口
GPIO8种模式
输出模式是指将GPIO对应端口置高低电平,输入模式是指单片机从对应端口读取高低电平
高阻态表示输出端不对外提供电压信号,既不输出高电平也不输出低电平,而是等效于断开状态,类似于电路中没有连接的情况
推挽输出:高低电平都有较强的驱动能力
开漏输出:只有低电平有驱动能力
输入:上下拉电阻阻值较大,所以是弱上拉和弱下拉,目的是尽量不影响正常的输入操作
GPIO位结构
如果选择对输出数据寄存器进行配置,就是普通的I/O口输出,因为输出数据寄存器同时控制16个端口,并且这个寄存器只能整体读写,如果想只控制某一个端口而不影响其他端口的话,需要一些特殊操作,第一种方式是先读出这个寄存器,然后用按位与和按位或的方式更改某一位;第二种方式是通过位设置/清除寄存器,如果要对某一位进行置1的操作就在位设置寄存器对应位上写1,如果要对某一位进行置0的操作就在位清楚寄存器对应位上写0.
STM32寄存器映射
#define ETH_MAC_BASE (ETH_BASE)
#define ETH_MMC_BASE (ETH_BASE + 0x0100)
#define ETH_PTP_BASE (ETH_BASE + 0x0700)
#define ETH_DMA_BASE (ETH_BASE + 0x1000)
#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000) /*!< FSMC Bank1 registers base address */
#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104) /*!< FSMC Bank1E registers base address */
#define FSMC_Bank2_R_BASE (FSMC_R_BASE + 0x0060) /*!< FSMC Bank2 registers base address */
#define FSMC_Bank3_R_BASE (FSMC_R_BASE + 0x0080) /*!< FSMC Bank3 registers base address */
#define FSMC_Bank4_R_BASE (FSMC_R_BASE + 0x00A0) /*!< FSMC Bank4 registers base address */
#define DBGMCU_BASE ((uint32_t)0xE0042000) /*!< Debug MCU registers base address */
/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
通过代码可以看出:挂载在同一总线上的外设,除了第一个外设地址是固定数值,其他外设地址都是通过 上一级基地址+偏移量
另外,标准库大量创建对应外设的新结构体变量名,通过宏定义将外设名映射为一个该结构体的变量(等同于通过宏定义创建了一个结构体变量),再通过宏定义将变量映射到对应外设的地址,
所以我们可以通过 GPIOA.CRL来对寄存器进行操作
代码实操
#include "stm32f10x.h" // Device header
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使用RCC开启GPIO的时钟
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);//使用GPIO_Init函数初始化GPIO
GPIO_SetBits(GPIOC,GPIO_Pin_13);//将PB13置1
GPIO_ResetBits(GPIOC,GPIO_Pin_13);//将PB13置0
while(1)
{
}
}
1.使用RCC开启GPIO的时钟(使用到什么外设就要打开对应外设的时钟,EXTI例外,NVIC是内核外设也不用手动打开时钟
2.使用GPIO_Init函数初始化GPIO
3.使用输出或者输入函数控制GPIO口
Stm32的端口复用与重映射
STM32上有很多I/O口和外设,在默认情况下这些I/O口作为普通的输入输出引脚,而将其配置为外设所用的I/O口,即为I/O口复用
可以通过配置GPIO_MODE将模式配置为复用推挽/开漏模式,即可通过GPIO口控制外设。