1 硬件连接
1.1 mini
1.2 战舰/精英
1.3 探索者
2 F1库函数介绍
头文件:stm32f10x_gpio.h
源文件:stm32f10x_gpio.c
/* 命令功能设置GPIO配置为默认复位状态 ****/
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
/* 初始化和配置功能 *********************************/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
/* GPIO读写功能 **********************************************/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
/* GPIO备用功能配置功能 ****************************/
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
2.1 1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
作用:初始化一个或者多个IO口(同一组)的工作方式和速度。
该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器
GPIOx: GPIOA~GPIOG
typedef struct
{
uint16_t GPIO_Pin; //指定要初始化的IO口
GPIOSpeed_TypeDef GPIO_Speed; //设置IO口输出速度
GPIOMode_TypeDef GPIO_Mode; //设置工作模式:8种中的一个
}GPIO_InitTypeDef;
注意:外设(包括GPIO)在使用之前,几乎都要先使能对应的时钟。
/**
* @摘要 根据指定的GPIO_InitStruct参数初始化GPIOx外围设备。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_InitStruct: 指向GPIO_InitTypeDef结构的指针包含指定GPIO外设的配置信息。
* @返回值 无
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
/*---------------------------- GPIO模式配置 -----------------------*/
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
{
/* 检查参数 */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* 输出模式 */
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
}
/*---------------------------- 对CRL的配置 ------------------------*/
/* 配置8个低端口引脚 */
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
{
tmpreg = GPIOx->CRL;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = ((uint32_t)0x01) << pinpos;
/* 取得端口脚的位置 */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
pos = pinpos << 2;
/* 清除相应的低控制寄存器位 */
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
/* 将模式配置写入相应的位 */
tmpreg |= (currentmode << pos);
/* 重新设置对应的ODR位 */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << pinpos);
}
else
{
/* 设置对应的ODR位 */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
}
}
}
}
GPIOx->CRL = tmpreg;
}
/*---------------------------- GPIO CRH配置 ------------------------*/
/* 配置8个高端口引脚 */
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
tmpreg = GPIOx->CRH;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
/* 取得端口脚的位置 */
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
if (currentpin == pos)
{
pos = pinpos << 2;
/* 清除相应的高控制寄存器位 */
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
/* 将模式配置写入相应的位 */
tmpreg |= (currentmode << pos);
/* 重新设置对应的ODR位 */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
/* 设置对应的ODR位 */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
GPIOx->CRH = tmpreg;
}
}
GPIO_Init函数初始化样例:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
可以一次初始化一个IO组下的多个IO,前提是这些IO口的配置方式一样。
2.2 2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如: GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平 入电平
/**
* @摘要 读取指定的输入端口引脚。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 输入端口引脚值。
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t bitstatus = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
{
bitstatus = (uint8_t)Bit_SET;
}
else
{
bitstatus = (uint8_t)Bit_RESET;
}
return bitstatus;
}
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如:GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平
/**
* @摘要 读取指定的GPIO输入数据端口。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @返回值 GPIO输入数据端口值。
*/
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
return ((uint16_t)GPIOx->IDR);
}
2.3 2个读取输出电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平
/**
* @摘要 读取指定的输出数据端口位。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 输出端口引脚值。
*/
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t bitstatus = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET)
{
bitstatus = (uint8_t)Bit_SET;
}
else
{
bitstatus = (uint8_t)Bit_RESET;
}
return bitstatus;
}
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如:GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平
/**
* @摘要 读取指定的GPIO输出数据端口。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @返回值 GPIO输出数据端口值。
*/
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
return ((uint16_t)GPIOx->ODR);
}
2.4 4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为高电平(1)。
实际操作BSRR寄存器
例如:GPIO_SetBits(GPIOB,GPIO_Pin_5);//设置PB.5 输出高
/**
* @摘要 设置选定的数据端口位。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 无
*/
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为低电平(0)。
实际操作的BRR寄存器。
例如:GPIO_ResetBits(GPIOB,GPIO_Pin_5);//设置PB.5 输出高
/**
* @摘要 清除选定的数据端口位。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 无
*/
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BRR = GPIO_Pin;
}
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
作用:设置某个IO口输出为低电平(0)或者高电平(1)。
实际操作的BRR或BSRR寄存器。
例如:GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_RESET);//设置PB.5 输出低
/**
* @摘要 设置或清除选定的数据端口位。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 GPIO_Pin: 指定要写入的端口位。该参数可以是GPIO_Pin_x中的一个,其中x可以是(0..15)。
* @参数 BitVal: 指定要写入所选位的值。该参数可以是BitAction枚举值之一:
* @arg Bit_RESET: 清除端口引脚
* @arg Bit_SET: 设置端口引脚
* @返回值 无
*/
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_BIT_ACTION(BitVal));
if (BitVal != Bit_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BRR = GPIO_Pin;
}
}
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
作用:设置某组IO口输出为低电平(0)或者高电平(1)。
实际操作的ODR寄存器。
例如:GPIO_Write(GPIOB,Bit_RESET);//设置PB 输出低
/**
* @摘要 将数据写入指定的GPIO数据端口。
* @参数 GPIOx: 其中x可以是(A..G)来选择GPIO外围设备。
* @参数 PortVal: 指定要写入端口输出数据寄存器的值。
* @返回值 无
*/
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
GPIOx->ODR = PortVal;
}
2.5 手把手写跑马灯实验-库函数
(1)使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();
不同的IO组,调用的时钟使能函数不一样。
(2)初始化IO口模式。调用函数GPIO_Init();
(3)操作IO口,输出高低电平。
GPIO_SetBits();
GPIO_ResetBits();
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
delay_init(); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5); //LED0对应引脚GPIOB.5拉低,亮 等同LED0=0;
GPIO_SetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉高,灭 等同LED1=1;
delay_ms(300); //延时300ms
GPIO_SetBits(GPIOB,GPIO_Pin_5); //LED0对应引脚GPIOB.5拉高,灭 等同LED0=1;
GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮 等同LED1=0;
delay_ms(300); //延时300ms
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5
void LED_Init(void);//初始化
#endif
/************led.c************/
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}
2.6 手把手写跑马灯实验-寄存器
(1)使能IO口时钟。配置寄存器RCC_APB2ENR。
(2)初始化IO口模式。配置寄存器GPIOx_CRH/CRL
(3)操作IO口,输出高低电平。配置寄存器GPIOX_ODR或者BSRR/BRR。
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
Stm32_Clock_Init(9);//系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
delay_ms(300);
LED0=1;
LED1=0;
delay_ms(300);
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PBout(5) // DS0
#define LED1 PEout(5) // DS1
void LED_Init(void); //初始化
#endif
/************led.c************/
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
RCC->APB2ENR|=1<<3; //使能PORTB时钟
RCC->APB2ENR|=1<<6; //使能PORTE时钟
GPIOB->CRL&=0XFF0FFFFF;
GPIOB->CRL|=0X00300000;//PB.5 推挽输出
GPIOB->ODR|=1<<5; //PB.5 输出高
GPIOE->CRL&=0XFF0FFFFF;
GPIOE->CRL|=0X00300000;//PE.5推挽输出
GPIOE->ODR|=1<<5; //PE.5输出高
}
2.7 手把手写跑马灯实验-位操作
2.7.1 位操作原理
把每个比特膨胀为一个32位的字,当访问这些字的时候就达到了访问比特的目的,比如说BSRR寄存器有32个位,那么可以映射到32个地址上,我们去访问(读-改-写)这32个地址就达到访问32个比特的目的。我们可以更方便、更快捷、更安全的设置或读取地址中的内容。
地址空间的有一个512MB范围由片上外设(的寄存器)使用。这个区通过系统总线来访问。在这个区的下部,有一个1MB的区间,被称为“位带区”。该位带区还有一个对应的、 32MB 的“位带别名(alias)区”,容纳了 8M 个“位变量”(对比 8051 的只有 128 个位变量)。要注意的是,外设区内不允许执行指令。SRAM区同样有位带区。
位带区: 支持位带操作的地址区
位带别名: 对别名地址的访问最终会变换成对位带区的访问(注意:这中途有一个地址映射过程)
CM3的预定义的存储器映射如下:
对应于 STM32F103ZET6内存映射如下:
可知外设区位带区地址 0x4000 0000 - 0x400F FFFF 基本涵盖了所有的外设寄存器,从而使得读改外设的效率提高。
SRAM 位带区的最低 1MB 范围: 0x20000000 ‐ 0x200FFFFF(SRAM区中的最低 1MB)
片内外设位带区的最低 1MB范围:0x40000000 ‐ 0x400FFFFF(片上外设区中最低 1MB)
下图为位带区和位带别名区的对应关系:
可以发现,位带区首地址:0x200F FFFF - 0x2000 0000 = 0xF FFFF (1Mb个地址) ,而位带别名区: 0x23FF FFFC - 0x2200 0000 = 0x1FF FFFF (32Mb个地址),的确是 1:32。又有 0x2200 0000 - 0x2000 0000 = 0x200 0000 (32Mb),0x2400 0000 - 0x2200 0000 = 0x200 0000 (32Mb),即位带别名区和位带区并非相邻的,而是相隔 31Mb 的内存空间(用作其他用途)。且位带区相互之间对应的位带别名区也不是连续的。
2.7.2 如何位带操作
在位带区中,每个比特都映射到别名地址区的一个字——这是个只有 LSB 才有效的字。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的一个字,再把需要的位右移到 LSB,并把 LSB 返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的 “读-改-写” 过程。
支持位带操作的两个内存区的范围是:
0x2000_0000-0x200F_FFFF(SRAM 区中的最低 1MB)
0x4000_0000-0x400F_FFFF(片上外设区中的最低 1MB)
对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
AliasAddr = 0x42000000 + ((A-0x40000000)*8+n)*4 =0x42000000 + (A-0x40000000)*32 + n*4
上式中,“*4” 表示一个字为 4 个字节,“*8” 表示一个字节中有 8 个比特。
在STM32中,上式可理解为:addr(GPIOx.y) = (addr(GPIO_BASE) + 0x200 0000) + (GPIOx-GPIO_BASE)*32 + y*4
位带别名区的起始地址 外设的偏移地址 1:32 别名区以4字节为步进
( 外设的别名区起始地址 )
上式中,x = (A, B, C, …),y 为 bit 位。GPIO_BASE 为 IO 口的起始地址。
举例:欲设置地址 0x2000_0000 中的比特 2,则使用位带操作的设置过程如下图所示:
对应的汇编代码如下图:
位带读操作相对简单些:
对应的汇编代码如下图:
例子:
1)在地址 0x40000000 处写入 0x3355AACC。
2)读取地址 0x42000008。本次读访问将读取 0x20000000,并提取比特 2,值为 1。
3)往地址 0x42000008 处写 0。本次操作将被映射成对地址 0x40000000 的 “读-改-写” 操作(原子的说法),把比特 2 清 0。
4)现在再读取 0x40000000,将返回 0x3355AAC8(bit[2]已清零)。
位带别名区的字只有 LSB 有意义。另外,在访问位带别名区时,不管使用哪一种长度的数据传送指令(字/半字/字节),都把地址对齐到字的边界上,否则会产生不可预料的结果。
2.7.3 在C编译器中使用位带操作
在 C 编译器中并没有直接支持位带操作。比如, C 编译器并不知道同一块内存能够使用不同的地址来访问,也不知道对位带别名区的访问只对 LSB 有效。欲在 C 中使用位带操作,最简单的做法就是 #define 一个位带别名区的地址。例如:
#define DEVICE_REG0 ((volatile unsigned long *) (0x40000000))
#define DEVICE_REG0_BIT0 ((volatile unsigned long *) (0x42000000))
#define DEVICE_REG0_BIT1 ((volatile unsigned long *) (0x42000004))
...
*DEVICE_REG0 = 0xAB; //使用正常地址访问寄存器
...
*DEVICE_REG0 = *DEVICE_REG0 | 0x2; //使用传统方法设置 bit1
...
*DEVICE_REG0_BIT1 = 0x1; // 通过位带别名地址设置 bit1
为简化位带操作,也可以定义一些宏。比如,我们可以建立一个把 “位带地址+位序号” 转换成别名地址的宏,再建立一个把别名地址转换成指针类型的宏:
// 把 “位带地址+位序号” 转换成 别名地址 的宏
// 外设的基地址 外设地址区域<=1Mb
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5) +(bitnum<<2))
// 把该地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *) (adr))
// 在此基础上,我们就可以如下改写代码:
MEM_ADDR(DEVICE_REG0) = 0xAB; //使用正常地址访问寄存器
MEM_ADDR(DEVICE_REG0)= MEM_ADDR(DEVICE_REG0) | 0x2; //传统做法
MEM_ADDR(BITBAND(DEVICE_REG0,1)) = 0x1; //使用位带别名地址
注意:当使用位带功能时,要访问的变量必须用 volatile 来定义。因为 C 编译器并不知道同一个比特可以有两个地址。所以就要通过 volatile,使得编译器每次都如实地把新数值写入存储器(内存),而不再会出于优化的考虑,在中途使用寄存器来操作数据的复本,直到最后才把复本写回——这会导致按不同的方式访问同一个位会得到不一致的结果。
以GPIO的位带操作为例,sys.h里面对GPIO输入输出部分功能实现了位带操作:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
…
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
GPIOx_ODR 寄存器如下:
每个寄存器32位,占4个地址(4 x 8),在访问或修改某个寄存器时,是从首地址开始的,逻辑运算则是直接可涵盖到32bit,offset 为0x0C。GPIOA 的地址在 stm32 的 datasheet 中给出为 0x40010800。
2.8 手把手写跑马灯实验-位带操作
(1)使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();
(2)初始化IO口模式。调用函数GPIO_Init();
(3)操作IO口,输出高低电平。使用位带操作。
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
delay_ms(300); //延时300ms
LED0=1;
LED1=0;
delay_ms(300); //延时300ms
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5
void LED_Init(void);//初始化
#endif
/************led.c************/
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}
3 F4库函数介绍
头文件:stm32f4xx_gpio.h
源文件:stm32f4xx_gpio.c
/* 命令功能设置GPIO配置为默认复位状态 ****/
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
/* 初始化和配置功能 *********************************/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
/* GPIO读写功能 **********************************************/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
/* GPIO备用功能配置功能 ****************************/
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
3.1 1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO口的4个配置寄存器。
(GPIOx->MODER, GPIOx->OSPEEDR,GPIOx->OTYPER,GPIOx->PUPDR)
GPIOx: GPIOA~GPIOK(最多11组,也就是16X11=176个IO)
typedef struct
{
uint32_t GPIO_Pin; //指定要初始化的端口
GPIOMode_TypeDef GPIO_Mode; //指定要初始化端口模式
GPIOSpeed_TypeDef GPIO_Speed; //指定要初始化端口速度
GPIOOType_TypeDef GPIO_OType; //指定要初始化端口输出类型
GPIOPuPd_TypeDef GPIO_PuPd; //指定要初始化端口上拉或者下拉
}GPIO_InitTypeDef;
注意:外设(包括GPIO)在使用之前,几乎都要先使能对应的时钟。
/**
* @摘要 根据GPIO_InitStruct中的指定参数初始化GPIOx外围设备。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_InitStruct: 指向GPIO_InitTypeDef结构的指针,该结构包含指定GPIO外设的配置信息。
* @返回值 无
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
/* ------------------------- 配置的端口引脚 ---------------- */
/*-- GPIO模式配置 --*/
for (pinpos = 0x00; pinpos < 0x10; pinpos++)
{
pos = ((uint32_t)0x01) << pinpos;
/* Get the port pins position */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
{
/* 检查速度模式参数 */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* 速度模式配置 */
GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
/* 检查输出模式参数 */
assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
/* 输出模式配置 */
GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;
GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
}
/* 上拉下拉电阻配置 */
GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
}
}
}
GPIO_Init函数初始化样例:
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOF9,F10
可以一次初始化一个IO组下的多个IO,前提是这些IO口的配置方式一样。
3.2 2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平
/**
* @摘要 读取指定的输入端口引脚。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 输入端口引脚值。
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t bitstatus = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
{
bitstatus = (uint8_t)Bit_SET;
}
else
{
bitstatus = (uint8_t)Bit_RESET;
}
return bitstatus;
}
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输入电平。
实际操作的是GPIOx_IDR寄存器。
例如:GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平
/**
* @摘要 读取指定的GPIO输入数据端口。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @返回值 GPIO输入数据端口值。
*/
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
return ((uint16_t)GPIOx->IDR);
3.3 2个读取输出电平函数
uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输出电平。
实际操作的是GPIO_ODR寄存器。
例如:GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平
/**
* @摘要 读取指定的输出数据端口位。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin: 指定要读的端口位。该参数可以是GPIO_Pin_x,其中x可以是(0..15)。
* @返回值 输出端口引脚值。
*/
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t bitstatus = 0x00;
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
if (((GPIOx->ODR) & GPIO_Pin) != (uint32_t)Bit_RESET)
{
bitstatus = (uint8_t)Bit_SET;
}
else
{
bitstatus = (uint8_t)Bit_RESET;
}
return bitstatus;
}
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输出电平。
实际操作的是GPIO_ODR寄存器。
例如:GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平
/**
* @摘要 读取指定的GPIO输出数据端口。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @返回值 GPIO输出数据端口值。
*/
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
return ((uint16_t)GPIOx->ODR);
}
3.4 4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为高电平(1)。
实际操作BSRRL寄存器
例如:GPIO_SetBits(GPIOB,GPIO_Pin_5);//设置PB.5输出高
/**
* @摘要 设置选定的数据端口位。
* @注意 这个函数使用GPIOx_BSRR寄存器来允许原子读/修改访问。这样,就不会有读取和修改访问之间发生IRQ的风险。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin:指定要写入的端口位。这个参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
* @返回值 无
*/
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRRL = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为低电平(0)。
实际操作的BSRRH寄存器。
例如:GPIO_ResetBits(GPIOB,GPIO_Pin_5);//设置PB.5输出低
/**
* @摘要 清除选定的数据端口位。
* @注意 这个函数使用GPIOx_BSRR寄存器来允许原子读/修改访问。这样,就不会有读取和修改访问之间发生IRQ的风险。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin: 指定要写入的端口位。这个参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
* @返回值 无
*/
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRRH = GPIO_Pin;
}
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
作用:设置某个IO口输出为低电平(0)或者高电平(1)。
实际操作的BSRRH或BSRRL寄存器。
例如:GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_RESET);//设置PB.5输出低
/**
* @摘要 设置或清除选定的数据端口位。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin: 指定要写入的端口位。这个参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
* @参数 BitVal: 指定要写入所选位的值。该参数可以是BitAction枚举值之一:
* @arg Bit_RESET: 清除端口引脚
* @arg Bit_SET: 设置端口引脚
* @返回值 无
*/
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_BIT_ACTION(BitVal));
if (BitVal != Bit_RESET)
{
GPIOx->BSRRL = GPIO_Pin;
}
else
{
GPIOx->BSRRH = GPIO_Pin ;
}
}
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
作用:设置某组IO口输出为低电平(0)或者高电平(1)。
实际操作的ODR寄存器。
例如:GPIO_Write(GPIOB,Bit_RESET);//设置PB输出低
/**
* @摘要 将数据写入指定的GPIO数据端口。
* @参数 GPIOx: 其中x可以(A..K)为STM32F405xx/407xx和STM32F415xx/417xx设备选择GPIO外设
x可以(A..I)为STM32F42xxx/43xxx设备选择GPIO外设。
x可以为(A, B, C, D和H)选择STM32F401xx设备的GPIO外设。
* @参数 GPIO_Pin: 指定要写入的端口位。这个参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
* @参数 BitVal: 指定要写入所选位的值。该参数可以是BitAction枚举值之一:
* @arg Bit_RESET: 清除端口引脚
* @arg Bit_SET: 设置端口引脚
* @返回值 无
*/
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
/* 检查参数 */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
GPIOx->ODR = PortVal;
}
3.5 手把手写跑马灯实验-库函数
(1)使能IO口时钟。调用函数RCC_AHB1PeriphClockCmd();
不同的外设调用的时钟使能函数可能不一样。
(2)初始化IO口模式。调用函数GPIO_Init();
(3)操作IO口,输出高低电平。
GPIO_SetBits();
GPIO_ResetBits();
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.9拉低,亮 等同LED0=0;
GPIO_SetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
delay_ms(500); //延时300ms
GPIO_SetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.0拉高,灭 等同LED0=1;
GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
delay_ms(500); //延时300ms
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PFout(9) // DS0
#define LED1 PFout(10) // DS1
void LED_Init(void);//初始化
#endif
/************led.c************/
#include "led.h"
//初始化PF9和PF10为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
}
3.6 手把手写跑马灯实验-寄存器
(1)使能IO口时钟。配置相关寄存器寄存器RCC->AHB1ENR
(2)初始化IO口模式。配置四个配置寄存器
GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR、GPIOx_PUPDR
(3)操作IO口,输出高低电平。配置寄存器GPIOX_ODR或者BSRRL/BSRRH。
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIOF->BSRRH=GPIO_Pin_9; //LED0亮
GPIOF->BSRRL=GPIO_Pin_10; //LED1灭
delay_ms(500);
GPIOF->BSRRL=GPIO_Pin_9; //LED0灭
GPIOF->BSRRH=GPIO_Pin_10; //LED1亮
delay_ms(500);
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PFout(9) // DS0
#define LED1 PFout(10) // DS1
void LED_Init(void);//初始化
#endif
/************led.c************/
#include "led.h"
//初始化PF9和PF10为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
RCC->AHB1ENR|=1<<5;//使能PORTF时钟
GPIO_Set(GPIOF,PIN9|PIN10,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);
//PF9,PF10设置
LED0=1;//LED0关闭
LED1=1;//LED1关闭
}
3.7 手把手写跑马灯实验-位操作
(1)使能IO口时钟。调用函数RCC_AHB1PeriphColckCmd();
(2)初始化IO口模式。调用函数GPIO_Init();
(3)操作IO口,输出高低电平。使用位带操作。
/************mian.c************/
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
LED0=0; //LED0亮
LED1=1; //LED1灭
delay_ms(500);
LED0=1; //LED0灭
LED1=0; //LED1亮
delay_ms(500);
}
}
/************led.h************/
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PFout(9) // DS0
#define LED1 PFout(10) // DS1
void LED_Init(void);//初始化
#endif
/************led.c************/
#include "led.h"
//初始化PF9和PF10为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
}