STM32固件库学习方法

更新时间:2024-03-26 23:19:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

STM32学习教程

教程一

1、一共24个库,不可能都学,都学也没用。主要学习来源是各种例程代码,固件库函数用户手册和参考手册。

具体学习方法是通读不同来源的程序,在程序中找到相关的函数库的应用,然后再阅读相关文档,有条件的实验。对于内容的选择方面,根据入门内容和未来应用,将所涉及的范围精简到最低,但是对选择的部分的学习则力求明确。,以下是按照自己的需求对程序库函数排列的学习顺序:

A、 绝大部分程序都要涉及到的库——flash、lib、nvic、rcc,学习基础的跟简单应用相关比

用的部分,其它部分后期再返回头学。

B、各种通用但不必用的库——exit、MDA、systic,通读理解其作用。 C、DEMO板拥有的外设库——gpio、usart,编写代码实验。

D、未来需要用到的外设库——tim、tim1、adc、i2c,spi,先理解待有条件后实验。 E、开发可靠性相关库——bkp、iwdg、wwdg、pwr F、其它,根据兴趣来学。

2、阅读flash

芯片内部存储器flash操作函数:对芯片内部flash进行操作的函数,包括读取,状态,擦除,写入等等,可以允许程序去操作flash上的数据。

基础应用1

Flash时序延迟几个周期,等待总线同步操作。推荐按照单片机系统运行频率,0-24MHz时,取Latency=0;24-48MHz时,取Latency=1;48~72MHz时,取Latency=2所有程序中必须的.用法:FLASH_SetLatency(FALSH_latency_2);

位置:RCC初始化子函数里面,时钟起振之后。

基础引用2

开启FLASH预读缓冲功能,加速FLASH的读取。所有程序中必须的。 用法:FALSH_PrefetchBufferCmd(FALSH_PrefetchBuffer_Enable); 位置:RCC初始化子函数里面,时钟起振之后。

3、阅读lib

调试所有外设初始化的函数。

只需要知道外设在调试的时候,EWRAM需要从这个函数里面获得调试所需信息的地址或指针之类的信息。

基础应用1

只有一个函数debug。所有程序中必须的。 用法: #ifdef DEBUG debug();

#endif

位置:main函数开头,申明变量之后。

4、阅读nvic,系统中断管理

管理系统内部的中断,负责打开和关闭中断。

基础应用1,中断的初始化函数

包括设置中断向量表位置,开启所需的中断两部分。所有程序中必须的。

