一.C中的内存划分在大多数单片机系统中程序运行时有两类物理存储器FLASH闪存非易失存储代码和只读数据掉电不丢失。RAM(随机存取存储器)易失存储可读写的变量和堆栈掉电丢失。内存通常分为ROM(read only memory)和RAM(random access memory)。其中栈区、堆区以及静态区(ZI 段/RW段)是存在于RAM中的常量区和代码区存在于ROM中。Data数据段:包括RW段与BSS段(ZI段)BSSZI。RW段存放的是已初始化的全局变量和静态变量BSS段存放的是未初始化的全局变量和静态变量。Data段也可称为静态区全局区。RO段:代表只读段存放的是常量数据 如const修饰的全局变量。Code段:存放程序的可执行代码。堆区:用于动态分配内存如malloc函数 所分配的内存存储在堆区由程序员手动管理内存分配和释放。栈区:存放局部变量形参返回地址等由系统自动分配内存和释放内存。二.STM32系统架构图STM32 主系统主要由四个驱动单元和四个被动单元构成。四个驱动单元是内核DCode总线; 系统总线; 通用DMA1; 通用DMA2;四个被动单元是AHB到APB的桥 连接所有的APB设备 内部FlASH闪存 内部SRAM FSMC;① ICode总线该总线将M3内核指令总线和闪存指令接口相连指令的预取在该总线上 面完成。② DCode总线该总线将M3内核的DCode总线与闪存存储器的数据接口相连接常量 加载和调试访问在该总线上面完成。③ 系统总线该总线连接M3内核的系统总线到总线矩阵总线矩阵协调内核和DMA间 访问。④ DMA总线该总线将DMA的AHB主控接口与总线矩阵相连总线矩阵协调CPU的 DCode 和DMA到SRAM,闪存和外设的访问。⑤ 总线矩阵总线矩阵协调内核系统总线和 DMA 主控总线之间的访问仲裁仲裁利用 轮换算法。⑥ AHB/APB桥:这两个桥在AHB和2个APB总线间提供同步连接APB1操作速度限于 36MHz,APB2 操作速度全速。三.STM32 时钟系统在STM32中有五个时钟源为HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为 高速时钟源和低速时钟源。在这5个中HISHSE以及PLL是高速时钟LSI和LSE是低速时钟。从来源可分为外部时钟源和内部时钟源外部时钟源就是从外部通过接晶振的方式获取时钟源其中HSE和LSE是外部时钟源其他的是内部时钟源。下面我们看看STM32的5个时钟源我们讲解顺序是按图中红圈标示的顺序①、HSI是高速内部时钟RC振荡器频率为8MHz。②、HSE 是高速外部时钟可接石英/陶瓷谐振器或者接外部时钟源频率范围为 4MHz~16MHz。我们的开发板接的是8M的晶振。③、LSI是低速内部时钟RC振荡器频率为40kHz。独立看门狗的时钟源只能是LSI同 时LSI还可以作为RTC的时钟源。④、LSE是低速外部时钟接频率为32.768kHz的石英晶体。这个主要是RTC的时钟源。⑤、PLL 为锁相环倍频输出其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为 1182~16 倍但是其输出频率最大不得超过72MHz。上面我们简要概括了STM32 的时钟源那么这5个时钟源是怎么给各个外设以及系统提供时钟的呢 图中我们用A ~E标示我们要讲解的地方。A. B. C. D. E. MCO 是STM32 的一个时钟输出IO(PA8)它可以选择一个时钟信号输出可以 选择为PLL输出的2分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外 部其他系统提供时钟源。 这里是RTC时钟源从图上可以看出RTC的时钟源可以选择LSILSE以及 HSE 的128分频。 从图中可以看出C处USB的时钟是来自PLL时钟源。STM32中有一个全速功能 的USB模块其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能 从PLL输出端获取可以选择为1.5分频或者1分频也就是当需要使用USB 模块时PLL必须使能并且时钟频率配置为48MHz或72MHz。 D处就是STM32的系统时钟SYSCLK它是供STM32中绝大部分部件工作的时 钟源。系统时钟可选择为PLL输出、HSI或者HSE。系统时钟最大频率为72MHz 当然你也可以超频不过一般情况为了系统稳定性是没有必要冒风险去超频的。这里的E处是指其他所有外设了。从时钟图上可以看出其他所有外设的时钟最 终来源都是SYSCLK。SYSCLK 通过AHB分频器分频后送给各模块使用。这些 模块包括①、AHB总线、内核、内存和DMA使用的HCLK时钟。②、通过8分频后送给Cortex的系统定时器时钟也就是systick了。③、直接送给Cortex的空闲运行时钟FCLK。④、送给APB1分频器。APB1分频器输出一路供APB1外设使用(PCLK1最大 频率36MHz)另一路送给定时器(Timer)2、3、4倍频器使用。⑤、送给APB2分频器。APB2分频器分频输出一路供APB2外设使用(PCLK2 最大频率72MHz)另一路送给定时器(Timer)1倍频器使用。其中需要理解的是APB1和APB2的区别APB1上面连接的是低速外设包括电源接口、 备份接口、CAN、USB、I2C1、I2C2、UART2、UART3等等APB2上面连接的是高速外设包 括UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口等。APB2下面所挂的外设的时 钟要比APB1的高。这里总结一下SystemInit()函数中设置的系统时钟大小SYSCLK系统时钟 72MHzAHB总线时钟(使用SYSCLK) 72MHzAPB1总线时钟(PCLK1) 36MHzAPB2总线时钟(PCLK2) 72MHzPLL时钟 72MHz以上关于时钟内容看的不是很明白之后再来继续学习。四.MDK下的C基础1. 位操作C 语言支持如下6中位操作1) 不改变其他位的值的状况下对某几个位进行设值。这个场景单片机开发中经常使用方法就是先对需要设置的位用操作符进行清零操作 然后用|操作符设值。比如我要改变GPIOA的状态,可以先对寄存器的值进行清零操作GPIOA-CRL0XFFFFFF0F;//将第 4-7 位清 0 然后再与需要设置的值进行|或运算GPIOA-CRL|0X00000040;//设置相应位的值不改变其他位的值2) 移位操作提高代码的可读性。移位操作在单片机开发中也非常重要下面让我们看看固件库的GPIO初始化的函数里 面的一行代码GPIOx-BSRR (((uint32_t)0x01) BSRR 0x0030;这样的代码就不好看也不好重用了。 类似这样的代码很多:GPIOA-ODR|15;//PA.5 输出高,不改变其他位这样我们一目了然5告诉我们是第5位也就是第6个端口1告诉我们是设置为1了。3) ~取反操作使用技巧 SR 寄存器的每一位都代表一个状态某个时刻我们希望去设置某一位的值为0同时 其他位都保留为1简单的作法是直接给寄存器设置一个值TIMx-SR0xFFF7这样的作法设置第3位为0但是这样的作法同样不好看并且可读性很差。看看库函数 代码中怎样使用的TIMx-SR (uint16_t)~TIM_FLAG;而TIM_FLAG 是通过宏定义定义的值#define TIM_FLAG_Update ((uint16_t)0x0001)#define TIM_FLAG_CC1 ((uint16_t)0x0002)看这个应该很容易明白可以直接从宏定义中看出TIM_FLAG_Update就是设置的第0位了 可读性非常强。2.define 宏定义define 是C 语言中的预处理命令它用于宏定义可以提高源代码的可读性为编程提供 方便。常见的格式 #define 标识符 字符串 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如 #define SYSCLK_FREQ_72MHz 72000000 定义标识符SYSCLK_FREQ_72MHz的值为72000000。3. ifdef 条件编译单片机程序开发过程中经常会遇到一种情况当满足某条件时对一组语句进行编译而 当条件不满足时则编译另一组语句。条件编译命令最常见的形式为#ifdef 标识符程序段1#else 程序段2#endif它的作用是当标识符已经被定义过(一般是用#define命令定义)则对程序段1进行编译 否则编译程序段2。 其中#else部分也可以没有即#ifdef程序段1#endif五.SYSTEM文件夹介绍1.delay 文件夹代码介绍delay 文件夹内包含了delay.c和delay.h 两个文件这两个文件用来实现系统的延时功能 其中包含7个函数void delay_osschedlock(void);void delay_osschedunlock(void);void delay_ostimedly(u32 ticks);void SysTick_Handler(void);void delay_init(void);void delay_ms(u16 nms);void delay_us(u32 nus);前面4个函数仅在支持操作系统OS的时候需要用到而后面三个函数则不论是 否支持OS都需要用到。 在介绍这些函数之前我们先了解一下delay延时的编程思想CM3内核的处理器内部 包含了一个 SysTick 定时器SysTick 是一个 24 位的倒计数定时器当计数到 0 时将从 RELOAD 寄存器中自动重装载定时初值开始新一轮计数。只要不把它在 SysTick 控制及状 态寄存器中的使能位清除就永不停息。就是利用STM32的内部SysTick来实现延时的这样既不占用中断也不占用系统定时器。2.sys 文件夹代码介绍sys 文件夹内包含了sys.c和sys.h两个文件。在sys.h里面定义了STM32的IO口输入读取 宏定义和输出宏定义。sys.c里面只定义了一个中断分组函数。IO 口的位操作实现该部分代码在sys.h文件中实现对STM32各个IO口的位操作包括读入和输出。当然 在这些函数调用之前必须先进行IO口时钟的使能和IO口功能定义。此部分仅仅对IO口进 行输入输出读取和控制。 位带操作简单的说就是把每个比特膨胀为一个32位的字当访问这些字的时候就达到了 访问比特的目的比如说BSRR寄存器有32个位那么可以映射到32个地址上我们去访问 这32个地址就达到访问32个比特的目的。这样我们往某个地址写1就达到往对应比特位写1 的目的同样往某个地址写0就达到往对应的比特位写0的目的。3.usart 文件夹代码介绍usart 文件夹内包含了 usart.c 和 usart.h 两个文件。这两个文件用于串口的初始化和中断接 收。这里只是针对串口1比如你要用串口2或者其他的串口只要对代码稍作修改就可以了。 usart.c里面包含了2个函数一个是void USART1_IRQHandler(void);另外一个是void uart_init(u32 bound);里面还有一段对串口 printf 的支持代码如果去掉则会导致 printf 无法使用虽然软 件编译不会报错但是硬件上STM32是无法启动的这段代码不要去修改。关于上面这三个文件夹略写后面学到了再去补充。实验一跑马灯实验1.工程结构说明 Start启动与核心层存放芯片启动文件和底层核心代码文件作用startup_stm32f10x_md.s启动文件汇编定义中断向量表、堆栈初始化、程序入口core_cm3.c/hCortex-M3 内核接口提供 NVIC、SysTick 等内核级功能访问stm32f10x.h寄存器头文件定义所有外设寄存器的地址和结构体system_stm32f10x.c/h系统初始化配置时钟树SYSCLK、AHB、APB、PLL 等 Library标准外设库通常存放 ST 官方提供的标准外设库Standard Peripheral Library源文件如stm32f10x_gpio.cstm32f10x_usart.cstm32f10x_rcc.c...截图中该文件夹是折叠状态展开后会有各个外设的驱动源文件。 System系统工具层用户自定义的系统级功能模块文件作用Delay.c/h延时函数实现微秒/毫秒级延时通常基于 SysTick 定时器 User用户应用层存放用户编写的应用代码文件作用main.c主程序入口包含main()函数和用户逻辑stm32f10x_conf.h库配置文件选择需要哪些外设库头文件通过#include控制stm32f10x_it.c/h中断服务程序存放所有外设的中断处理函数如 USART1_IRQHandler、TIM2_IRQHandler 等2. 硬件设计3. 软件设计跑马灯实验我们主要用到的固件库文件是stm32f10x_gpio.c /stm32f10x_gpio.h stm32f10x_rcc.c/stm32f10x_rcc.h misc.c/ misc.h stm32f10x_usart /stm32f10x_usart.h其中stm32f10x_rcc.h 头文件在每个实验中都要引入因为系统时钟配置函数以及相关的外设时 钟使能函数都在这个其源文件 stm32f10x_rcc.c 中。stm32f10x_usart.h 和 misc.h 头文件在我们 SYSTEM文件夹中都需要使用到所以每个实验都会引用。实验代码如下#include stm32f10x.h // Device header#include Delay.hint main(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟//使用各个外设前必须开启时钟否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //GPIO模式赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin GPIO_Pin_All; //GPIO引脚赋值为所有引脚GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //GPIO速度赋值为50MHzGPIO_Init(GPIOA, GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOA的初始化/*主循环循环体内的代码会一直循环执行*/while (1){/*使用GPIO_Write同时设置GPIOA所有引脚的高低电平实现LED流水灯*/GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001PA0引脚为低电平其他引脚均为高电平注意数据有按位取反Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010PA1引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100PA2引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000PA3引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000PA4引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000PA5引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000PA6引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000PA7引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100ms}}大多数 STM32 开发板的 LED 采用低电平点亮共阳极接法GPIO 输出低电平0→ LED 两端有电压差 →LED 亮GPIO 输出高电平1→ LED 两端无电压差 →LED 灭实验二蜂鸣器实验1 蜂鸣器简介蜂鸣器是一种一体化结构的电子讯响器采用直流电压供电广泛应用于计算机、打印机、 复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣 器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。这里的有源不是指电源的“源”而是指有没有自带震荡电路有源蜂鸣器自带了震荡电路 一通电就会发声无源蜂鸣器则没有自带震荡电路必须外部提供2~5Khz 左右的方波驱动 才能发声。2 硬件设计3 软件设计#include stm32f10x.h // Device header#include Delay.hint main(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟//使用各个外设前必须开启时钟否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //GPIO模式赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin GPIO_Pin_0|GPIO_Pin_12; //GPIO引脚赋值为第12号引脚GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //GPIO速度赋值为50MHzGPIO_Init(GPIOB, GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOB的初始化/*主循环循环体内的代码会一直循环执行*/while (1){GPIO_SetBits(GPIOB, GPIO_Pin_0);Delay_ms(100); //将PA0引脚设置为高电平GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平蜂鸣器停止Delay_ms(100); //延时100msGPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平蜂鸣器停止Delay_ms(700);GPIO_ResetBits(GPIOB, GPIO_Pin_0);Delay_ms(100);}}
STM32学习
一.C中的内存划分在大多数单片机系统中程序运行时有两类物理存储器FLASH闪存非易失存储代码和只读数据掉电不丢失。RAM(随机存取存储器)易失存储可读写的变量和堆栈掉电丢失。内存通常分为ROM(read only memory)和RAM(random access memory)。其中栈区、堆区以及静态区(ZI 段/RW段)是存在于RAM中的常量区和代码区存在于ROM中。Data数据段:包括RW段与BSS段(ZI段)BSSZI。RW段存放的是已初始化的全局变量和静态变量BSS段存放的是未初始化的全局变量和静态变量。Data段也可称为静态区全局区。RO段:代表只读段存放的是常量数据 如const修饰的全局变量。Code段:存放程序的可执行代码。堆区:用于动态分配内存如malloc函数 所分配的内存存储在堆区由程序员手动管理内存分配和释放。栈区:存放局部变量形参返回地址等由系统自动分配内存和释放内存。二.STM32系统架构图STM32 主系统主要由四个驱动单元和四个被动单元构成。四个驱动单元是内核DCode总线; 系统总线; 通用DMA1; 通用DMA2;四个被动单元是AHB到APB的桥 连接所有的APB设备 内部FlASH闪存 内部SRAM FSMC;① ICode总线该总线将M3内核指令总线和闪存指令接口相连指令的预取在该总线上 面完成。② DCode总线该总线将M3内核的DCode总线与闪存存储器的数据接口相连接常量 加载和调试访问在该总线上面完成。③ 系统总线该总线连接M3内核的系统总线到总线矩阵总线矩阵协调内核和DMA间 访问。④ DMA总线该总线将DMA的AHB主控接口与总线矩阵相连总线矩阵协调CPU的 DCode 和DMA到SRAM,闪存和外设的访问。⑤ 总线矩阵总线矩阵协调内核系统总线和 DMA 主控总线之间的访问仲裁仲裁利用 轮换算法。⑥ AHB/APB桥:这两个桥在AHB和2个APB总线间提供同步连接APB1操作速度限于 36MHz,APB2 操作速度全速。三.STM32 时钟系统在STM32中有五个时钟源为HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为 高速时钟源和低速时钟源。在这5个中HISHSE以及PLL是高速时钟LSI和LSE是低速时钟。从来源可分为外部时钟源和内部时钟源外部时钟源就是从外部通过接晶振的方式获取时钟源其中HSE和LSE是外部时钟源其他的是内部时钟源。下面我们看看STM32的5个时钟源我们讲解顺序是按图中红圈标示的顺序①、HSI是高速内部时钟RC振荡器频率为8MHz。②、HSE 是高速外部时钟可接石英/陶瓷谐振器或者接外部时钟源频率范围为 4MHz~16MHz。我们的开发板接的是8M的晶振。③、LSI是低速内部时钟RC振荡器频率为40kHz。独立看门狗的时钟源只能是LSI同 时LSI还可以作为RTC的时钟源。④、LSE是低速外部时钟接频率为32.768kHz的石英晶体。这个主要是RTC的时钟源。⑤、PLL 为锁相环倍频输出其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为 1182~16 倍但是其输出频率最大不得超过72MHz。上面我们简要概括了STM32 的时钟源那么这5个时钟源是怎么给各个外设以及系统提供时钟的呢 图中我们用A ~E标示我们要讲解的地方。A. B. C. D. E. MCO 是STM32 的一个时钟输出IO(PA8)它可以选择一个时钟信号输出可以 选择为PLL输出的2分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外 部其他系统提供时钟源。 这里是RTC时钟源从图上可以看出RTC的时钟源可以选择LSILSE以及 HSE 的128分频。 从图中可以看出C处USB的时钟是来自PLL时钟源。STM32中有一个全速功能 的USB模块其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能 从PLL输出端获取可以选择为1.5分频或者1分频也就是当需要使用USB 模块时PLL必须使能并且时钟频率配置为48MHz或72MHz。 D处就是STM32的系统时钟SYSCLK它是供STM32中绝大部分部件工作的时 钟源。系统时钟可选择为PLL输出、HSI或者HSE。系统时钟最大频率为72MHz 当然你也可以超频不过一般情况为了系统稳定性是没有必要冒风险去超频的。这里的E处是指其他所有外设了。从时钟图上可以看出其他所有外设的时钟最 终来源都是SYSCLK。SYSCLK 通过AHB分频器分频后送给各模块使用。这些 模块包括①、AHB总线、内核、内存和DMA使用的HCLK时钟。②、通过8分频后送给Cortex的系统定时器时钟也就是systick了。③、直接送给Cortex的空闲运行时钟FCLK。④、送给APB1分频器。APB1分频器输出一路供APB1外设使用(PCLK1最大 频率36MHz)另一路送给定时器(Timer)2、3、4倍频器使用。⑤、送给APB2分频器。APB2分频器分频输出一路供APB2外设使用(PCLK2 最大频率72MHz)另一路送给定时器(Timer)1倍频器使用。其中需要理解的是APB1和APB2的区别APB1上面连接的是低速外设包括电源接口、 备份接口、CAN、USB、I2C1、I2C2、UART2、UART3等等APB2上面连接的是高速外设包 括UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口等。APB2下面所挂的外设的时 钟要比APB1的高。这里总结一下SystemInit()函数中设置的系统时钟大小SYSCLK系统时钟 72MHzAHB总线时钟(使用SYSCLK) 72MHzAPB1总线时钟(PCLK1) 36MHzAPB2总线时钟(PCLK2) 72MHzPLL时钟 72MHz以上关于时钟内容看的不是很明白之后再来继续学习。四.MDK下的C基础1. 位操作C 语言支持如下6中位操作1) 不改变其他位的值的状况下对某几个位进行设值。这个场景单片机开发中经常使用方法就是先对需要设置的位用操作符进行清零操作 然后用|操作符设值。比如我要改变GPIOA的状态,可以先对寄存器的值进行清零操作GPIOA-CRL0XFFFFFF0F;//将第 4-7 位清 0 然后再与需要设置的值进行|或运算GPIOA-CRL|0X00000040;//设置相应位的值不改变其他位的值2) 移位操作提高代码的可读性。移位操作在单片机开发中也非常重要下面让我们看看固件库的GPIO初始化的函数里 面的一行代码GPIOx-BSRR (((uint32_t)0x01) BSRR 0x0030;这样的代码就不好看也不好重用了。 类似这样的代码很多:GPIOA-ODR|15;//PA.5 输出高,不改变其他位这样我们一目了然5告诉我们是第5位也就是第6个端口1告诉我们是设置为1了。3) ~取反操作使用技巧 SR 寄存器的每一位都代表一个状态某个时刻我们希望去设置某一位的值为0同时 其他位都保留为1简单的作法是直接给寄存器设置一个值TIMx-SR0xFFF7这样的作法设置第3位为0但是这样的作法同样不好看并且可读性很差。看看库函数 代码中怎样使用的TIMx-SR (uint16_t)~TIM_FLAG;而TIM_FLAG 是通过宏定义定义的值#define TIM_FLAG_Update ((uint16_t)0x0001)#define TIM_FLAG_CC1 ((uint16_t)0x0002)看这个应该很容易明白可以直接从宏定义中看出TIM_FLAG_Update就是设置的第0位了 可读性非常强。2.define 宏定义define 是C 语言中的预处理命令它用于宏定义可以提高源代码的可读性为编程提供 方便。常见的格式 #define 标识符 字符串 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如 #define SYSCLK_FREQ_72MHz 72000000 定义标识符SYSCLK_FREQ_72MHz的值为72000000。3. ifdef 条件编译单片机程序开发过程中经常会遇到一种情况当满足某条件时对一组语句进行编译而 当条件不满足时则编译另一组语句。条件编译命令最常见的形式为#ifdef 标识符程序段1#else 程序段2#endif它的作用是当标识符已经被定义过(一般是用#define命令定义)则对程序段1进行编译 否则编译程序段2。 其中#else部分也可以没有即#ifdef程序段1#endif五.SYSTEM文件夹介绍1.delay 文件夹代码介绍delay 文件夹内包含了delay.c和delay.h 两个文件这两个文件用来实现系统的延时功能 其中包含7个函数void delay_osschedlock(void);void delay_osschedunlock(void);void delay_ostimedly(u32 ticks);void SysTick_Handler(void);void delay_init(void);void delay_ms(u16 nms);void delay_us(u32 nus);前面4个函数仅在支持操作系统OS的时候需要用到而后面三个函数则不论是 否支持OS都需要用到。 在介绍这些函数之前我们先了解一下delay延时的编程思想CM3内核的处理器内部 包含了一个 SysTick 定时器SysTick 是一个 24 位的倒计数定时器当计数到 0 时将从 RELOAD 寄存器中自动重装载定时初值开始新一轮计数。只要不把它在 SysTick 控制及状 态寄存器中的使能位清除就永不停息。就是利用STM32的内部SysTick来实现延时的这样既不占用中断也不占用系统定时器。2.sys 文件夹代码介绍sys 文件夹内包含了sys.c和sys.h两个文件。在sys.h里面定义了STM32的IO口输入读取 宏定义和输出宏定义。sys.c里面只定义了一个中断分组函数。IO 口的位操作实现该部分代码在sys.h文件中实现对STM32各个IO口的位操作包括读入和输出。当然 在这些函数调用之前必须先进行IO口时钟的使能和IO口功能定义。此部分仅仅对IO口进 行输入输出读取和控制。 位带操作简单的说就是把每个比特膨胀为一个32位的字当访问这些字的时候就达到了 访问比特的目的比如说BSRR寄存器有32个位那么可以映射到32个地址上我们去访问 这32个地址就达到访问32个比特的目的。这样我们往某个地址写1就达到往对应比特位写1 的目的同样往某个地址写0就达到往对应的比特位写0的目的。3.usart 文件夹代码介绍usart 文件夹内包含了 usart.c 和 usart.h 两个文件。这两个文件用于串口的初始化和中断接 收。这里只是针对串口1比如你要用串口2或者其他的串口只要对代码稍作修改就可以了。 usart.c里面包含了2个函数一个是void USART1_IRQHandler(void);另外一个是void uart_init(u32 bound);里面还有一段对串口 printf 的支持代码如果去掉则会导致 printf 无法使用虽然软 件编译不会报错但是硬件上STM32是无法启动的这段代码不要去修改。关于上面这三个文件夹略写后面学到了再去补充。实验一跑马灯实验1.工程结构说明 Start启动与核心层存放芯片启动文件和底层核心代码文件作用startup_stm32f10x_md.s启动文件汇编定义中断向量表、堆栈初始化、程序入口core_cm3.c/hCortex-M3 内核接口提供 NVIC、SysTick 等内核级功能访问stm32f10x.h寄存器头文件定义所有外设寄存器的地址和结构体system_stm32f10x.c/h系统初始化配置时钟树SYSCLK、AHB、APB、PLL 等 Library标准外设库通常存放 ST 官方提供的标准外设库Standard Peripheral Library源文件如stm32f10x_gpio.cstm32f10x_usart.cstm32f10x_rcc.c...截图中该文件夹是折叠状态展开后会有各个外设的驱动源文件。 System系统工具层用户自定义的系统级功能模块文件作用Delay.c/h延时函数实现微秒/毫秒级延时通常基于 SysTick 定时器 User用户应用层存放用户编写的应用代码文件作用main.c主程序入口包含main()函数和用户逻辑stm32f10x_conf.h库配置文件选择需要哪些外设库头文件通过#include控制stm32f10x_it.c/h中断服务程序存放所有外设的中断处理函数如 USART1_IRQHandler、TIM2_IRQHandler 等2. 硬件设计3. 软件设计跑马灯实验我们主要用到的固件库文件是stm32f10x_gpio.c /stm32f10x_gpio.h stm32f10x_rcc.c/stm32f10x_rcc.h misc.c/ misc.h stm32f10x_usart /stm32f10x_usart.h其中stm32f10x_rcc.h 头文件在每个实验中都要引入因为系统时钟配置函数以及相关的外设时 钟使能函数都在这个其源文件 stm32f10x_rcc.c 中。stm32f10x_usart.h 和 misc.h 头文件在我们 SYSTEM文件夹中都需要使用到所以每个实验都会引用。实验代码如下#include stm32f10x.h // Device header#include Delay.hint main(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟//使用各个外设前必须开启时钟否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //GPIO模式赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin GPIO_Pin_All; //GPIO引脚赋值为所有引脚GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //GPIO速度赋值为50MHzGPIO_Init(GPIOA, GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOA的初始化/*主循环循环体内的代码会一直循环执行*/while (1){/*使用GPIO_Write同时设置GPIOA所有引脚的高低电平实现LED流水灯*/GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001PA0引脚为低电平其他引脚均为高电平注意数据有按位取反Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010PA1引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100PA2引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000PA3引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000PA4引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000PA5引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000PA6引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000PA7引脚为低电平其他引脚均为高电平Delay_ms(100); //延时100ms}}大多数 STM32 开发板的 LED 采用低电平点亮共阳极接法GPIO 输出低电平0→ LED 两端有电压差 →LED 亮GPIO 输出高电平1→ LED 两端无电压差 →LED 灭实验二蜂鸣器实验1 蜂鸣器简介蜂鸣器是一种一体化结构的电子讯响器采用直流电压供电广泛应用于计算机、打印机、 复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣 器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。这里的有源不是指电源的“源”而是指有没有自带震荡电路有源蜂鸣器自带了震荡电路 一通电就会发声无源蜂鸣器则没有自带震荡电路必须外部提供2~5Khz 左右的方波驱动 才能发声。2 硬件设计3 软件设计#include stm32f10x.h // Device header#include Delay.hint main(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟//使用各个外设前必须开启时钟否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //GPIO模式赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin GPIO_Pin_0|GPIO_Pin_12; //GPIO引脚赋值为第12号引脚GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //GPIO速度赋值为50MHzGPIO_Init(GPIOB, GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOB的初始化/*主循环循环体内的代码会一直循环执行*/while (1){GPIO_SetBits(GPIOB, GPIO_Pin_0);Delay_ms(100); //将PA0引脚设置为高电平GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平蜂鸣器停止Delay_ms(100); //延时100msGPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平蜂鸣器停止Delay_ms(700);GPIO_ResetBits(GPIOB, GPIO_Pin_0);Delay_ms(100);}}