第5章〓通用输入/输出接口 通用输入/输出(GeneralPurpose Input/Output,GPIO)接口是处理器中最简单也是最重要的接口配置,通常也表示为I/O接口。即便如此,GPIO接口也有各种各样的类型和配置选项,即有输入、输出、上拉、下拉以及推挽功能等。软硬件工程师几乎天天和它们打交道,因此深入了解其中的配置,特别是它们的多功能配置和使用方法至关重要。GPIO接口最常见的用途是控制电子设备。例如,无论你期望构建自己的机械臂还是DIY气象站,GPIO接口都可以让你自定义信号,以便它们能正确地操控设备。从本章开始介绍ARM STM32系列处理器的典型外设GPIO接口。以STM32F103ZET6处理器为例,介绍其GPIO接口的工作模式、逻辑结构以及使用方法。 5.1GPIO接口概述 在绝大多数嵌入式系统设计与应用中,都会涉及数字开关量的输入和输出,如常见的状态指示、报警输出、继电器闭合和断开信号输入判断、按钮的开或关状态的读入以及开关控制报警信息的输出等。所有这些开关量的输入和输出控制都必须通过处理器的GPIO接口来实现。随着大规模集成电路的快速发展,为了节约其输入/输出引脚数量,处理器中许多GPIO接口都有复用功能,用户可视需求进行不同的设置和取舍。本章仅介绍STM32F103ZET6处理器GPIO接口的基本输入/输出功能和设置。 STM32F103ZET6处理器拥有112根能承受TTL 5V电压的多功能双向快速引脚(也称为I/O引脚)。每16根引脚划分为一组,分别称为PA、PB、PC、PD和PE、PF、PG等GPIO接口。每组GPIO接口拥有两个32位的配置寄存器(GPIOx_CRL和GPIOx_CRH)、两个32位的数据寄存器(GPIOx_IDR和GPIOx_ODR)、一个32位的置位/复位寄存器(GPIOx_BSRR)、一个16位的复位寄存器(GPIOx_BRR)和一个32位的锁定寄存器(GPIOx_LCKR)来配置该接口的初始状态和功能。每组GPIO接口的每个引脚都有不同的工作模式,可通过软件配置相应的控制寄存器位,可设置成以下几种工作模式。 (1) 输入浮空模式: 浮空模式Floating是设置处理器引脚为输入模式,它既不接高电平,也不接低电平。基于逻辑器件的内部结构,当引脚设置为输入引脚浮空模式时,等于引脚接了一个高电平。因为这种设置易受电磁干扰,所以通常不建议设置引脚为浮空模式。 (2) 输入上拉模式: 上拉就是设置为高电平,即把引脚电压向上拉高,如拉到Vcc或Vdd。上拉就是将不确定的输入信号电平通过一个上拉电阻使引脚的电位钳位在高电平,同时电阻也起限流作用。使用中有强上拉和弱上拉之分,两者仅上拉电阻的阻值大小不同。 (3) 输入下拉模式: 就是把引脚电压拉低到低电平,一般拉到接地GND。 (4) 模拟输入模式: 模拟输入是指模拟量输入模式。数字输入是指输入以1或0表示的高或低电平信号。 (5) 开漏输出模式: 输出端相当于三极管的集电极使用前悬空。要想得到确定的高电平状态,需要外接上拉电阻。此模式适用于电流型的驱动,其引脚灌流电流的能力较强。 (6) 推挽式输出模式: 推挽结构是指两个三极管分别受两个互补信号的控制,在一个三极管导通的时候另一个总是截止,且两个互补三极管工作在极限状态。它的输出电流较大,常用于驱动功率较大的器件。 (7) 推挽式复用功能模式,引脚可设置为多功能引脚。 (8) 开漏复用功能模式,引脚可设置为多功能引脚。 ARM STM32处理器接口复用功能可以被理解为GPIO接口被用于第二功能时的情况,即它们可以被设置为不作为通用的GPIO接口使用。每个接口引脚可以由接口控制寄存器自由编程来设置。控制寄存器设置必须按32位字访问,不允许按半字或字节访问。如GPIOx_BSRR和GPIOx_BRR控制寄存器允许对任何GPIO寄存器的读/更改的独立访问,保证在读和更改访问之间产生中断IRQ时不会发生危险行为。ARM STM32处理器单个I/O引脚的基本结构如图5.1所示,其基本结构包括以下几部分。 图5.1GPIO接口引脚电路设计结构图 1. 输入通道 输入通道如图5.1中上面虚框部分所示。输入通道包括输入数据寄存器和输入驱动器。在GPIO接口引脚电路中设计了两个保护二极管。因为二极管导通电压降为Vd,所以输入到输入驱动器的信号电压范围被钳位在: Vss -Vd00b时,可配置为: 00——通用推挽输出模式。01——通用开漏输出模式。 10——复用功能推挽输出模式。11——复用功能开漏输出模式。 另外,MODEy[1:0],对接口x的模式位(y=0~7),可配置为: 00——输入模式(复位后的状态)。01——输出模式,最大速度为10MHz。 10——输出模式,最大速度为2MHz。11——输出模式,最大速度为50MHz。 I/O引脚工作模式配置如表5.1所示。 表5.1I/O引脚工作模式配置表 配 置 模 式CNF1CNF0MODE1MODE0PxODR寄存器 通用输出 复用功能输出 推挽(PushPull) 开漏(OpenDrain) 推挽(PushPull) 开漏(OpenDrain) 0 1 0 1 0 101: 最大输出速度为10MHz 10: 最大输出速度为2MHz 11: 最大输出速度为50MHz 0或1 0或1 不使用 不使用 输入 模拟输入 浮空输入 下拉输入 上拉输入 0 1 0 1 000 不使用 不使用 0 1 5.3.2x接口配置高寄存器GPIOx_CRH 偏移地址: 0x04,复位值: 0x4444 4444。各位定义如下: 位号31302928272625242322212019181716 定义CNF15 [1:0]MODE15 [1:0]CNF14 [1:0]MODE14 [1:0]CNF13 [1:0]MODE13 [1:0]CNF12 [1:0]MODE12 [1:0] 读写rwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrw 位号1514131211109876543210 定义CNF11 [1:0]MODE11 [1:0]CNF10 [1:0]MODE10 [1:0]CNF9 [1:0]MODE9 [1:0]CNF8 [1:0]MODE8 [1:0] 读写rwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrw CNFy[1:0]——接口x配置位(y=8~15)。 MODEy[1:0]——接口x模式位(y=8~15)。 除y的取值不同,接口配置高寄存器的各位定义和接口配置低寄存器类似。 5.3.3x接口输入/输出数据寄存器GPIOx_IDR和GPIOx_ODR GPIOx_IDR偏移地址: 0x08,复位值: 0x0000 XXXX。各位定义如下: 位号31~1615141312111098 定义保留IDR15IDR14IDR13IDR12IDR11IDR10IDR9IDR8 读写rrrrrrrr 位号76543210 定义IDR7IDR6IDR5IDR4IDR3IDR2IDR1IDR0 读写rrrrrrrr 位[31:16]——保留,始终读为0。 位[15:0]——IDRy[15:0],接口输入数据(y=0~15)。所有位只读,并只能以字(16位)方式的形式读出。读出的值为对应I/O接口引脚的状态。 GPIOx_ODR偏移地址: 0x0C,复位值: 0x0000 0000。各位定义如下: 位号31~1615141312111098 定义保留ODR15ODR14ODR13ODR12ODR11ODR10ODR9ODR8 读写rwrwrwrwrwrwrwrw 位号76543210 定义ODR7ODR6ODR5ODR4ODR3ODR2ODR1ODR0 读写rwrwrwrwrwrwrwrw 位[31:16]——保留,始终读为0。 位[15:0]——ODRy[15: 0],接口输出数据(y=0~15)。 所有位可读可写,并只能以字(16位)的形式操作。 注意: 对GPIOx_BSRR(x=A,B,C,D,E,F,G),可以分别地对各个ODR位进行独立的设置/清除。 5.3.4接口位设置/清除寄存器GPIOx_BSRR 地址偏移: 0x10,复位值: 0x0000 0000。各位定义如下: 位号3130292827262524 定义BR15BR14BR13BR12BR11BR10BR9BR8 读写wwwwwwww 位号2322212019181716 定义BR7BR6BR5BR4BR3BR2BR1BR0 读写wwwwwwww 位号15141312111098 定义BS15BS14BS13BS12BS11BS10BS9BS8 读写wwwwwwww 位号76543210 定义BS7BS6BS5BS4BS3BS2BS1BS0 读写wwwwwwww 位[31:16]——BRy,清除接口x的位y(y=0~15)。所有位只能以字(16位)的形式写入。 清0表示对对应接口的ODRy位不产生影响。置1表示清除对应接口的ODRy位为0。 注意: 如果同时设置了BSy和BRy的对应位,BSy位起作用。 位[15:0]——BSy,设置接口x的位y(y=0~15)。所有位只能以字(16位)的形式写入。 清0表示对对应接口的ODRy位不产生影响。置1表示设置对应接口的ODRy位为1。 5.3.5接口位清除寄存器GPIOx_BRR 偏移地址: 0x14,复位值: 0x0000 0000。各位定义如下: 位号31~1615141312111098 定义保留BR15BR14BR13BR12BR11BR10BR9BR8 读写wwwwwwww 位号76543210 定义BR7BR6BR5BR4BR3BR2BR1BR0 读写wwwwwwww 位[31:16]——保留。 位[15:0]——BRy,清除接口x的位y(y=0~15)。所有位只能以字(16位)的形式写入。 清0表示对对应的ODRy位不产生影响。置 1表示清除对应的ODRy位为0。 5.3.6接口配置锁定寄存器GPIOx_LCKR 该寄存器用来锁定接口位的配置。位[15:0]用于锁定GPIO接口的配置。在规定操作期间,不能改变LCKR[15:0]。对相应的接口位执行LOCK序列后,在下次系统复位之前将不能再更改接口位的值。每个锁定位锁定控制寄存器(CRL,CRH)中相应的4位。 地址偏移: 0x18,复位值: 0x0000 0000。各位定义如下: 位号31~17161514131211109 定义保留LCKK16LCK15LCK14LCK13LCK12LCK11LCK10LCK9 读写rwrwrwrwrwrwrwrw 位号876543210 定义LCK8LCK7LCK6LCK5LCK4LCK3LCK2LCK1LCK0 读写rwrwrwrwrwrwrwrwrw 位[31:17]——保留。 位[16]——LCKK,锁键(Lock Key)。该位可随时读出,仅可通过锁键写入序列修改。 清0表示接口配置锁键未激活。置1表示接口配置锁键被激活,下次系统复位前GPIOx_LCKR寄存器被锁住。 锁键的写入序列: 写1→写0→写1→读0→读1。最后一个读可省略,但可以用来确认锁键已被激活。 注意: 在操作锁键的写入序列时,不能改变LCK[15:0]的值。操作锁键写入序列中的任何错误将不能激活锁键。 位[15:0]——LCKy,接口x的锁位y(y=0~15)。所有位可读可写,但只能在LCKK位为0时写入。清0表示不锁定接口的配置,置 1表示锁定接口的配置。 5.4RCC时钟模块寄存器 任何计算机硬件系统在复位后,首先都要进行初始化工作。其中时钟配置是初始化的一项重要任务。从第3章可看到,在STM32F103ZET6处理器的时钟结构中,如果要使用某个外设,就必须先使能该外设的时钟。时钟配置需要首先考虑系统时钟的来源,即选择使用内部时钟、外部时钟或外部振荡器,以及是否需要锁相环(PLL)等。再考虑内部总线和外部总线,最后考虑外设的时钟信号。通常使用时应先倍频作为处理器的时钟,再使用由内向外分频的原则逐步设置。其时钟配置流程如图5.6所示。 图5.6时钟配置流程图 时钟配置主要是对RCC时钟模块的各相关寄存器进行设置。RCC时钟模块寄存器的首地址是0x40021000,下面逐一详细介绍。 5.4.1时钟控制和配置寄存器RCC_CR和RCC_CFGR 1. 时钟控制寄存器RCC_CR 偏移地址: 0x00,复位值: 0x000 XX83,X代表未定义。 访问方式: 无等待状态。字、半字和字节访问。各位定义如下: 位号31~26252423~2019181716 定义保留PLLRDYPLLON保留CSSONHSEBYPHSERDYHSEON 读写rrwrwrwrrw 位号15~876543210 定义HSICAL[7:0]HSITRIM[4:0]保留HSIRDYHSION 读写rrwrwrwrwrwrrw 位[31:26]——保留,始终读为0。 位[25]——PLLRDY,PLL时钟就绪标志,PLL锁定后由硬件置1。 0: PLL未锁定; 1: PLL锁定。 位[24]——PLLON,PLL使能。由软件置1或清0。当进入待机和停止模式时,该位由硬件清0。当PLL时钟被用作或被选择作为系统时钟时,该位不能被清0。 0: PLL关闭; 1: PLL使能。 位[23:20]——保留,始终读为0。 位[19]——CSSON,时钟安全系统使能。由软件置1或清0以使能时钟监测器。 0: 时钟监测器关闭; 1: 如果外部4~25MHz振荡器就绪,时钟监测器开启。 位[18]——HSEBYP,外部高速时钟旁路。在调试模式下由软件置1或清0来旁路外部晶体振荡器。只有在外部4~25MHz振荡器关闭的情况下,才能写入该位。 0: 外部4~25MHz振荡器没有旁路; 1: 外部4~25MHz外部晶体振荡器被旁路。 位[17]——HSERDY,外部高速时钟就绪标志。由硬件置1来指示外部4~25MHz振荡器已经稳定。在HSEON位清0后,该位需要6个外部4~25MHz振荡器周期清零。 0: 外部4~25MHz振荡器没有就绪; 1: 外部4~25MHz振荡器就绪。 位[16]——HSEON,外部高速时钟使能。由软件置1或清0。当进入待机和停止模式时,该位由硬件清零,关闭4~25MHz外部振荡器。当外部4~25MHz振荡器被用作或被选择作为系统时钟时,该位不能被清0。 0: HSE振荡器关闭; 1: HSE振荡器开启。 位[15:8]——HSICAL[7:0],内部高速时钟校准。在系统启动时,这些位被自动初始化。 位[7:3]——HSITRIM[4:0],内部高速时钟调整。由软件写入来调整内部高速时钟,它们被叠加在HSICAL[5:0]数值上。这些位在HSICAL[7:0]的基础上,让用户可以输入一个调整数值,根据电压和文档的变化调整内部HSI RC振荡器的频率。默认数值为16,可以把HIS调整到8MHz±1%; 每步HSICAL的变化调整约40kHz。 位[2]——保留,始终读为0。 位[1]——HSIRDY,内部高速时钟就绪标准。由硬件置1来指示内部8MHz振荡器已经稳定。 在HSION位清0后,该位需要6个内部8MHz振荡器周期清0。 0: 内部8MHz振荡器没有就绪; 1: 内部8MHz振荡器就绪。 位[0]——HSION,内部高速时钟使能。由软件置1或清0。当从待机或停止模式返回或用作系统时钟的外部4~16MHz振荡器发生故障时,该位由硬件置1来启动内部8MHz的RC振荡器。当内部8MHz振荡器被直接或间接地用作或选择为系统时钟时,该位不能被清0。 0: 内部8MHz振荡器关闭; 1: 内部8MHz振荡器开启。 2. 时钟配置寄存器RCC_CFGR 偏移地址: 0x04,复位值: 0x0000 0000。 访问方式: 0~2个等待周期,可字、半字、字节访问。 只有当访问发生在时钟切换时,才会插入1或2个等待周期。各位定义如下: 位号31~272625242322212019181716 定义保留MCO[2:0]保留USBPREPLLMUL[3:0]PLLXTPREPLLSRC 读写rwrwrwrwrwrwrwrwrwrw 位号1514131211109876543210 定义ADCPRE [1:0]PPRE2[2:0]PPRE1[2:0]HPRE[3:0]SWS [1:0]SW[1:0] 读写rwrwrwrwrwrwrwrwrwrwrwrwrrrwrw 位[31:27]——保留,始终读为0。 位[26:24]——MCO,微控制器时钟输出(Microcontroller Clock Output)。由软件置1或清0。 0xx: 没有时钟输出; 100: 系统时钟(SYSCLK)输出; 101: 内部RC振荡器时钟(HSI,8MHz)输出; 110: 外部振荡器时钟(HSE,4~25MHz)输出; 111: PLL时钟2分频后输出。 注意: 该时钟输出在启动和切换MCO时钟源时可能会被截断。在系统时钟作为输出至MCO引脚时,请保证输出时钟频率不超过50MHz(I/O引脚最高频率)。 位[22]——USBPRE: USB预分频。由软件置1或清0来产生48MHz的USB时钟。在RCC_APB1ENR寄存器中使能USB时钟之前,必须保证该位已经有效。如果USB被使能,该位不能被清0。 0: PLL时钟1.5倍分频作为USB时钟; 1: PLL时钟直接作为USB时钟。 位[21:18]——PLLMUL,PLL倍频系数。由软件设置来确定PLL倍频系数。只有在PLL关闭的情况下才可被写入。注意PLL的输出频率不能超过72MHz。 0000: PLL2倍频输出; 0001: PLL3倍频输出; 0010: PLL4倍频输出; 0011: PLL5倍频输出; 0100: PLL6倍频输出; 0101: PLL7倍频输出; 0110: PLL8倍频输出; 0111: PLL9倍频输出; 1000: PLL10倍频输出; 1001: PLL11倍频输出; 1010: PLL12倍频输出; 1011: PLL13倍频输出; 1100: PLL14倍频输出; 1101: PLL15倍频输出; 1110: PLL16倍频输出; 1111: PLL16倍频输出。 位[17]——PLLXTPRE: HSE分频器作为PLL输入。由软件置1或清0来分频HSE后作为PLL输入时钟。只能在关闭PLL时才能写入此位。 0: HSE不分频; 1: HSE 2分频。 位[16]——PLLSRC: PLL输入时钟源。由软件置1或清0来选择PLL输入时钟源。只能在关闭PLL时才能写入此位。 0: HSI振荡器时钟经2分频后作为PLL输入时钟; 1: HSE时钟作为PLL输入时钟。 位[15:14]——ADCPRE[1:0],ADC预分频由软件置1或清0来确定ADC时钟频率。 00: PCLK2 2分频后作为ADC时钟; 01: PCLK2 4分频后作为ADC时钟; 10: PCLK2 6分频后作为ADC时钟; 11: PCLK2 8分频后作为ADC时钟。 位[13:11]——PPRE2[2:0],高速APB预分频(APB2)。由软件置1或清0来控制APB2时钟(PCLK2)的预分频系数。 0xx: HCLK不分频; 100: HCLK 2分频; 101: HCLK 4分频; 110: HCLK 8分频; 111: HCLK 16分频。 位[10:8]——PPRE1[2:0],低速APB预分频(APB1)。由软件置1或清0来控制低速APB1时钟(PCLK1)的预分频系数。注意: 软件必须保证APB1时钟频率不超过36MHz。 0xx: HCLK不分频;100: HCLK 2分频; 101: HCLK 4分频; 110: HCLK 8分频; 111: HCLK 16分频。 位[7:4]——HPRE[3:0],AHB预分频。由软件置1或清0来控制AHB时钟的预分频系数。 0xxx: SYSCLK不分频; 1000: SYSCLK 2分频; 1001: SYSCLK 4分频; 1010: SYSCLK 8分频; 1011: SYSCLK 16分频; 1100: SYSCLK 64分频; 1101: SYSCLK 128分频; 1110: SYSCLK 256分频; 1111: SYSCLK 512分频。 位[3:2]——SWS[1:0],系统时钟切换状态。由硬件置1或清0来选择系统时钟的时钟源。 00: HSI作为系统时钟; 01: HSE作为系统时钟; 10: PLL输出作为系统时钟; 11: 不可用。 位[1:0]——SW[1:0],系统时钟切换。由软件置1或清0来选择系统时钟源。从停止或待机模式中返回时,或者直接或间接作为系统时钟的HSE在出现故障时,由硬件强制选择HSI作为系统时钟。 00: HSI作为系统时钟; 01: HSE作为系统时钟; 10: PLL输出作为系统时钟; 11: 不可用。 5.4.2时钟中断寄存器RCC_CIR 偏移地址: 0x08,复位值: 0x0000 0000。 访问方式: 无等待周期。可字、半字、字节访问。各位定义如下: 位号31~242322212019181716 定义保留CSSC保留PLLR DYCHSER DYCHISR DYCLSER DYCLSIR DYC 读写wwwwww 位号15~131211109876543210 定义保留PLLR DYIEHSER DYIEHISR DYIELSER DYIELSIR DYIECSSF保留PLLR DYFHSER DYFHISR DYFLSER DYFLSIR DYF 读写rwrwrwrwrwrrrrrr 位[31:24]——保留,始终读为0。 位[23]——CSSC(Clock Security System interrupt Clear),清除始终安全系统中断。由软件置1来清除CSSF安全系统中断标志位CSSF。 0: 无作用;1: 清除CSSF安全系统中断标志位。 位[22:21]——保留,始终读为0。 位[20]——PLLRDYC,清除PLL就绪中断。由软件置1来清除PLL中断就绪标志位PLLRDYE。 0: 无作用; 1: 清除PLL就绪中断标志位PLLRDYE。 位[19]——HSERDYC,清除HSE就绪中断。由软件置1来清除HSE就绪中断标志位HSERDYF。 0: 无作用; 1: 清除HSE就绪中断标志位HSERDYF。 位[18]——HSIRDYC,清除HSI就绪中断。由软件置1来清除HSI就绪中断标志位HSIRDYF。 0: 无作用; 1: 清除HSI就绪中断标志位HSIRDYF。 位[17]——LSERDYC,清除LSE就绪中断。由软件置1来清除LSE就绪中断标志位LSERDYF。 0: 无作用; 1: 清除LSE就绪中断标志位LSERDYF。 位[16]——LSIRDYC,清除LSI就绪中断。由软件置1来清除LSI就绪中断标志位LSIRDYF。 0: 无作用; 1: 清除LSI就绪中断标志位LSIRDYF。 位 [15:13]——保留,始终读为0。 位[12]——PLLRDYIE,PLL就绪中断使能。由软件置1或清0来使能或关闭PLL就绪中断。 0: PLL就绪中断关闭; 1: PLL就绪中断使能。 位[11]——HSERDYIE,HSE就绪中断使能。由软件置1或清0来使能或关闭外部4~16MHz振荡器就绪中断。 0: HSE就绪中断关闭; 1: HSE就绪中断使能。 位[10]——HSIRDYIE,HSI就绪中断使能。由软件置1或清0来使能或关闭内部8MHz RC振荡器就绪中断。 0: HSI就绪中断关闭; 1: HSI就绪中断使能。 位[9]——LSERDYIE,LSE就绪中断使能。由软件置1或清0来使能或关闭外部32kHz RC振荡器就绪中断。 0: LSE就绪中断关闭; 1: LSE就绪中断使能。 位[8]——LSIRDYIE,LSI就绪中断使能。由软件置1或清0来使能或关闭内部40kHz RC振荡器就绪中断。 0: LSI就绪中断关闭; 1: LSI就绪中断使能。 位[7]——CSSF,时钟安全系统中断标志。在外部4~16MHz振荡器始终出现故障时,由硬件置1。由软件通过置1CSSC位来清除。 0: 无HSE时钟失效产生的安全系统中断; 1: HSE时钟失效导致了时钟安全系统中断。 位[6:5]——保留,始终读为0。 位[4]——PLLRDYF,PLL就绪中断标志。在PLL就绪且PLLRDYIE位被置1时,由硬件置1。由软件通过置1PLLRDYC位来清除。 0: 无PLL上锁产生的时钟就绪中断; 1: PLL上锁导致时钟就绪中断。 位[3]——HSERDYF,HSE就绪中断标志。在外部低速时钟就绪且HSERDYIE位被置1时,由硬件置1。由软件通过置1HSERDYC位来清除。 0: 无外部4~16MHz振荡器产生的时钟就绪中断; 1: 外部4~16MHz振荡器导致时钟就绪中断。 位[2]——HSIRDYF,HSI就绪中断标志。在内部高速时钟就绪且HSIRDYIE位被置1时,由硬件置1。由软件通过将HSIRDYC位置1来清除。 0: 无内部8MHz RC振荡器产生的时钟就绪中断; 1: 内部8MHz RC振荡器导致时钟就绪中断。 位[1]——LSERDYF,LSE就绪中断标志。在外部低速时钟就绪且LSERDYIE位被置1时,由硬件置1。由软件通过置1LSERDYC位来清除。 0: 无外部32kHz振荡器产生的时钟就绪中断; 1: 外部32kHz振荡器导致的时钟就绪中断。 位[0]——LSIRDYF,LSI就绪中断标志。在内部时钟就绪且LSIRDYIE位被置1时,由硬件置1。由软件通过置1LSIRDYC位来清除。 0: 无内部40kHz RC振荡器产生的时钟就绪中断; 1: 内部40kHz RC振荡器导致的时钟就绪中断。 5.4.3APB1/2外设复位寄存器RCC_APB1RSTR和RCC_APB2RSTR 1. 外设复位寄存器 RCC_APB1RSTR 偏移地址: 0x10, 复位值: 0x0000 0000。 访问方式: 无等待周期,可字、半字、字节访问。 所有可设置的位都由软件置1或清0。各位定义如下: 位号31~30292827262524 定义保留DACRSTPWRRSTBKPRST保留CANRST保留 读写rwrwrwrw 位号2322212019181716 定义USBRSTI2C2RSTI2C1RSTUART5RSTUART4RSTUSART3RSTUSART2RST保留 读写rwrwrwrwrwrwrw 位号15141312111098 定义SPI3RSTSPI2RST保留WWDGRST保留 读写rwrwrw 位号76543210 定义TIM7RSTTIM6RSTTIM5RSTTIM4RSTTIM3RSTTIM2RST 读写rwrwrwrwrwrw 位[31:30]——保留,始终读为0。 位[29]——DACRST,DAC接口复位。0: 无作用; 1: 复位DAC接口。 位[28]——PWRRST,电源接口复位。0: 无作用; 1: 复位电源接口。 位[27]——BKPRST,备份接口复位。0: 无作用; 1: 复位备份接口。 位[26]——保留,始终读为0。 位[25]——CANRST,CAN复位。0: 无作用; 1: 复位CAN。 位[24]——保留,始终读为0。 位[23]——USBRST,USB复位。0: 无作用; 1: 复位USB。 位[22]——I2C2RST,I2C 2复位。0: 无作用; 1: 复位I2C 2。 位[21]——I2C1RST,I2C 1复位。0: 无作用; 1: 复位I2C 1。 位[20]——UART5RST,UART5复位。0: 无作用; 1: 复位UART5。 位[19]——UART4RST,UART4复位。0: 无作用; 1: 复位UART4。 位[18]——USART3RST,USART3复位。0: 无作用; 1: 复位USART3。 位[17]——USART2RST,USART2复位。0: 无作用; 1: 复位USART2。 位[16]——保留,始终读为0。 位[15]——SPI3RST,SPI3复位。0: 无作用; 1: 复位SPI3。 位[14]——SPI2RST,SPI2复位。0: 无作用; 1: 复位SP12。 位[13:12]——保留,始终读为0。 位[11]——WWDGRST,窗口看门狗复位。0: 无作用; 1: 复位窗口看门狗。 位[10:6]——保留,始终读为0。 位[5]——TIM7RST,定时器7复位。0: 无作用; 1: 复位TIM7定时器。 位[4]——TIM6RST,定时器6复位。0: 无作用; 1: 复位TIM6定时器。 位[3]——TIM5RST,定时器5复位。0: 无作用; 1: 复位TIM5定时器。 位[2]——TIM4RST,定时器4复位。0: 无作用; 1: 复位TIM4定时器。 位[1]——TIM3RST,定时器3复位。0: 无作用; 1: 复位TIM3定时器。 位[0]——TIM2RST,定时器2复位。0: 无作用; 1: 复位TIM2定时器。 2. 外设复位寄存器RCC_APB2RSTR 偏移地址: 0x0C,复位值: 0x0000 0000。 访问方式: 无等待周期,可字、半字和字节访问。 所有可设置的位都可由软件置1或清0。各位定义如下: 位号31~1615141312111098 定义保留ADC3 RSTUSART 1RSTTIM8 RSTSPI1 RSTTIM1 RSTADC2 RSTADC1 RSTIOPG RST 读写rwrwrwrwrwrwrwrw 位号76543210 定义IOPF RSTIOPE RSTIOPD RSTIOPC RSTIOPB RSTIOPA RST保留AFIO RST 读写rwrwrwrwrwrwrw 位[31:16]——保留,始终读为0。 位[15]——ADC3RST,ADC3接口复位。0: 无作用; 1: 复位ADC3接口。 位[14]——USART1RST,USART1复位。0: 无作用; 1: 复位USART1。 位[13]——TIM8RST,TIM8定时器复位。0: 无作用; 1: 复位TIM8定时器。 位[12]——SPI1RST,SPI1复位。0: 无作用; 1: 复位SPI1。 位[11]——TIM1RST,TIM1定时器复位。0: 无作用; 1: 复位TIM1定时器。 位[10]——ADC2RST,ADC2接口复位。0: 无作用; 1: 复位ADC2接口。 位[9]——ADC1RST,ADC1接口复位。0: 无作用; 1: 复位ADC1接口。 位[8]——IOPGRST,I/O接口G复位。0: 无作用; 1: 复位I/O接口G。 位[7]——IOPFRST,I/O接口F复位。0: 无作用; 1: 复位I/O接口F。 位[6]——IOPERST,I/O接口E复位。0: 无作用; 1: 复位I/O接口E。 位[5]——IOPDRST,I/O接口D复位。0: 无作用; 1: 复位I/O接口D。 位[4]——IOPCRST,I/O接口C复位。0: 无作用; 1: 复位I/O接口C。 位[3]——IOPBRST,I/O接口B复位。0: 无作用; 1: 复位I/O接口B。 位[2]——IOPARST,I/O接口A复位。0: 无作用; 1: 复位I/O接口A。 位[1]——保留,始终读为0。 位[0]——AFIORST,辅助功能I/O复位。0: 无作用; 1: 复位辅助功能。 5.4.4AHB外设时钟使能寄存器RCC_AHBENR 偏移地址: 0x14,复位值: 0x0000 0014。 访问方式: 无等待周期,字、半字、字节访问。 所有可设置的位都由软件置1或清零。各位定义如下: 位号31~11109876543210 定义保留SDIOEN保留FSMCEN保留CRCEN保留FLITFEN保留ISRAMENDMA2ENDMA1EN 读写rwrwrwrwrwrwrw 位[31:11]——保留,始终读为0。 位[10]——SDIOEN,SDIO时钟使能。0: SDIO时钟关闭; 1: SDIO时钟开启。 位[9]——保留,始终读为0。 位[8]——FSMCEN,FSMC时钟使能。0: FSMC时钟关闭; 1: FSMC时钟开启。 位[7]——保留,始终读为0。 位[6]——CRCEN,CRC时钟使能。0: CRC时钟关闭; 1: CRC时钟开启。 位[5]——保留,始终读为0。 位[4]——FLITFEN,闪存接口电路时钟使能。 0: 睡眠模式时闪存接口电路时钟关闭; 1: 睡眠模式时闪存接口电路时钟开启。 位[3]——保留,始终读为0。 位[2]——SRAMEN,SRAM时钟使能。 0: 睡眠模式时SRAM时钟关闭;1: 睡眠模式时SRAM时钟开启。 位[1]——DMA2EN,DMA2时钟使能。 0: DMA2时钟关闭; 1: DMA2时钟开启。 位[0]——DMA1EN,DMA1时钟使能。 0: DMA1时钟关闭; 1: DMA1时钟开启。 5.4.5APB1/2外设时钟使能寄存器RCC_APB1ENR和RCC_APB2ENR 1. 外设时钟使能寄存器RCC_APB1ENR 偏移地址: 0x1C,复位值: 0x0000 0000。 访问方式: 通常无访问等待周期,字、半字、字节访问,但APB1总线上的外设被访问时,将插入等待状态直到APB1的外设访问结束。 所有可访问的位都可由软件置1或清0。各位定义如下: 位号31~302928272625242322212019181716 定义保留DAC ENPWR ENBKP EN保留CAN EN保留USB ENI2C 2ENI2C 1ENUART 5ENUART 4ENUSAR T3ENUSAR T2EN保留 读写rwrwrwrwrwrwrwrwrwrwrw 位号1514131211109876543210 定义SPI 3ENSPI 2EN保留WWD GEN保留TIM 7ENTIM 6ENTIM 5ENTIM 4ENTIM 3ENTIM 2EN 读写rwrwrwrwrwrwrwrwrw 位[31:30]——保留,始终读为0。 位[29]——DACEN,DAC接口时钟使能。 0: DAC接口时钟关闭; 1: DAC接口时钟开启。 位[28]——PWREN,电源接口时钟使能。 0: 电源接口时钟关闭; 1: 电源接口时钟开启。 位[27]——BKPEN,备份接口时钟使能。0: 备份接口时钟关闭; 1: 备份接口时钟开启。 位[26]——保留,始终读为0。 位[25]——CANEN,CAN时钟使能。0: CAN时钟关闭; 1: CAN时钟开启。 位[24]——保留,始终读为0。 位[23]——USBEN,USB时钟使能。0: USB时钟关闭; 1: USB时钟开启。 位[22]——I2C2EN,I2C 2时钟使能。0: I2C 2时钟关闭; 1: I2C 2时钟开启。 位[21]——I2C1EN,I2C 1时钟使能。0: I2C 1时钟关闭; 1: I2C 1时钟开启。 位[20]——UART5EN,UART5时钟使能。 0: UART5时钟关闭; 1: UART5时钟开启。 位[19]——UART4EN,UART4时钟使能。 0: UART4时钟关闭; 1: UART4时钟开启。 位[18]——USART3EN,USART3时钟使能。 0: USART3时钟关闭; 1: USART3时钟开启。 位[17]——USART2EN,USART2时钟使能。 0: USART2时钟关闭; 1: USART2时钟开启。 位[16]——保留,始终读为0。 位[15]——SPI3EN,SPI3时钟使能。0: SPI3时钟关闭; 1: SPI3时钟开启。 位[14]——SPI2EN,SPI2时钟使能。0: SPI2时钟关闭; 1: SPI2时钟开启。 位[13:12]——保留,始终读为0。 位[11]——WWDGEN,窗口看门狗时钟使能。 0: 窗口看门狗时钟关闭; 1: 窗口看门狗时钟开启。 位[10:6]——保留,始终读为0。 位[5]——TIM7EN,定时器7时钟使能。0: 定时器7时钟关闭; 1: 定时器7时钟开启。 位[4]——TIM6EN,定时器6时钟使能。0: 定时器6时钟关闭: 1: 定时器6时钟开启。 位[3]——TIM5EN,定时器5时钟使能。0: 定时器5时钟关闭; 1: 定时器5时钟开启。 位[2]——TIM4EN,定时器4时钟使能。0: 定时器4时钟关闭; 1: 定时器4时钟开启。 位[1]——TIM3EN,定时器3时钟使能。0: 定时器3时钟关闭; 1: 定时器3时钟开启。 位[0]——TIM2EN,定时器2时钟使能。0: 定时器2时钟关闭; 1: 定时器2时钟开启。 2. 外设时钟使能寄存器RCC_APB2ENR 偏移地址: 0x18,复位值: 0x0000 000。 访问方式: 通常无访问等待周期,字、半字、字节访问,但APB2总线上的外设被访问时, 将插入等待状态,直到APB2的外设访问结束。 所有可设置的位都由软件置1或清0。各位定义如下: 位号31~1615141312111098 定义保留ADC3ENUSART1ENTIM8ENSPI1ENTIM1ENADC2ENADC1ENIOPGEN 读写rwrwrwrwrwrwrwrw 位号76543210 定义IOPFENIOPEENIOPDENIOPCENIOPBENIOPAEN保留AFIOEN 读写rwrwrwrwrwrwrw 位[31:16]——保留,始终读为0。 位[15]——ADC3EN,ADC3接口时钟使能。 0: ADC3接口时钟关闭; 1: ADC3接口时钟开启。 位[14]——USART1EN,USARTI时钟使能。 0: USART1时钟关闭; 1: USART1时钟开启。 位[13]——TIM8EN,TIM8定时器时钟使能。 0: TIM8定时器时钟关闭; 1: TIM8定时器时钟开启。 位[12]——SPI1EN,SPI1时钟使能。0: SPI1时钟关闭; 1: SPI1 时钟开启。 位[11]——TIM1EN,TIM1定时器时钟使能。 0: TIM1定时器时钟关闭; 1: TIM1定时器时钟开启。 位[10]——ADC2EN,ADC2接口时钟使能。 0: ADC2接口时钟关闭; 1: ADC2接口时钟开启。 位[9]——ADC1EN,ADC1接口时钟使能。 0: ADC1接口时钟关闭; 1: ADC1接口时钟开启。 位[8]——IOPGEN,I/O接口G时钟使能。 0: I/O接口G时钟关闭; 1: I/O接口G时钟开启。 位[7]——IOPFEN,I/O接口F时钟使能。 0: I/O接口F时钟关闭; 1: I/O接口F时钟开启。 位[6]——IOPEEN,I/O接口E时钟使能。 0: I/O接口E时钟关闭; 1: I/O接口E时钟开启。 位[5]——IOPDEN,I/O接口D时钟使能。 0: I/O接口D时钟关闭; 1: I/O接口D时钟开启。 位[4]——IOPCEN,I/O接口C时钟使能。 0: I/O接口C时钟关闭; 1: I/O接口C时钟开启。 位[3]——IOPBEN,I/O接口B时钟使能。 0: I/O接口B时钟关闭; 1: I/O接口B时钟开启。 位[2]——IOPAEN,I/O接口A时钟使能。 0: I/O接口A时钟关闭; 1: I/O接口A时钟开启。 位[1]——保留,始终读为0。 位[0]——AFIOEN,辅助功能I/O时钟使能。 0: 辅助功能I/O时钟关闭; 1: 辅助功能I/O时钟开启。 5.4.6备份域控制寄存器RCC_BDCR 偏移地址: 0x20,复位值: 0x00000000。它只能由备份域复位有效复位。 访问方式: 需要0~3个等待周期,字、半字、字节访问。各位定义如下: 位号31~17161514~10987~3210 定义保留BDRSTRTCEN保留RTCSEL[1:0]保留LSEBYPLSERDYLSEON 读写rwrwrwrwrwrrw 位[31:17]——保留,始终读为0。 位[16]——BDRST,备份域软件复位,由软件置1或清0。 0: 复位未激活; 1: 复位整个备份域。 位[15]——RTCEN,RTC时钟使能,由软件置1或清0。 0: RTC时将关闭; 1: RTC时钟开启。 位[14:10]——保留,始终读为0。 位[9:8]——RTCSEL[1:0],RTC时钟源选择。由软件设置来选择RTC时钟源。一旦RTC时钟源被选定,直到下次后备域被复位,它不能再被改变。可通过设置BDRST位来清除。 00: 无时钟; 01: LSE振荡器作为RTC时钟; 10: LSI振荡器作为RTC时钟; 11: HSE振荡器在128分频后作为RTC时钟。 位[7:3]——保留,始终读为0。 位[2]——LSEBYP,外部低速时钟振荡器旁路。在调试模式下由软件置1或清0来旁路LSE。只有在外部32kHz振荡器关闭时,才能写入该位。 0: LSE时钟未被旁路; 1: LSE时钟被旁路。 位[1]——LSERDY,外部低速LSE就绪。由硬件置1或清零来指示是否外部32kHz振荡器就绪。在LSEON被清0后,该位需要6个外部低速振荡器的周期才被清0。 0: 外部32kHz振荡器未就绪; 1: 外部32kHz振荡器就绪。 位[0]——LSEON,外部低速振荡器使能。由软件置1或清0。 0: 外部32kHz振荡器关闭; 1: 外部32kHz振荡器开启。 5.4.7控制/状态寄存器RCC_CSR 偏移地址: 0x24,复位值: 0x0C00 0000,除复位标志外由系统复位清除,复位标志只能由电源复位清除。 访问方式: 需要0~3个等待周期,字、半字、字节访问。当连续对该寄存器进行访问时,将插入等待状态。各位定义如下: 位号313029282726252423~210 定义LPWR RSTFWWDG RSTFIWDG RSTFSFT RSTFPOR RSTFPIN RSTF保留RMVF保留LSIRDYLSION 读写rwrwrwrwrwrwrwrwrw 位[31]——LPWRRSTF,低功耗复位标志。发生低功耗复位时由硬件置1; 由软件写RMVF位清除。 0: 无低功耗管理复位发生; 1: 发生低功耗管理复位。 位[30]——WWDGRSTF,窗口看门狗复位标志。发生窗口看门狗复位时由硬件置1; 由软件写RMVF位清除。 0: 无窗口看门狗复位发生; 1: 发生窗口看门狗复位。 位[29]——IWDGRSTF,独立看门狗复位标志。发生独立看门狗复位时由硬件置1; 由软件通过写RMVF位清除。 0: 无独立看门狗复位发生; 1: 发生独立看门狗复位。 位[28]——SFTRSTF,软件复位标志。发生软件复位时由硬件置1;由软件写RMVF位清除。 0: 无软件复位发生; 1: 发生软件复位。 位[27]——PORRSTF,上电/掉电复位标志。发生上电/掉电复位时由硬件置1; 由软件写RMVF位清除。 0: 无上电/掉电复位发生; 1: 发生上电/掉电复位。 位[26]——PINRSTF,NRST引脚复位标志。在NRST引脚发生复位时由硬件置1; 由软件写RMVF位清除。 0: 无NRST引脚复位发生; 1: 发生NRST引脚复位。 位[25]——保留,读操作返回0。 位[24]——RMVF,清除复位标志。由软件置1来清除复位标志。 0: 无作用; 1: 清除复位标志。 位[23:2]——保留,读操作返回0。 位[1]——LSIRDY,内部低速振荡器就绪。由硬件置1或清0来指示内部40kHz RC振荡器是否就绪。在LSION清零后,3个内部40kHz RC振荡器的周期后LSIRDY被清0。 0: 内部40kHz RC振荡器时钟未就绪; 1: 内部40kHz RC振荡器时钟就绪。 位[0]——LSION,内部低速振荡器使能。由软件置1或清0。 0: 内部40kHz RC振荡器关闭; 1: 内部40kHz RC振荡器开启。 5.5通用输入输出GPIO接口使用 在使用处理器GPIO接口时, 可按下面流程步骤进行: (1) 配置系统时钟,打开GPIO接口时钟。 (2) 设置GPIO接口的工作模式。 (3) 使用GPIO接口进行输入或输出操作。 在第4章虽然介绍了汇编语言设计,但是现今用汇编语言编写程序已经很少了,仅作为入门学习用。本章将通过具体实例,详细介绍使用C语言操作寄存器和固件库函数两种方式来完成应用程序的设计。 5.5.1利用C语言直接操作寄存器方法访问GPIO方法 【例51】编写C语言程序,利用C语言直接操作外设寄存器,利用第3章实验平台控制系统板上,详见图3.17和图3.63。连接有GPIO PE和PF 接口的发光二极管共12个。请编写控制程序使用GPIO,控制11个LED二极管灯同时点灭,亮0.5s,灭0.5s。 解: 通过查看实验系统(图3.17)和实验板(图3.63), 如要将发光二极管点亮,需要将SW开关拨到ON,给发光二极管提供电源。经过分析当连接LED0~LED11的GPIO PB接口引脚输出低电平0时,发光二极管亮; 输出高电平1时,发光二极管灭。 在使用C语言直接操作STM32处理器寄存器进行开发时,应避免进行重复的系统初始化操作,如设置堆栈和中断向量表等内容。其C语言文件的程序内容如下: # include"stm32f10x. h"//包含STM32F1系列处理器的头文件 void delay_ ms(unsigned short int Number);//延时子函数 # define LED0 (1<<5)//LED接口定义,LED0连接PE8 # define LED1 (1<<6) # define LED2 (1<<0) # define LED3 (1<<1) # define LED4 (1<<2) # define LED5 (1<<3) # define LED6 (1<<4) # define LED7 (1<<5) # define LED8 (1<<6) # define LED9 (1<<7) # define LED10 (1<<8) # define LED11 (1<<12) # define RCC_APB2Periph_GPIOE ((uint32_ t) 0x00000040) # define RCC_APB2Periph_GPIOF ((uint32_ t) 0x00000080) int main(void) { RCC->APB2ENR | = RCC_ APB2Periph_GPIOE//使能GPIOE时钟 GPIOE-> CRL& = 0XFF0FFFFF; GPIOE-> CRL | = 0X00300000;//推挽输出 GPIOE-> ODR |= 1 <<5;//输出高(下面同理) GPIOE-> CRL& = 0XF0FFFFFF; GPIOE-> CRL | = 0X03000000; GPIOE-> ODR |= 1 <<6; RCC->APB2ENR | = RCC_ APB2Periph_GPIOF//使能GPIOF时钟 GPIOF-> CRL& = 0XFFFFFFF0; GPIOF-> CRL | = 0X00000003; GPIOF-> ODR |= 1 <<0; GPIOF-> CRL& = 0XFFFFFF0F; GPIOF-> CRL | = 0X00000030; GPIOF-> ODR |= 1 <<1; GPIOF-> CRL& = 0XFFFFF0FF; GPIOF-> CRL | = 0X00000300; GPIOF-> ODR |= 1 <<2; GPIOF-> CRL& = 0XFFFF0FFF; GPIOF-> CRL | = 0X00003000; GPIOF-> ODR |= 1 <<3; GPIOF-> CRL& = 0XFFF0FFFF; GPIOF-> CRL | = 0X00030000; GPIOF-> ODR |= 1 <<4; GPIOF-> CRL& = 0XFF0FFFFF; GPIOF-> CRL | = 0X00300000; GPIOF-> ODR |= 1 <<5; GPIOF-> CRL& = 0XF0FFFFFF; GPIOF-> CRL | = 0X03000000 GPIOF-> ODR |= 1 <<6; GPIOF-> CRL& = 0X0FFFFFFF; GPIOF-> CRL | = 0X30000000; GPIOF-> ODR |= 1 <<7; GPIOF-> CRH& = 0XFFFFFFF0; GPIOF-> CRH| = 0X00000003; GPIOF-> ODR|= 1 <<8; GPIOF-> CRH& = 0XFFF0FFFF; GPIOF-> CRH| = 0X00030000; GPIOF-> ODR|= 1 <<12; While(1) { GPIOE->ODR|=1 <<5|1 <<6;//PE.5,PE.6输出高 Delay_ms(500) ; GPIOF->ODR|=1 <<0|1 <<1|1 <<2|1 <<3|1 <<4|1 <<5|1 <<6|1 <<7|1 <<8|1 <<12; //PF.0~PF8,PF.12输出高 Delay_ms(500) ; GPIOE-> ODR&= ~(1 << 5);//LED输出低 GPIOE-> ODR&= ~(1 << 6); GPIOF-> ODR&= ~(1 << 0); GPIOF-> ODR&= ~(1 << 1); GPIOF-> ODR&= ~(1 << 2); GPIOF-> ODR&= ~(1 << 3); GPIOF-> ODR&= ~(1 << 4); GPIOF-> ODR&= ~(1 << 5); GPIOF-> ODR&= ~(1 << 6); GPIOF-> ODR&= ~(1 << 7); GPIOF-> ODR&= ~(1 << 8); GPIOF-> ODR&= ~(1 << 12); delay_ ms(500); } } void delay_ ms(unsigned short int Number ) { unsigned int i; while(Number-- ){ i= 12000; while(i-- ); } } 由于在stm32f10x.h文件中定义了STM32F10处理器所有的外设寄存器,因此在C语言程序中,只要包含了这个头文件,就可以省略外设寄存器的定义,直接在用户程序代码中使用即可。读者可能发现,在应用程序中没有进行系统时钟配置。其实系统时钟配置工作已经在SystemInit()函数中实现了,并且默认的系统时钟配置为72MHz。查看启动文件startup_stm32f10x_hd.s可以发现,SystemInit()函数是在main()函数之前调用的。因此在main()函数中,不再需要进行系统时钟配置工作。C语言编程比汇编语言编程相对简单,但是需要开发者查阅相关的寄存器定义进行设置。 5.5.2利用固件库函数方法访问GPIO接口方法 GPIO接口固件库函数具有多个用途,包括引脚设置、单位设置/重置、锁定机制、从接口引脚读入或者向接口引脚写入数据等用途。 5.5.2.1GPIO接口寄存器结构 GPIO接口寄存器结构CPIO_TypeDef和AFIO_TypeDef,在文件stm32f10x.h中定义如下: typedef struct { vu32 CRL;//接口配置低寄存器 vu32 CRH;//接口配置高寄存器 vu32 IDR;//接口输入数据寄存器 vu32 ODR;//接口输出数据寄存器 vu32 BSRR;//接口位设置/复位寄存器 vu32 BRR;//接口位复位寄存器 vu32 LCKR;//接口配置锁定寄存器 } GPIO_TypeDef; typedef struct { vu32 EVCR;//事件控制寄存器 vu32 MAPR;//复用重映射和调试I/O配置寄存器 vu32 EXTICR[4];//外部中断线路0~15配置寄存器 }AFIO_TypeDef; 在文件结构中列出了GPIO接口用所有的寄存器。其中AFIO与GPIO接口的中断和事件有关,如需要可参考第6章。另外,在stm32f10x.h文件中声明了7个GPIO接口外设: ... # define PERIPH_BASE ((u32 )0x40000000) # define APB1PERIPH_BASE PERIPH BASE # define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) # define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) ... # define AFIO_BASE (APB2PERIPH_BASE + 0x0000) # define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) # define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) # define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) # define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) # define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) # define GPIOE_BASE (APB2PER2PH_BASE + 0x1C00) # define GPIOE_BASE (APB2PER2PH_BASE + 0x4001 2000) 5.5.2.2GPIO接口库函数 ARM STM32处理器的固件库提供的GPIO接口库函数包括: GPIO_DeInit()——将外设GPIOx寄存器重设为默认值。 GPIO_AFIODeInit()——将复用功能(重映射事件控制EXT设置)重设为默认值。 GPIO_Init()——根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器。 GPIO_StructInit()——把GPIO_InitStruct中的每个参数按默认值填入。 GPIO_ReadInputDataBit()——读取指定接口引脚的输入。 GPIO_ReadInputData()——读取指定的GPIO接口输入。 GPIO_ReadOutputDataBit()——读取指定接口引脚的输出。 GPIO_ReadOutputData()——读取指定的GPIO接口输出。 GPIO_SetBits()——设置指定的数据接口位。 GPIO_ResetBits()——清除指定的数据接口位。 GPIO_WriteBit()——设置或者清除指定的数据接口位。 GPIO_Write()——向指定GPIO接口写入数据。 GPIO_PinLockConfig()——锁定GPIO引脚设置寄存器。 GPIO_EventOutputCmd()——使能或者除能事件输出。 GPIO_PinRemapConfig()——改变指定引脚的映射。 GPIO_EXTILineConfig()——选择GPIO引脚用作外部中断线路。 下面仅介绍与本例题相关的,与普通输入/输出相关的库函数。其他库函数的使用方法,请查阅相关手册。 1. 函数GPIO_Init() 函数原型: void GPIO_Init(GPIO_TypeDef * GPIOx, GPIO_InitTypeDef * GPI0_InitStruct); 根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择外设GPIO; 输入参数2: GPIO_InitStruct,指向结构GPIO_InitTypeDef的指针,包含了外设GPIO的配置信息,GPIO_InitTypeDef在文件stm32f10x_gpio.h中定义。 typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_ Mode; } GPIO InitTypeDef; 1) GPIO_Pin GPIO_Pin用于选择待设置的GPIO接口引脚,使用操作符“|”可以一次选中多个引脚。可以使用如表5.2所示的任意组合。 表5.2GPIO_Pin可取值 GPIO_Pin可取值描述GPIO_Pin可取值描述 GPIO_Pin_None无引脚被选中GPIO_Pin_8选中引脚 8 GPIO_Pin_0选中引脚 0GPIO_Pin_9选中引脚 9 GPIO_Pin_1选中引脚 1GPIO_Pin_10选中引脚 10 GPIO_Pin_2选中引脚 2GPIO_Pin_11选中引脚 11 GPIO_Pin_3选中引脚 3GPIO_Pin_12选中引脚 12 GPIO_Pin_4选中引脚 4GPIO_Pin_13选中引脚 13 GPIO_Pin_5选中引脚 5GPIO_Pin_14选中引脚 14 GPIO_Pin_6选中引脚 6GPIO_Pin_15选中引脚 15 GPIO_Pin_7选中引脚 7GPIO_Pin_All选中全部引脚 2) GPIO_Speed GPIO_Speed用于设置选中接口引脚的速率。GPIO_Speed可选取的值如表5.3所示。 表5.3GPIO_Speed可取值 GPIO_Speed可取的值描述 GPIO_Speed_10MHz最高输出速率10MHz GPIO_Speed_2MHz最高输出速率2MHz GPIO_Speed_50MHz最高输出速率50MHz 3) GPIO_Mode GPIO_Mode用于设置选中接口引脚的工作模式。GPIO_Mode可选取的值如表5.4所示。 表5.4GPIO_Mode可取值 GPIO_Mode可取的值描述GPIO_Mode可取的值描述 GPIO_Mode_AIN模拟输入GPIO_Mode_Out_OD开漏输出 GPIO_Mode_IN_FLOATING浮空输入GPIO_Mode_Out_PP推挽输出 GPIO_Mode_IPD下拉输入GPIO_Mode_AF_OD复用开漏输出 GPIO_Mode_IPU上拉输入GPIO_Mode_AF_PP复用推挽输出 例如,若需设置PE0接口位模式为推挽输出,最大速率为50MHz,则可以使用下面的代码: GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOE, &GPIO_InitStructure); 2. 函数GPIO_SetBits() 函数原型: void GPIO_SetBits(GPIO_TypeDef* GPIOx, ul6 GPIO_ Pin); 该函数用于将指定的接口位输出高电平。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: GPIO_Pin,带设置的接口位。 例如,若需将PE0和PE1接口位设置输出高电平,则可以使用下面的代码: GPIO_SetBits(GPIO_Pin_0|GPIO_Pin_1); 3. 函数GPIO_ResetBits() 函数原型: void GPIO_ResetBits(GPIO_TypeDef * GPIOx,u16GPIO_Pin); 该函数用于将指定的接口位输出低电平。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: GPIO_Pin,待设置的接口位。 例如,若需将PE0接口位设置为输出低电平,则可以使用下面的代码: GPIO_ResetBits(GPIOE,GPIO_Pin_0); 4. 函数GPIO_WriteBit() 函数原型: void GPIO_WriteBit(GPIO_TypeDef * GPIOx,u16 GPIO_Pin,BitAction BitVal); 该函数用于将指定的接口位设置为高电平或者低电平。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: GPIO_Pin,待设置的接口位。 输入参数3: BitVal,该参数指定了待写入的值。该参数必须取枚举BitAction中的一个值。 Bit_RESET: 将接口位设置为低电平; Bit_SET: 将接口位设置为高电平。 例如,若需将PE8设置为高电平,则可以使用下面的代码: GPIO_WriteBit(GPI0E, GPIO_Pin_8,Bit_SET); 5. 函数GPIO_Write() 函数原型: void GPIO_Write(GPIO_TypeDef * GPIOx, u16 PortVal); 该函数用于向指定GPIO接口写入数据。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: PortVal,待写入接口数据寄存器的值。 例如,若需向PE接口写入0x1101,则可以使用下面的代码: GPIO_ Write(GPIOE,0x1101); 6. 函数GPIO_ReadInputDataBit() 函数原型: Uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef * GPIOx,ul6 GPIO_Pin); 该函数用于读取指定接口引脚的输入。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: GPIO_Pin,待读取的接口位。 返回值: 输入接口引脚值。 例如,若需读取PA0的状态,则可以使用下面的代码: Uint8_t ReadValue; RedValue = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0); 7. 函数GPIO_ReadInputData() 函数原型: u16 GPIO_ReadInputData(GPIO_TypeDef* GPIOx); 该函数用于读取指定的GPIO接口输入。 输入参数: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 返回值: GPIO输入数据接口值。 例如,若需读取GPIOA接口值,则可以使用下面的代码: u16 ReadValue; ReadValue = GPIO_ReadInputData(GPIOA); 8. 函数GPIO_ReadOutputDataBit() 函数原型: u8 GPIO_ReadOutputDataBit(GPIO_TypeDef * GPIOx,u16 GPIO_Pin); 该函数用于读取指定接口引脚的输出。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设; 输入参数2: GPIO_Pin,待读取的接口位。 返回值: 输出接口引脚值。 例如,若需读取PE7接口位的输出,则可以使用下面的代码: u8 ReadValue; ReadValue = GPI0_ReadOutputDataBit(GPIOE,GPI0_Pin_7); 9. 函数GPIO_ReadOutputData() 函数原型: ul6 GPIO_ReadOuputData(GPIO_TypeDef* GPIOx); 该函数用于读取指定的GPIO接口输出。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO不同接口外设。 返回值: GPIO输出数据接口值。 例如,若需读取GPIOE的输出数据,则可以使用下面的代码: u16 ReadValue; ReadValue = GPIO_ReadOutputData(GPIOE); 10. 函数GPIO_PinLockConfig() 函数原型: void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, u16 GPIO_Pin); 该函数用于锁定GPIO引脚设置寄存器。 输入参数1: GPIOx,x可以是A、B、C、D、E、F和G,用来选择GPIO外设; 输入参数2: GPIO_Pin,待锁定的接口位。 例如,若需锁定PA0引脚,则可以使用下面的代码: GPIO_PinLockConfig(GPI0A,GPIO_Pin_0); 下面使用固件库函数,解决例51的设计编程问题,完成同样的功能。 【例52】利用实验平台,可详见图3.17和图3.63。连接有GPIO PE和PF 接口的发光二极管LED共12个。请利用固件库函数编写控制程序使用GPIO接口,控制11个LED灯同时点灭: 亮0.5s和灭0.5s闪烁。 解: 下面介绍利用固件库函数进行GPIO接口应用编程的过程。 按照上面描述的过程,在创建Manage Run_Time Envionment对话框时,选中Device→Startup; 选中CMSIS→CORE。选中Device→StdPeriph Drivers→GPIO,则屏幕下方出现如图5.7所示的提示信息。 图5.7选中Device→StdPeriph Drivers→GPIO时屏幕底部的提示 根据提示,选中StdPeriph Drivers中的Framework和RCC。选中这两项后,屏幕底部不再出现提示信息,说明选择能够满足要求。单击OK按钮进入开发界面。此时可以看到Project视图中包含了如图5.8所示的元素。 图5.8设置固件库函数支持后的Project视图 创建C语言文件ex52.c,并加入到Source Group 1中,然后输入下面的代码: #include "stm32f10x.h"//包含STM32F1系列处理器的头文件 Void delay_ms(unsigned short intNumber);//延时子函数 Int main(void) { GPIO_InitTypeDef GPIO_InitStructure;//声明用于GPIO初始化的结构体 RCC_APB2PeriphClockCmd(RCC_APBPeriph_GPIOE, ENABLE);//使能PE接口时钟 RCC_APB2PeriphClockCmd(RCC_APBPeriph_GPIOF, ENABLE);//使能PF接口时钟 //GPIOE// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//对PE的LED引脚进行设置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//选择推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//频率最高为50MHz GPIO_Init( GPIOE, &GPIO_InitStructure) ;//对引脚进行配置 //GPIOF// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3 GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_12; while(1) { GPIO_SetBits(GPIOE, GPIO_Pin_5|GPIO_Pin_6);//输出高电平 GPIO_SetBits(GPIOF, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_12); delay_ms(500); GPIO_ResetBits(GPIOE, GPIO_Pin_5|GPIO_Pin_6);//输出低电平 GPIO_ResetBits(GPIOF, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_12); delay_ms(500); } } void delay_ms(unsigned short int Number) { unsigned int i; while(Number--){ i = 12000; while(i--); } } 由上述程序代码可以看出,使用固件库函数进行应用程序开发,可以让开发者不必记忆和查阅大量的寄存器名称和符号,编写的代码也更加符合人类的语言习惯,程序更加流畅,提高了应用开发效率。初学读者可以利用本书配套实验板进行反复验证。