用法:如果定义了VECT_TAB_RAM,则在RAM中调试,没定义,在Flash中调试。 void NVIC_Configuration(void) {

NVIC_InitTypeDef NVIC_InitStructure; //中断管理恢复默认参数 #ifdef VECT_TAB_RAM

NVIC_SetVectorTable(NVIC_VecTab_RAM,0x0); #else

NVIC_SetVectorTable(NVIC_VecTab_FLASH,0x0); #endif

//下面为中断的开启过程,不是所有程序必须的 //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC优先级分组方式

//一共有16个优先级,分为抢占式和响应式。两种优先级所占有的数量由此代码确定。//NVIC_PriorityGroup_x可以是0、1、2、3、4,分别代表抢占优先级有1、2、4、8、16个//和响应优先级有16、8、4、2、1个。规定两种优先级的数量后,所有的中断级别必须在//其中选择,抢占级别高的会打断其它中断优先执行,而响应级别高的会在其它中断执行完//优先执行。

//NVIC_InitStructure.NVIC_IRQChannel=中断通道名;

//NVIC_InitStructure.NVIC_IRQChannelPreemptionPriorit=0; //抢占优先级 //NVIC_InitStructure.NVIC_IRQChannelSubPriorit=0; //响应优先级

//NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //启动此通道的中断 //NVIC_Init(&NVIC_InitStructure); //中断初始化 }

5、阅读rcc:时钟管理

管理外部、内部和外设的时钟,设置、打开和关闭这些时钟。

基础应用1:

时钟的初始化函数过程: 用法:

void RCC_Configuration(void) {

ErrorStatus HSEStartUpStatus; //等待时钟的稳定

RCC_DeInit(); //将时钟重置为缺省值 RCC_HSEConfig(RCC_HSE_ON); //设置外部晶振 HSEStartUpStatus=RCC_WaitForHSEStartUp(); //等待外部晶振就绪 If(HSEStartUpStatus==SUCCESS) {

FALSH_PrefetchBufferCmd(FALSH_PrefetchBuffer_Enable); //使能预取缓存 FLASH_SetLatency(FLASH_Latency_2); //FALSH操作的延时 RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB使用系统时钟 RCC_PCLK2Config(RCC_HCLK_Div2); //APB2为HCLK的一半 RCC_PCLK1Config(RCC_HCLK_Div2); //APB1为HCLK的一半

RCC_PLLConfig(RCC_PLLSAource_HSE_Div1,RCC_PLLMul_9);//PLLCLK=8MHz*9=72MHz RCC_PLLCmd(ENABLE); //启动pll while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET)

{} //等待PLL启动

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //将PLL设置为系统时钟源 While(RCC_SYSCLKSource()!=0x80)

{} //将PLL返回,并作为系统时钟源

}

//RCC_AHBPeriphClockCmd(AHB2设备1|AHB2设备2,ENABLE); //启动AHP设备 //RCC_APB2PeriphClockCmd(ABP2设备1|ABP2设备2,ENABLE); //启动abp2设备 //RCC_APB1PeriphClockCmd(ABP1设备1|ABP1设备2,ENABLE); //启动abp1设备 }

6阅读exti:外部设备中断函数

外部设备通过引脚给出的硬件中断,也可以产生软件中断,19个上升、下降或都触发。EXTI0~EXTI15连接到管脚,EXTI线16连接到PVD(VDD监视),EXTI线17连接到RTC(闹钟),EXTI线18连接到USB(唤醒)。

基础应用1:设定外部中断初始化函数。按需求,不是必须代码。 用法:

Void EXTI_Configuration(void) {

EXTI_InitTypeDef EXTI_InitStructure; //外部设备中断恢复默认参数 EXTI_InitStructure.EXTI_Line = EXTI_Line12 | EXTI_Line14; //设定所需产生中断的通道,共19个 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //启动中断的接收 EXTI_Init(&EXTI_InitStructure); //外部设备中断启动

}

图中在第4步根据中断屏蔽寄存器中的设定判别发生了哪一个中断,然后将该信号发送给NVIC(增强型可屏蔽中断控制器),NVIC根据信号将相应的中断函数的地址赋予PC要调用的寄存器,这样Cortex-M3将执行对应的中断函数。 用户在使用中无需通过头文件定义,在软件库3.1.2中的startup_stm32f10x_??.s文件中对中断函数的地址进行了宏定义,用户只需根据startup_stm32f10x_??.s中的PPP_IRQHandler,在stm32f10x_it.c和stm32f10x_it.h中添加相应的void PPP_IRQHandler(void) 函数,并在.c中添加中断处理代码即可。

对于EXTI9_5_IRQHandler和EXTI15_10_IRQHandler中断用户需要在处理函数中通过ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)来判别发生了哪一个中断,再进行相应的处理。

7、阅读dma

通过总线而越过CPU读取外设数据

通过DMA应用可以加速单片机外设、存储器之间的数据传输,并在传输期间不影响CPU进行其他事情。这对于入门卡法基本功来说,没有太大必要,这个内容先行跳过。

8、阅读systic:系统定时器

可以输出和利用系统时钟的计数、状态

基础应用一,精确计时的延时子函数。推荐使用的代码。

Static vu32 TimingDelay; //全局变量申明 Void SysTick_Configuration(void) {

SysTick_CounterCmd(SysTick_Counter_Disable); //停止系统定时器 SysTick_ITConfig(Disable); //停止systick中断

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_DIV8); //HCLK作为时钟源,频率值/8 SysTick_SetReload(9000); //重置时间1毫秒,以72MHz为基础计算 SysTick_ITConfig(Enable); //开启systic中断 }

void Delay(u32 nTime) //延迟一毫秒的函数 {

SysTick_CounterCmd(SysTick_Counter_Enable); //systic开始计时

TimingDelay=nTime; //计时长度赋值给递减变量 while(TimingDelay!=0); //检测是否计时完成 SysTick_CounterCmd(SysTick_Counter_Disable); //关闭计数器 SysTick_CounterCmd(SysTick_Counter_Clear); //清楚计数值 }

Void TimingDelay_Decrement(void) //递减变量函数,函数名由“stm32f10x_it.c”中的中断

//相应函数定义好了

{

If(TimingDelay!=0x00) TimingDelay--; }

新手可用简化的延时函数代替: Void Delay(vu32 nCount) {

For(;nCount!=0;nCount--); }

