stm32个人学习随笔记录贴,如有错误,欢迎指正。
带cmd函数:使能函数
带Init的函数:初始化函数
带config的函数:设置函数
stm32最小系统组成
(1)电源电路
(2)复位电路
(3)晶振电路
(4)下载电路
stm32启动模式
启动模式选择引脚 | 启动模式 | 说明 | |
---|---|---|---|
BOOT0 | BOOT1 | ||
X | 0 | 主闪存存储器 | 主闪存存储器被选为启动区域 |
0 | 1 | 系统存储器 | 系统存储器被选为启动区域 |
1 | 1 | 内置SRAM | 内置SRAM被选为启动区域 |
一般情况下如果我们想用串口下载代码,则必须配置BOOTO为1, BOOT1为 0,而如果想让STM32一按复 位键就开始跑代码,则需要配置BOOTO为0,BOOT1随便设置都可以。
存储器映射:存储器本身不具有地址信息,地址由厂商或用户分配,给存储器分配地址的过程称为存储器映射。如果再分配一个地址就叫重映射。
寄存器映射:通过给特定功能的内存单元取别名,这个别名就是寄存器。给已经分配好地址的有特定功能的内存单元起别名的过程叫寄存器映射。
寄存器操作:定义采用宏定义,
寄存器模板文件创建:
0bj文件夹:用于存放编译产生的c/汇编/链接的列表清单、调试信息、hex文件、预览信息、封装库等文件。
User文件夹:用于存放用户编写的main. C、STM32F1启动文件、stm32f10x. h头文件。
GPIO ( general purpose intput output ):GPI0引脚与外部设备连接起来,从而实现与外部通讯控制以及数据采集的功能。
STM32引脚种类:
电源管脚、晶振管脚、复位管脚、下载管脚、BOOT管脚、GPIO管脚
2. GPI0工作模式
输入模式
输入浮空 、输入上拉、 输入下拉、 模拟输入输出模式
开漏输出 、开漏复用功能、 推挽式输出 、推挽式复用功能(1)推挽输出
A.可以输出高低电平,用于连接数字器件,高电平由VDD决定,低电平由VSS决定。
B.推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候另外一个截止,优点开关效率效率高,电流大,驱动能力强。
C.输出高电平时,电流输出到负载,叫灌电流,可以理解成推,输出低电平时,负载电流流向芯片,叫拉电流,即挽
(2)开漏输出
a.只能输出低电平,不能输出高电平。如果要输出高电平,则需要外接上拉。
b.开漏输出具有“线与”功能,一个 为低,全部为低,多用于I2C和SMBUS总线。
3. GPI0初始化步骤
具体寄存器参考STM32F1xx中文参考手册
(1)确定GPI0的工作模式和工作速度(通过配置寄存器GPIOx_ CRL/低八位和GPIOx_ CRH/高八位)
(2)具体要输出的内容(置位复位寄存器: BSRR和数据输出寄存器: ODR)
- STM32启动文件
对于启动文件这部分我们主要总结它的功能,不详细讲解
里面的代码,其功能如下:
初始化堆栈指针 SP;
初始化程序计数器指针PC;
设置堆、栈的大小;
设置中断向量表的入口地址;
配置外部SRAM作为数据存储器(这个由用户配置,一般的开发板可没有外部SRAM)
调用SystemInit() 函数配置STM32的系统时钟
设置C库的分支入口“__main”( 最终用来调用main函数)
- GPI0库函数介绍
(1) GPI0外设的库文件:
stm32f10x_gpio.c、
stm32f10x_gpio.h
(2) GPI0常用库函数
<1>初始化函数
void GPIO_Init(GPI0_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_ InitStruct)
功能:初始化一个或多个I0口(同- -组端口)的工作模式、输出速度即GPI0的2个配置寄存器。
初始化范例:
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPI0_InitStructure.GPIO_Pin=GPI0_Pin_0; //选择你要设置的I0口
GPIO_InitStructure.GPIO_Mode=GPI0_Mode_Out_PP://设置推挽输出模式
GPIO_InitStructure.GPI0_Speed=GPI0_Speed_ 50MHz; //设置传输速率
GPIO_Init(GPIOC,&GPIO_InitStructure) ;/*初始化GPI0 */
可以一次对多个管脚进行初始化,前提必须是它们的配置模式需-样。
比如:
GPIO InitStructure.GPIO_Pin=GPI0_Pin_0|GPIO_Pin_1;
<2>设置管脚输出电平函数
void GPIO_ SetBits (GPIO_ TypeDef* GPI0x, uint16_ t GPIO_ Pin) ;
功能:设置某个I0口为高电平(可同时设置同一端口的多个I0)。底层是通过配置BSRR寄存器。
void GPIO_ ResetBits (GPIO_ TypeDef* GPIOx,uint16_ t GPIO_ Pin) ;
功能:设置某个I0口为低电平(可同时设置同一端口的多个I0)。底层是通过配置BSRR寄存器。
void GPIO_ WriteBit (GPI0_ TypeDef* GPI0x, uint16_ t GPIO_ Pin,
BitAction BitVal) ;
void GPIO_ Write (GPIO_ TypeDef* GPI0x, uint16_ t PortVal) ;
功能:设置端口管脚输出电平,很少使用。
<3>读取管脚输入电平函数
uint8_t GPIO_ReadInputDataBit (GPIO_TypeDef* GPI0x, uint16_tGPI0_Pin);
功能:读取端口中的某个管脚输入电平。底层是通过读取IDR寄存器。
uint16_t GPIO_ReadInputData (GPIO_TypeDef* GPIOx) ;
功能:读取某组端口的输入电平。底层是通过读取IDR寄存器。
<4>读取管脚输出电平函数
uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPI0x, uint16_t GPI0_ Pin);
功能:读取端口中的某个管脚输出电平。底层是通过读取ODR寄存器。
uint16_t GPIO_ReadOutputData (GPIO_TypeDef* GPI0x) ;
功能:读取某组端口的输出电平。底层是通过读取ODR寄存器
(3)使能GPI0时钟函数
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,FunctionalState NewState);
不同的外设调用的时钟使能函数可能不一样
使能GPI0C端口时钟:
RCC_APB2Per iphClockCmd (RCC_APB2Periph_GPIOC, ENABLE) ;
时钟
HSI内部高速时钟:8M。
HSE外部高速时钟:8M。
LSE外部低速时钟:外接32.768k晶振。
PLL锁相环:倍频。72M
SYSCLK:72M。
2.时钟配置函数介绍
(1)时钟使能配置函数:
RCC HSICmd
RCC LSICmd
RCC PLLCmd
RCC RTCCLKCmd
RCC_ AHBPer i phClockCmd
RCC_ APBxPeri phClockCmd
SystemInit()后时钟频率大小:
SYSCLK (系统时钟)=72MHz
AHB总线时钟(HCLK=SYSCLK) =72MHz
APB1 总线时钟(PCLK1=SYSCLK/2) =36MHz
APB2 总线时钟(PCLK2=SYSCLK/1) =72MHz
PLL主时钟=72MHz
2.时钟配置函数介绍
(1)时钟使能配置函数:
RCC_HSICmd
RCC_LSICmd
RCC_PLLCmd
RCC_RTCCLKCmd
RCC_AHBPeriphClockCmd
RCC_APBxPeriphClockCmd
(2)时钟源和分频因子相关配置函数:
RCC_HSEConfig
RCC_LSEConfig
RCC_PLLConfig
RCC_MCOConfig
RCC_SYSCLKConfig
RCC_HCLKConfig
RCC_PCLK1Config
RCC_PCLK2Config
RCC_RTCCLKConfig
RCC_ADCCLKConfig
RCC_USBCLKConfig
(3) 外设复位函数:
RCC_APB1PeriphResetCmd
RCC_APB2PeriphResetCmd
(4)状态参数获取函数:
RCC_GetSYSCLKSource
RCC_GetClocksFreq
RCC_GetFlagStatus
RCC_ClearFlag
系统时钟设置步骤
void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时钟(可以修改时钟)
{
RCC_DeInit(); //将外设RCC寄存器重设为缺省值
RCC_HSEConfig(RCC_HSE_ON) ;//设置外部高速晶振(HSE)
if (RCC_WaitForHSEStartUp() ==SUCCESS) / /等待HSE起振
{
RCC_HCLKConfig (RCC_SYSCLK_Div1) ; //设置AHB时钟(HCLK)
RCC_PCLK1Config (RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1 )
RCC_PCLK2Config (RCC_HCLK_Div1) ;//设置高速AHB时钟(PCLK2)
RCC_PLLConfig (div,pllm); //设置PLL时钟源及倍频系数
RCC_PLLCmd (ENABLE); //使能或者失能PLL
while (RCC_GetFlagStatus (RCC_FLAG_PLLRDY)== RESET); //检查指定的RCC标志位设置与否,PLL就绪
RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK); //设置系统时钟(SYSCLK)
while (RCC_GetSYSCLKSource() !=0x08) ; //返回用作系统时钟的时钟源, 0x08: PLL作为系统时钟
}
}
位带操作:通过访问位带别名区来实现,即通过将每个比特位膨胀成一个32位字,当访问这些字的时候就达到了访问比特的目的。比方说BSRR寄存器有32个位,那么可以映射到32个地址上,当我们去访问这32个地址就达到访问32个比特的目的。
STM32位带及位带别名区域
支持位带操作的区域是SRAM区的最低1MB范围(APB1/2AHB外设)和片内外设区的最低1MB范围。
位带区与位带别名区地址转换
外设位带区与外设位带别名区的地址转换公式:
AliasAddr =( 0x42000000+ (A-0x40000000) 84 +n4
SRAM位带区与SRAM位带别名区的地址转换公式:
AliasAddr = 0x22000000+ (A-0x20000000) 84 +n4
A:表示我们要操作的那个位所在的寄存器的地址
n:位序号
理解要点:位带区的一个位在位带别名区会被膨胀成四个字节
根据上述两个公式特点,将其统一为一个公式表示:
((A & 0xF0000000) +0x02000000+ ( (A &0x0OOFFFF) <<5)+(n<<2))
位带操作的优点
(1)控制GPI0口输入输出非常简单。
(2)操作串行接口芯片非常方便。
(3)代码简洁,阅读方便。
4. GPI0位带操作
#define BITBAND (addr, bitnum) ((addr & 0x0000000) +0x2000000+ ( (addr&0xFFF) <<5)+ (bi tnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *) (addr))
#define BIT_ADDR (addr, bi tnum) MEM ADDR (BITBAND(addr, bi tnum) )
//I0口地址映射
#define GPIOA_ODR_Addr (GPI0A_ BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_ BASE+12) //0x40010COC
#define GPIOC_ODR_Addr (GPI0C_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPI0E_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPI0G_BASE+12) //0x40011EOC
#define PAout (n) BIT_ADDR(GPIOA_ODR_Addr, n) //输出
#define PAin(n) BIT_ADDR(GPI0A_IDR_Addr,n) //输入
#define PBout (n) BIT_ADDR(GPI0B_ODR_Addr,n) //输出
#define PBin (n) BIT_ADDR(GPI0B_IDR_Addr,n) //输入
#define PCout (n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin (n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout (n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin (n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout (n) BIT_ADDR(GPI0E_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPI0E_IDR_Addr,n) //输入
- SysTick定时器介绍
SysTick定时器也叫SysTick滴答定时器,它是Cortex -M3内核的一个外设,被嵌入在NVIC中。它是一个24位向下递减的定时器,每计数一次所需时间为1/SYSTICK,SYSTICK是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟8分频后获取。当定时器计数到0时,将从LOAD寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启SysTick中断的话,当定时器计数到0,将产生一个中断信号。因此只要知道计数的次数就可以准确得到它的延时时间。
(1) CTRL寄存器
CTRL是SysTi ck定时器的控制及状态寄存器。
(2) LOAD寄存器
LOAD是SysTick定时器的重装载数值寄存器。
(3) VAL寄存器
VAL是SysTick定时器的当前数值寄存器。
(4) CALIB寄存器
CALIB是SysTick定时器的校准数值寄存器。
SysTick定时器配置步骤
(1)设置SysTick定时器的时钟源。
(2)设置SysTick定时器的重装初始值(如果要使用中断的话,就将中断使能打开)
(3)清零SysTick定时器当前计数器的值。
(4)打开SysTick定时器。
SysTick_Init() 函数:
void SysTick_Init(u8 SYSCLK){
SysTick_CLKSourceConfig (SysTick_CLKSource_HCLK_Div8) ;
fac_us=SYSCLK/8;
fac_ms=(u16) fac_us*1000;
}
delay_ us() 函数:
void delay_us (u32 nus){
u32 temp;
SysTick->L0AD=nus*fac_us;//时间加载
SysTick->VAL=0x00;//清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do{
temp=SysTick->CTRL;
}while( (temp&0x01) &&! (temp&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
SysTick->VAL =0X00;//清空计数器
}
delay_ms ()函数:最大1.864s
void delay_ ms (u16 nms){
u32 temp;
SysTick->L0AD= (u32) nms*fac_ms;//时间加载(SysTick->L0AD为24bit)
SysTick->VAL =0x00;//清空计数器
SysTick->CTRL =SysTick_CTRL_ENABLE_Msk ;//开始倒数
do{
temp=SysTick->CTRL;
}while( (temp&0x01) &&! (temp&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
SysTick->VAL =0X00;//清空计数器
}