1、第第4 4章章 STM32STM32最小最小系统系统 与与嵌入式嵌入式C C语言语言STM32本章知识与能力要求u 掌握掌握STM32最小系统设计;最小系统设计;u 理解理解和掌握嵌入式和掌握嵌入式C语言的程序结构;语言的程序结构;u 掌握掌握嵌入式嵌入式C语言的数据类型、语言的数据类型、const、volatile和和extern等关键字的使用;等关键字的使用;u 掌握嵌入式掌握嵌入式C语言的条件编译;语言的条件编译;u 掌握嵌入式掌握嵌入式C语言指针的应用语言指针的应用;u 学会学会分析分析HAL库文件源码。库文件源码。4.1 STM32最小系统最小系统4.2 嵌入式嵌入式C语言语言4.3
2、 HAL库文件源码分库文件源码分析析第4章 STM32最小系统与嵌入式C语言4.1 STM32最小系统4.1.1 电源电路4.1.2 时钟电路(晶振电路)4.1.3 复位电路4.1.4 调试和下载电路03040102用最少的元用最少的元件组成单片件组成单片机可以工作机可以工作的系统的系统4.1.1 电源电路电源电路微控制器微控制器工作电压工作电压3.4V5.5V或或2.7V3.6V为系统提供稳定可靠的供电为系统提供稳定可靠的供电STM32F103系列系列:2.0V3.6V电源电压电源电压5V5VAMS1117系列芯片系列芯片1.5V1.8V2.5V2.85V3.0V3.3V5.0V供电供电4.
3、1.1 电源电路电源电路 C1、C3为输入电容,作用是为输入电容,作用是防止断电后出现电压防止断电后出现电压倒置倒置 C2、C4为输出滤波电容,作为输出滤波电容,作用是抑制自激振荡和稳定输用是抑制自激振荡和稳定输出出电压电压AMS1117-3.3v降压电路图降压电路图电压转换电路提供电压转换电路提供3.3V电压供电压供STM32主电源主电源VDD使用使用4.1.1 电源电路电源电路 STM32内部包含一个电压调节器,将内部包含一个电压调节器,将外部外部3.3V电压转换为电压转换为1.8V提供给提供给Cortex-M3内核、内存以及外设内核、内存以及外设使用。使用。STM32STM32内部电源框
4、图内部电源框图内部有两内部有两个可选电源选项:个可选电源选项:ADC模块所需的模块所需的参考电压参考电压VREF+和和VREF-。常采。常采用独立的用独立的供电供电电源。电源。备用电池电源备用电池电源VBAT。当主电源当主电源VDD掉电后,掉电后,通过通过VBAT引脚引脚为为实时时钟和实时时钟和备份寄存器提供备份寄存器提供电源。通常选择备用电源。通常选择备用电池作为该电源电池作为该电源,电,电压压范围为范围为1.83.6V。若不。若不使用备用电池供使用备用电池供电,则电,则VBAT引脚必须与主电源引脚引脚必须与主电源引脚VDD连接。连接。4.1.2 时钟电路(晶振电路)时钟电路(晶振电路)时钟
5、系统产生时钟系统产生供系统供系统正常工作的稳定的脉冲正常工作的稳定的脉冲信信号号.单片机单片机的一切指令的执行都依赖的一切指令的执行都依赖于晶振提供的时钟频率,时钟频率于晶振提供的时钟频率,时钟频率越高,单片机运行速度越快,功耗越高,单片机运行速度越快,功耗也就越也就越大大。4.1.2 时钟电路(晶振电路)时钟电路(晶振电路)晶晶振振有源晶振(振荡器)有源晶振(振荡器)无源晶振(晶体)无源晶振(晶体)内部内部包含晶体管和阻容包含晶体管和阻容元件元件一一个完整的谐振个完整的谐振振荡器振荡器只需只需要给它要给它供电供电不不需要外接其他需要外接其他元件元件通常通常有四个引脚:有四个引脚:VCC、GN
6、D、晶、晶振输出引脚和一个悬空的引脚。振输出引脚和一个悬空的引脚。只有只有2个引脚个引脚的无极性的无极性元件元件需要需要借助外部电路起借助外部电路起振振精度精度上差上差一些一些价格便宜价格便宜4.1.2 时钟电路(晶振电路)时钟电路(晶振电路)使用使用低速晶振电路作为系统低速低速晶振电路作为系统低速外部时钟源,频率为外部时钟源,频率为32.768kHz,主,主要要用作用作RTC的时钟源,用于精准计时的时钟源,用于精准计时定时电路、万年历等。定时电路、万年历等。使用使用外部高速晶振电路作为系外部高速晶振电路作为系统时钟源,一般称为高速外部时钟统时钟源,一般称为高速外部时钟信号(信号(HSE)。一
7、般。一般选择选择8MHz,方,方便便倍频。倍频。01024.1.3 复位电路复位电路复位电路将系统进行复位复位电路将系统进行复位 STM32内部复位电路内部复位电路 STM32芯片复位管脚持续为低电平时复位,芯片复位管脚持续为低电平时复位,STM32的的NRST引脚在内部已引脚在内部已经连接了一个上拉电阻经连接了一个上拉电阻,数据,数据手册建议复位电路需外接一个对地电容,如手册建议复位电路需外接一个对地电容,如果认为这个上拉电阻较小,用户也可以在复位电路中外接一个上拉电阻果认为这个上拉电阻较小,用户也可以在复位电路中外接一个上拉电阻4.1.3 复位电路复位电路 按键按键S1按下时,按下时,RE
8、SET和地连通和地连通,产生低产生低电平,实现复位。电平,实现复位。上电上电复位:复位:在上电瞬间,由于电容来两端电在上电瞬间,由于电容来两端电压不能突变,压不能突变,RESET出现短暂低电平,出现短暂低电平,芯片自动复位,之后电容充电,充电芯片自动复位,之后电容充电,充电时间由电阻和电容共同决定:时间由电阻和电容共同决定:1.11.1 10k0.1F=1.1mstRC 上电后约上电后约1ms左右系统完成复位,左右系统完成复位,之后,单片机开始正常之后,单片机开始正常工作。工作。手动复位:手动复位:4.1.4 调试和下载电路调试和下载电路 将将编译好的程序下载到单片机编译好的程序下载到单片机中
9、运行以及进行在线调试中运行以及进行在线调试 STM32F103系列微控制器内核集成系列微控制器内核集成了了SWD/JTAG调试端口(缩写为调试端口(缩写为SWJ-DP)JTAG调试调试接口接口(5引脚的引脚的JTAG-DP)SWD(Serial Wire Debug,串行单,串行单总线调试)总线调试)(2引脚的引脚的SW-DP)SWJ-DP引脚名称引脚名称JTAG-DPSW-DP引脚引脚号号JTMS/SWDIO输入:输入:JTAG模模式选择式选择输入输出:串行输入输出:串行数据输入输出数据输入输出PA13JTCK/SWCLK输入:输入:JTAG时时钟钟输入:串行时钟输入:串行时钟PA14JTD
10、I输入:输入:JTAG数数据输入据输入 PA15JTDO/TRACESWO输出:输出:JTAG数数据输出据输出异步跟踪异步跟踪PB3JNTRST输入:输入:JTAG模模块复位块复位 PB4STM32STM32调试端口引脚功能调试端口引脚功能4.1.4 调试和下载电路调试和下载电路Cortex-M3内核有三种启动方式,可以通过内核有三种启动方式,可以通过BOOT0和和BOOT1的电平组合进行选择的电平组合进行选择。内核的启动模式内核的启动模式启动模式选择引脚启动模式选择引脚启动模式对应的存储介质启动模式对应的存储介质说明说明BOOT0BOOT10 x闪存存储器,即用户闪存闪存存储器,即用户闪存F
11、lash闪存存储器被选为启动区域闪存存储器被选为启动区域10系统存储器,即系统系统存储器,即系统Flash系统存储器被选为启动区域系统存储器被选为启动区域11内置内置SRAM内置内置SRAM被选为启动区域被选为启动区域 在在STM32F103系列微控制器中通过设置系列微控制器中通过设置BOOT1:0引脚电平的高低选择三种不引脚电平的高低选择三种不同启动模式,从而将同启动模式,从而将STM32F103微控制器的存储空间起始地址微控制器的存储空间起始地址0 x00000000映射映射到不同存储区域的起始到不同存储区域的起始地址。地址。4.1.4 调试和下载电路调试和下载电路保持保持BOOT1为为“
12、0”,当,当BOOT0=1时,时,STM32F103系列微控制器从系统存储器系列微控制器从系统存储器的起始地址的起始地址0 x1FFFF000开始启动开始启动;当当BOOT0=0时,时,STM32F103系列微控制器系列微控制器从用户从用户Flash的起始地址的起始地址0 x80000000开始开始启动运行代码,这是启动运行代码,这是STM32最常用的启最常用的启动方式动方式。若要若要从片内从片内SRAM起始地址起始地址0 x20000000启启动,则动,则BOOT0、BOOT1均设为均设为“1”。内核的启动模式内核的启动模式4.2 嵌入式C语言4.2.1 STM32的数据类型4.2.2 co
13、nst 关键字4.2.3 static 关键字4.2.4 volatile 关键字4.2.5 extern 关键字4.2.8 typedef4.2.9#define4.2.10#ifdef、#if条件编译4.2.11 指针4.2.12 回调函数4.2.7 enum4.2.6 struct结构体4.2 嵌入式C语言 嵌入式嵌入式开发中既有底开发中既有底层硬件的开发又涉及上层硬件的开发又涉及上层应用的开发,即涉及层应用的开发,即涉及系统的硬件和软件,系统的硬件和软件,C语言既具有汇编语言操语言既具有汇编语言操作底层的优势,又具有作底层的优势,又具有高级语言功能性强的高级语言功能性强的特特点。点。1
14、程序总是从程序总是从main函数开始执行;函数开始执行;2345函数是函数是C语言的基本结构;语言的基本结构;函数由两部分组成:函数说明函数由两部分组成:函数说明部分和函数体。部分和函数体。一个一个C语言程序包含若干个源程序文件语言程序包含若干个源程序文件(.c文件)和头文件(文件)和头文件(.h文件)。文件)。采用外设功能模块化设计方法,一个外采用外设功能模块化设计方法,一个外设功能模块包括一个源文件(设功能模块包括一个源文件(.c文件)文件)和一个头文件(和一个头文件(.h文件)。文件)。4.2 嵌入式C语言 嵌入式嵌入式系统系统开发多采用模块开发多采用模块化、层次化的设化、层次化的设计思
15、想,系统层计思想,系统层次架构清晰,便次架构清晰,便于协同开发。于协同开发。嵌入式系统软件结构框图嵌入式系统软件结构框图4.2.1 STM32的数据类型的数据类型 数据数据是嵌入式是嵌入式C语语言的基本操作言的基本操作对象对象。数据类型数据类型是指数据是指数据在计算机内存中的存在计算机内存中的存储方式。储方式。嵌入式嵌入式C C语言的数据类型语言的数据类型4.2.1 STM32的数据类型的数据类型 不同不同CPU所定义的数据类型的长度不一致,所定义的数据类型的长度不一致,ST公司为开发人员提供了基于公司为开发人员提供了基于C语言的标准外设库语言的标准外设库,标准外设库标准外设库中中定义定义的数
16、据类型的长度的数据类型的长度如下表所示。如下表所示。C语言的数据类型语言的数据类型STM32对应的数据类型对应的数据类型说明说明unsigned charuint8_t8位无符号数据(位无符号数据(0 255)unsigned short intuint16_t16位无符号数据(位无符号数据(0 65535)unsigned intuint32_t32位无符号数据(位无符号数据(0 232-1)unsigned long longuint64_t64位无符号数据(位无符号数据(0 264-1)signed char int8_t8位有符号数据(位有符号数据(-128 +127)signed s
17、hort intint16_t16位有符号数据(位有符号数据(-32768 +32767)signed int int32_t32位有符号数据(位有符号数据(-231 231-1)signed long longint64_t64位有符号数据(位有符号数据(-263 263-1)4.2.1 STM32的数据类型的数据类型 v3.5.0版本已不再使版本已不再使用旧用旧的的数据类型,数据类型,为了为了兼容兼容以前以前的版本,的版本,stm32f10 x.h头头文件还对标准外设库之前版本所使用文件还对标准外设库之前版本所使用的数据类型进行了的数据类型进行了说明。说明。STM32开发开发中同一中同一数
18、据类型数据类型有多种表示方式有多种表示方式。无无符号符号8位整型数据位整型数据有有unsigned char,uint8_t,u8三三种表示种表示方式。方式。例例 最新的最新的v3.5.0版本采用标准版本采用标准的的C99标准,即标准,即uint8_t方式。方式。4.2.1 STM32的数据类型的数据类型 _I、_O以及以及_IO为为IO类型限定类型限定词,内核头文件词,内核头文件core_cm3.h定义了定义了标准外设库所使用的标准外设库所使用的IO类型类型限定词限定词。数据类型和数据类型和IO类型限定词结合类型限定词结合在一起,在标准外设库中常用来在一起,在标准外设库中常用来定义寄存器和结
19、构体定义寄存器和结构体变量:变量:IO类型类型限定词限定词类型类型说明说明_Ivolatile const只读操作只读操作_Ovolatile只写操作只写操作_IOVolatile读和写操作读和写操作4.2.1 STM32的数据类型的数据类型 stm32f10 x.h头文件中常用头文件中常用的布尔类型的的布尔类型的变量的定义变量的定义typedef enum DISABLE=0,ENABLE=!DISABLE FunctionalState;typedef enum RESET=0,SET=!RESET FlagStatus,ITStatus;typedef enum ERROR=0,SUCC
20、ESS=!ERROR ErrorStatus;RESET与与SETDISABLE与与ENABLEERRORERROR与与SUCCESSSUCCESS#define IS_FUNCTIONAL_STATE(STATE)(STATE)=DISABLE)|(STATE)=ENABLE)4.2.2 const关键字关键字const作用作用 用于定义只读用于定义只读的变的变量,其值在编译时不量,其值在编译时不能被改变能被改变const 常量类型常量类型 常量名常量名=常量表达式;常量表达式;格式格式 为了为了在编译时防在编译时防止变量的值被误修改,止变量的值被误修改,提高程序的安全性和提高程序的安全性和
21、可靠性。可靠性。目的目的要求要求属性属性 const关键词修饰的关键词修饰的变量在声明时必须初变量在声明时必须初始化始化 在在C标准中,标准中,const定义的变量是全局的。定义的变量是全局的。4.2.3 static关键词关键词 修饰变量或函数。修饰后的变量称为静态变量。修饰变量或函数。修饰后的变量称为静态变量。在在全局变量全局变量之前加上关键字之前加上关键字static,则该全局变量被,则该全局变量被定义成为一个静态定义成为一个静态全局变量全局变量。作用作用范围只在定义该变量的源文件内范围只在定义该变量的源文件内有效有效,其他源文其他源文件不能引用该全局变量,避免了在其他源文件中因引用件不
22、能引用该全局变量,避免了在其他源文件中因引用相同名字的变量而引发错误,有利于模块化程序设计。相同名字的变量而引发错误,有利于模块化程序设计。4.2.3 static关键词关键词#include stm32f1xx_hal.hstatic void DMA_SetConfig(DMA_HandleTypeDef*hdma,uint32_t SrcAddress,uint32_t DstAddress,uint32_t DataLength);HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef*hdma,uint32_t SrcAddress,
23、uint32_t DstAddress,uint32_t DataLength)HAL_StatusTypeDef status=HAL_OK;if(HAL_DMA_STATE_READY=hdma-State)DMA_SetConfig(hdma,SrcAddress,DstAddress,DataLength);static编程要点编程要点1:模块化模块化的程序设计的程序设计中,用中,用static声明一个声明一个函数,则该函数只能函数,则该函数只能被该模块内的其它函被该模块内的其它函数调用数调用。解析:解析:DMA_SetConfig()函数函数只能被只能被stm32f1xx_hal_d
24、ma.c的的其它函数调用,不能被其其它函数调用,不能被其它模块的文件它模块的文件使用使用。4.2.3 static关键词关键词void fun_count()static count_num=0;/声明一个静态局部变量,声明一个静态局部变量,count_num用作计数器,初值为用作计数器,初值为0。count_num+;printf(%dn,count_num);int main(void)int i=0;for(i=0;i=5;i+)fun_count();return 0;static编程要点编程要点2:static除了用于静态除了用于静态全局变量,还用于定义全局变量,还用于定义静态局部变
25、量,保证静静态局部变量,保证静态局部变量在调用过程态局部变量在调用过程中不被重新初始化。中不被重新初始化。解析:解析:在在main函数中每调用一次函数中每调用一次fun_count()函数,则静态局部变量函数,则静态局部变量count_num加加1,而不是每次都被初始化为初值而不是每次都被初始化为初值0。4.2.4 volatile关键字关键字 一一个类型修饰符,个类型修饰符,“易变的易变的”。volatile char i;使用使用volatile关键字定义了一个字符型的变关键字定义了一个字符型的变量量i,指出,指出i是随时可能发生变化的,每次使用是随时可能发生变化的,每次使用的时候都必须从
26、的时候都必须从i的地址中读取的地址中读取。使用使用volatile就是就是不让编译器进行优化不让编译器进行优化,即每次读取或者修改值的时候,都必须重即每次读取或者修改值的时候,都必须重新新从内存中读取或者修改从内存中读取或者修改,而不是使用保,而不是使用保存在寄存器里的备份。存在寄存器里的备份。中断服务程序中断服务程序中修改中修改的的供其他供其他程序检测的变量需程序检测的变量需要使用要使用volatile;多任务环境下各任务间多任务环境下各任务间共享的标志应添加共享的标志应添加volatile;存储器映射的硬件寄存存储器映射的硬件寄存器通常也要加器通常也要加volatile进行进行说明。说明。
27、作用作用使用使用方式方式应用应用场合场合4.2.5 extern关键词关键词extern int a;指明指明此函数或变量的定义在别的文件中,此函数或变量的定义在别的文件中,提示编译器遇到此函数或变量时去其他模块提示编译器遇到此函数或变量时去其他模块中寻找其定义中寻找其定义。使用使用extern是一个声明是一个声明而不是重新定义而不是重新定义声明变量声明变量a,而而不是在定义变量不是在定义变量a,并未为,并未为a分配内存空间分配内存空间声明函数声明函数funA(),此函数已在其,此函数已在其他文件中定义。他文件中定义。extern C进行链接指进行链接指定,告知编译器这是采定,告知编译器这是采
28、用用C语言定义的函数语言定义的函数extern int funA();4.2.5 extern关键词关键词#ifdef _cplusplusextern C#endif#ifdef _cplusplus#endif解析:解析:如果如果定义了定义了_cplusplus(C+编译器中自定编译器中自定义的义的宏),宏),则执行则执行extern“C”语句。语句。C+支持函数重支持函数重载载,而,而C语言不支持函数重载语言不支持函数重载,在,在C+环境下使用环境下使用C函数会函数会出现链接时找不到对应函数的情况,出现链接时找不到对应函数的情况,这时需这时需要要使用使用extern“C”进行进行链接指定
29、,告知链接指定,告知编译器使用编译器使用C语言的命名规则来处理语言的命名规则来处理函数。函数。#ifdef _cplusplusextern C#endif/函数声明函数声明#ifdef _cplusplus#endif 当当函数有可能被函数有可能被C语言或语言或C+使用时,将函数声明放在使用时,将函数声明放在extern“C”中中以免出现编译以免出现编译错误。错误。4.2.5 extern关键词关键词 为为保证全局变量和功能保证全局变量和功能函数的使用,函数的使用,extern一般一般用在用在.h头文件中对某个模头文件中对某个模块提供给其它模块调用的块提供给其它模块调用的外部函数及变量进行声
30、明,外部函数及变量进行声明,实际编程中只需要将该实际编程中只需要将该.h头文件包含进该模块对应头文件包含进该模块对应的的.c文件,即在该模块的文件,即在该模块的.c文件中加入代码文件中加入代码#include xxx.h。ADCx.h/声明全局变量声明全局变量extern uint16_t ADC_ConvertedValue;/存放存放ADC的转换结果的转换结果/外部功能函数声明外部功能函数声明extern void ADC_Init(void);/ADC初始化函数初始化函数 ADCx.c#include ADCx.h/定义变量定义变量static uint16_t pwd=1;/局部变量局
31、部变量,仅,仅作用于本函数作用于本函数uint16_t ADC_ConvertedValue;/全局变量全局变量 void ADC_Init(void)4.2.6 struct结构体结构体举例作用格式 struct用于定义结构体用于定义结构体类型,其作用是将类型,其作用是将不同不同数据类型数据类型的数据组合在的数据组合在一起,构造出一个新的一起,构造出一个新的数据类型。数据类型。struct一般用法为:一般用法为:struct 结构体名结构体名 类型标识符类型标识符 成员名成员名1;类型标识符类型标识符 成员名成员名2;结构体变量;结构体变量;struct person char name8;
32、int age;char sex8;char address20;person_liu;4.2.6 struct结构体结构体struct person char name8;int age;char sex8;char address20;struct person person_liu,person_zhang;结构体结构体变量变量(如上例中的(如上例中的person_liu)可以不在定义结构体可以不在定义结构体时定义,后续需要时再进行时定义,后续需要时再进行定义。定义。使用这种定义方式可以很方便地同使用这种定义方式可以很方便地同时定义多个结构体时定义多个结构体变量。变量。结构体变量的使用结
33、构体变量的使用采用如下形式:采用如下形式:结构体结构体变量名变量名.成员名成员名如:如:person_liu.age=35;struct 结构体名结构体名 结构体变量结构体变量;定义格式定义格式4.2.7 enum关键词关键词enum Weekdays Monday=1,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday Mydays,Olddays;enum 枚举名枚举名 枚举枚举成员成员1,枚举枚举成员成员2,枚举变量枚举变量;用来用来将一个变量或对象的所有可能的值将一个变量或对象的所有可能的值一一列出,变量取值只限于列举出来的值。一一列出,
34、变量取值只限于列举出来的值。enum枚举类型的用法:枚举类型的用法:枚举成员的值是常枚举成员的值是常量,不是变量,量,不是变量,不能不能被赋值被赋值,但可以将枚,但可以将枚举值赋给枚举变量。举值赋给枚举变量。枚举枚举类型具有类型具有自动编自动编号号的功能,第一个的功能,第一个枚举枚举成员其成员其默认值为整型的默认值为整型的0,后续后续枚举枚举成员成员的的值在前值在前一个成员上加一个成员上加1。可以可以自定义自定义枚举枚举成员成员的的值,如果把第一个枚值,如果把第一个枚举元素的值定义为举元素的值定义为1,那么第二枚举成员的值那么第二枚举成员的值就为就为2,以此类推,如,以此类推,如上述例子中上述
35、例子中Friday的值的值为为5。4.2.8 typedef给变量起一个容易给变量起一个容易记且意义明确的新记且意义明确的新名字;名字;简化简化一些比较复杂的类型声明一些比较复杂的类型声明。目的目的:用来为复杂的声明定义一个简单的用来为复杂的声明定义一个简单的别名,方便别名,方便记忆记忆作用:作用:不是一个真正意不是一个真正意义上的新义上的新类型类型用法一用法一:typedef的基本的基本应用应用 typedef 类型名类型名 自定义的别名自定义的别名;格式:格式:typedef signed char int8_t;/给数据类型给数据类型signed char起个别名起个别名int8_t t
36、ypedef signed int int32_t;/给数据类型给数据类型signed int起个别名起个别名int32_t举例举例:为已知的数据类型为已知的数据类型起一个简单的别名起一个简单的别名4.2.8 typedeftypedef struct uint16_t GPIO_Pin;GPIOSpeed_TypeDef GPIO_Speed;GPIOMode_TypeDef GPIO_Mode;GPIO_InitTypeDef;STM32标准外设库中标准外设库中stm32f10 x_gpio.h头头文件文件中利用结构体中利用结构体别名别名GPIO_InitTypeDef使用使用typede
37、f为这个新建的结构体起了为这个新建的结构体起了一个新的名字叫一个新的名字叫GPIO_InitTypeDef 则则可以可以使用使用GPIO_InitTypeDef定义定义一个变量一个变量GPIO_InitStructure,从而,从而调用调用GPIO_Mode。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;用法二用法二:与结构体与结构体struct结合使用结合使用4.2.8 typedefstm32f10 x_gpio.h头文件中的代码。头文件中的代码。typedef enum
38、GPIO_Speed_10MHz=1,GPIO_Speed_2MHz,GPIO_Speed_50MHzGPIOSpeed_TypeDef;解析解析1:利用利用typedef关键字将此枚举类关键字将此枚举类型定义一个别名型定义一个别名GPIOSpeed_TypeDef,这,这里省略里省略了枚举了枚举类型的枚举类型的枚举名,名,只用只用typedef起了个别名。起了个别名。用法三用法三:与与enum结合使用结合使用解析解析2:enum枚举类型共三个枚举类型共三个成员成员,并并将第一个枚举成员将第一个枚举成员GPIO_Speed_10MHz赋赋值为值为1,enum枚举类型会将枚举成员的赋枚举类型会将
39、枚举成员的赋值在第一个枚举成员赋值的基础上加值在第一个枚举成员赋值的基础上加1,因此,因此,GPIO_Speed_2MHz默认值为默认值为2。4.2.9#definedefine#define是是C语言中的预处理命语言中的预处理命令,它用于宏定义,用来将一个令,它用于宏定义,用来将一个标识符定义为一个字符串,该标标识符定义为一个字符串,该标识符称为宏名,被定义的字符串识符称为宏名,被定义的字符串称为替换称为替换文本。文本。所谓所谓预处理是指预处理是指在编译在编译之前所之前所做的工作,由预处理程序负责完做的工作,由预处理程序负责完成,编译时,系统将自动引用预成,编译时,系统将自动引用预处理程序对
40、源程序中的预处理部处理程序对源程序中的预处理部分进行处理分进行处理。采用采用宏定义的目的主要是方便宏定义的目的主要是方便程序编写,一般放在源文件的前程序编写,一般放在源文件的前面,称为预处理部分。面,称为预处理部分。typedef与与#define的区别的区别:typedef是在编译阶段处理是在编译阶段处理的的;#define是在预处理阶段处理的。是在预处理阶段处理的。4.2.9#define用法一用法一:无参数宏定义无参数宏定义例例1:#define UINT8_MAX 255解析解析:定义宏名定义宏名UINT8_MAX,代表,代表255。例例2:#define _ _IO volatile
41、;解析解析:定义宏名定义宏名_ _IO,表示,表示volatile,以后程序以后程序中中再需要用再需要用到到volatile,就可以使用就可以使用_ _IO。例例3:#define RCC_AHBPeriph_DMA1 (uint32_t)0 x00000001)解析解析:定义:定义RCC_AHBPeriph_DMA1宏名,代表宏名,代表32位的无符号数据位的无符号数据0 x00000001。定义格式:定义格式:#define 所定义的所定义的宏宏名名可以是常数、字符可以是常数、字符串、表达式串、表达式等等4.2.9#define用法一:用法一:无参数宏定义无参数宏定义 标准外设库标准外设库v
42、3.5.0的的stm32f10 x_rcc.h文件中文件中APB2_peripheral外设基外设基地址的地址的定义定义APB2_peripheral各外设基地址的定义各外设基地址的定义4.2.9#define用法用法二二:带参数的宏定义带参数的宏定义例例4:#define SUM(x,y)(x+y)a=SUM(2,2);解析解析:将将SUM(x,y)定义为定义为x+y,预编译时会预编译时会将将SUM(x,y)替换为替换为x+y,a的结果是的结果是4例例5:#define IS_GPIO_SPEED(SPEED)(SPEED)=GPIO_Speed_10MHz)|(SPEED)=GPIO_Sp
43、eed_2MHz)|(SPEED)=GPIO_Speed_50MHz)解析解析:使用使用宏定义宏定义#define将将IS_GPIO_SPEED(SPEED)替换为替换为GPIO_Speed_10MHz或者或者GPIO_Speed_2MHz或者或者GPIO_Speed_50MHz。定义格式:定义格式:#define(参数(参数1,参数,参数2,参数参数n)4.2.9#define用法用法二二:带参数的宏定义带参数的宏定义void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,FunctionalState NewState)/*Check the
44、 parameters*/assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph);assert_param(IS_FUNCTIONAL_STATE(NewState);if(NewState!=DISABLE)RCC-APB2ENR|=RCC_APB2Periph;else RCC-APB2ENR&=RCC_APB2Periph;STM32标准外设库标准外设库stm32f10 x.h头文件中的头文件中的代码:代码:解析解析:该该函数为外设时钟使能函函数为外设时钟使能函数,第一个参数为要使能的外设,数,第一个参数为要使能的外设,第二个参数为是否使能第二个参
45、数为是否使能。#define IS_FUNCTIONAL_STATE(STATE)(STATE)=DISABLE)|(STATE)=ENABLE)在在stm32f10 x_rcc.c源文件中函数源文件中函数RCC_APB2PeriphClockCmd()使用了使用了IS_FUNCTIONAL_STATE(STATE)这个这个宏宏 RCC_APB2PeriphClockCm()源码源码如下:如下:4.2.10#ifdef、#ifndef、#else、#if条件编译条件编译 只有只有满足一定条件满足一定条件才才进行编译进行编译,一般,一般用在用在头头文件文件或或文件文件开头开头部分部分 嵌入式嵌入
46、式C语言常语言常使用条使用条件编译,通过条件判断件编译,通过条件判断来来确定是否确定是否对某段源程序进对某段源程序进行编译行编译。条件条件编译的编译的用途用途:可以:可以用源程序产生不同版本用源程序产生不同版本。条件编译条件编译说明说明#define宏定义宏定义#undef撤销已定义的宏名撤销已定义的宏名#if条件编译命令,如果条件编译命令,如果#if后面的表达式为后面的表达式为true,则执行,则执行语句语句#ifdef判断某个宏是否被定义,若被定义,则执行语句判断某个宏是否被定义,若被定义,则执行语句#ifndef判断某个宏是否未被定义,若未被定义,则执行语句,判断某个宏是否未被定义,若未
47、被定义,则执行语句,与与#ifdef相反相反#elif#else指令用于指令用于#if指令之后,当指令之后,当#if指令的条件不为真时,指令的条件不为真时,就编译就编译#else后面的代码,后面的代码,elif相当于相当于else if#endif条件编译的结束命令,用在条件编译的结束命令,用在#if、#ifdef、#ifndef之后之后嵌入式嵌入式C语言常用的条件编译语言常用的条件编译命令命令4.2.10#ifdef、#ifndef、#else、#if条件编译条件编译形式一形式一:#ifdef 标识符标识符 程序段程序段1#else 程序段程序段2#endif功能:功能:当指定的标识符当指定
48、的标识符已被已被#define定义过,则定义过,则只编译程序段只编译程序段1,否则编,否则编译程序段译程序段2。#ifdef IN_XXX#define XXX_EXT#else#define XXX_EXT extern#endif.XXX_EXT volatile u16 Name;如果如果定义了定义了IN_XXX,则定义,则定义XXX_EXT,否则定义否则定义XXX_EXT为为extern。4.2.10#ifdef、#ifndef、#else、#if条件编译条件编译形式形式二二:#ifndef 标识符标识符 程序段程序段1#else 程序段程序段2#endif功能功能:当指定的标识符当指
49、定的标识符没有被没有被#define定义过,定义过,则编译程序段则编译程序段1,否则编,否则编译程序段译程序段2。#ifndef STM32F10X_CL#define RCC_USBCLKSource_PLLCLK_1Div5 (uint8_t)0 x00)#define RCC_USBCLKSource_PLLCLK_Div1 (uint8_t)0 x01)#define IS_RCC_USBCLK_SOURCE(SOURCE)(SOURCE)=RCC_USBCLKSource_PLLCLK_1Div5)|(SOURCE)=RCC_USBCLKSource_PLLCLK_Div1)#els
50、e#define RCC_OTGFSCLKSource_PLLVCO_Div3 (uint8_t)0 x00)#define RCC_OTGFSCLKSource_PLLVCO_Div2 (uint8_t)0 x01)#define IS_RCC_OTGFSCLK_SOURCE(SOURCE)(SOURCE)=RCC_OTGFSCLKSource_PLLVCO_Div3)|(SOURCE)=RCC_OTGFSCLKSource_PLLVCO_Div2)#endif标准标准外设库外设库v3.5.0版本中的版本中的stm32f10 x_rcc.h头文件中的头文件中的源码源码4.2.10#ifdef
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。