当延时较长,又不需要精确计时的时候可以使用嵌套循环: Void Delay(vu32 nCount) {

Int i;

for(;nCount!=0;nCount--) For(i=0;i<0xffff;i++); }

9、基本硬件功能的建立

flash、lib、nvic、rcc和gpio,基础程序库编写

这几个库函数中有一些函数是关于芯片的初始化的,每个程序中比用。为保证程序品质,初学阶段要求严格遵守官方习惯。注意,官方程序库例程中有个platform_conf.h文件,是专门用来指定同类外设中第几号外设被使用,就是说在main.c里面所有外设程序号用x代替,比如USARTx,程序会到这个头文件中去找到底是用那些外设,初学的时候参考例程别被这个所迷惑。

全部比用代码取自函数库所带例程,并增加主句注释。 习惯顺序:lib(debug),RCC(包括flash优化),NVIC,GPIO 必用模块初始化函数的定义:

void RCC_Configuration(void); //定义时钟初始化函数 void GPIO_Configuration(void); //定义管脚初始化函数 void NVIC_Configuration(void); //定义中断管理初始化函数 void Delay(vu32 nCount); //定义延迟函数 main中的初始化函数调用: RCC_Configuration();

GPIO_Configuration(void); NVIC_Configuration(void);

Lib注意事项:

属于lib的Debug函数的调用,应该放在main函数最开始,不要改变其位置。 RCC注意事项:

Flash优化处理可以不做,但是两句也不难,也不用改参数。。。。。。 根据需要开启设备时钟可以节省电能 时钟频率需要根据实际情况设置参数 NVIC注意事项:

注意理解占先优先级和响应优先级的分组概念 GPIO注意事项:

注意以后的过程中收集不同管脚对应的频率和模式的设置。 作为高低电平的I/O,所需设置:RCC初始化里面打开

10、基本串口通讯

初始化函数定义、调用:

Void USART_Configuration(void); //定义初始化函数 初始化代码:

Void USART_Configuration(void) {

USART_InitTypeDef USART_InitStructure;

USART_InitStructure.USART_BaudRate = 9600;

USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_Odd;

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_InitStructure.USART_Clock = USART_Clock_Disable; USART_InitStructure.USART_CPOL = USART_CPOL_High; USART_InitStructure.USART_CPHA = USART_CPHA_1Edge; USART_InitStructure.USART_LastBit = USART_LastBit_Enable; USART_Init(USART1, &USART_InitStructure); //初始化

USART_Cmd(USART1,ENABLE); //启动串口 }

RCC中打开相应串口

RCC_APB2Perph kCmd(RCC_APB2Perph_USART1,ENABLE);

GPIO里面设定相应的串口管脚模式:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //9

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //配置为复用推挽输出 GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //LED2, LED3 V7 V8 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);

简单应用:发送一位字符

USART_SendData(USART1,数据); //发送一位数据

whlie(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){} //等待发送完毕 接收一位字符

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){} //等待接收完毕 变量=USART_ReceibeData(USART1); //接收一个字节

发送一个字符串

先定义字符串:char rx_data[250];然后再需要发送的地方添加如下代码

Int i; //定义循环变量

while(rx_data!=’\\0’) //循环逐字输出,到结束字‘\\0’ {

USART_SendData(USART1,rx_data);

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){} i++; }

USART 注意事项:

发送和接收都需要配合标志等待。

只能对一个字节操作,对字符串等大量数据操作需要写函数 使用串口所需设置:

RCC初始化:RCC_APB2PeriphClockCmd(Rcc_APB2Periph_USARTx);

GPIO里面管脚设定:串口RX(50Hz,IN_FLOATING);串口TX(50Hz,AF_PP); printf函数重定义(不必理解,调试通过后备用) (1)需要c标准函数: #include “stdio.h” (2)粘贴函数定义代码

#define PUTCHAR_PROTOTYPE int _io_putchar(int ch) //定义为putchar应用 (3)RCC中打开相应串口

(4)GPIO里面设定相应串口管脚模式 (5)增加为putchar函数 int putchar(int c) {

If(c==’\\n’)

{putchar(‘\\r’);}

USART_SendData(USART1,c);

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){} renturn c; }

(6) printf使用的变量输出:%c 字符,%d整数,%f浮点数,%s字符串,/n或/f为换行。注意,只能用于main.c中。

NVIC串口中断的应用

在实际应用中,不使用中断进行的输入,效率是非常低的,这种用法很少见,大部分串口的输入都离不开中断。

初始化函数定义及函数调用:不用添加和调用初始化函数,在指定调试地址的时候已经调用过,在那个NVIC_Configuration里面添加相应中断代码就行了。 过程:

(1)串口初始化中USART_Cmd之前加入中断设置:

USART_ITConfig(USART1,USART_IT_TXE,ENABLE); //TXE发送中断,TC传输完成中断,

//RXNE接收中断,PE奇偶错误中断,可以是多个

(2)RCC、GPIO里面打开串口相应的基本时钟、管脚设置 (3)NVIC里面加入串口中断打开代码;

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel; //串口1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //中断占先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //中断相应优先级0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断 NVIC_InitStructure(&NVIC_InitStructure); //初始化

(4)在stm32f10x_it.c文件中找到void USART1_IRQHandler函数,在其中添加执行代码。一 般最少三个步骤:先使用if语句判断是发生那个中断,然后清除中断标志位,最后给字符串符 赋值,或做其它事情。 Void USART1_IRQHandler(void) {

Char RX_dat;

If(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) //判断是否收到中断 {

USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志 GPIO_WriteBit(GPIOB,GPIO_P_10,(BitAction)0x01); //开始传输

RX_dat=USART_ReceiveData(USART1)&0X7F; //接收数据并除去第一位 USART_SendData(USART1,RX_dat); //发送数据 while(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET){}

} }

(5)中断注意事项:

可以随时在程序中使用USART_ITConfig(USART1,USART_IT_TXE,DISABLE);来关闭中断相应。 NVIC_InitTypeDef NVIC_InitStructure定义一定要加在NVIC初始化模块的第一句。 全局变量于函数的定义:在任意.c文件中定义的变量或函数,在其他.c文件中使用extern+ 定义码再次定义就可以直接调用了。

STM32F100X RCC_APB2Periph_AFIO--复用IO时钟的使用

为了优化64脚或100 脚封装的外设数目,可以把一些复用功能重新映射到其他引脚上。设置复用

重映射和调试I/O 配置寄存器(AFIO_MAPR) 实现引脚的重新映射。这时,复用功能不再映射到它

们的原始分配上。

需要用到外设的重映射功能时才需要使能AFIO的时钟

外部中断(EXTI)中与AFIO有关的寄存器是AFIO-EXTICR1、2、3,它们是用来选择EXTIx外部中断的输入脚之用。 举例:重映射USART2

USART2的TX/RX在PA.2/3

但是,PA.2已经被Timer2的channel3使用 这时,如果还想使用USART2,但又不想影响Timer2的使用,这就需要把USART2的TX/RX重映射到PD.5/6。 映射库函数的调用过程

(1)使能被重新映射到的I/O端口时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

(2)使能被重新映射的外设时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); (3)使能AFIO功能的时钟(勿忘!)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); (4)进行重映射

GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

用库函数使用SYSTICK的方法

Systick配置函数

延迟一毫秒函数

中断调用函数

中断进入函数

Q:什么是SYSTick定时器?

SysTick 是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。

Q:为什么要设置SysTick定时器?

(1)产生操作系统的时钟节拍

SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。 (2)便于不同处理器之间程序移植。

Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。 不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的。

(3)作为一个闹铃测量时间。

SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间等。要注意的是,当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂停运作。

Q:Systick如何运行?

首先设置计数器时钟源,CTRL->CLKSOURCE(控制寄存器)。设置重载值(RELOAD寄存器),清空计数寄存器VAL(就是下图的CURRENT)。置CTRL->ENABLE位开始计时。

如果是中断则允许Systick中断,在中断例程中处理。如采用查询模式则不断读取控制寄存器的COUNTFLAG标志位,判断是否计时至零。或者采取下列一种方法

当SysTick定时器从1计到0时,它将把COUNTFLAG位置位;而下述方法可以清零之:

1. 读取SysTick控制及状态寄存器(STCSR) 2. 往SysTick当前值寄存器(STCVR)中写任何数据 只有当VAL值为0时,计数器自动重载RELOAD。

Q:如何使用SysTicks作为系统时钟?

SysTick 的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要这种“滴答”来推动任务和时间的管理。如欲使能SysTick异常,则把STCSR.TICKINT置位。另外,如果向量表被重定位到SRAM中,还需要为SysTick异常建立向量,提供其服务例程的入口地址。

本文来源:https://www.bwwdw.com/article/uk9r.html

Top