本帖最后由 南河壹号 于 2023-10-8 20:41 编辑
#申请原创#
GPIO简介 General Purpose Input Output,即通用输入输出端口,简称GPIO 作用:负责采集外部器件的信息或者控制外部器件工作,即输入输出
GPIO特点 - 不同型号,IO口数量可能不一样,可通过数据手册快速查询
- 快速翻转,每次翻转最快只需要两个时钟周期(F1最高速度可以到50Mhz)
- 每个IO口都可以做中断
- 支持8种工作模式
IO端口基本结构 1.保护二极管:用是防止从外部I/O管脚输入的电压过高或者过低造成内部电路损坏。
2.内部上下拉电阻:上拉下拉就是将不确定的信号通过一个电阻嵌位在高低电平,电阻同时起限流作用。
3.施密特触发器:施密特触发器就是一种整形电路,可以将非标准方波整形成方波,如正弦波转方波。
- 当输入电压高于正向阈值电压,输出为高;
- 当输入电压低于负向阈值电压,输出为低;
- 当输入在正负向阈值电压之间,输出不改变
4.P-MOS和N-MOS:MOS管是压控型元件,通过控制栅源电压( Vgs )来实现导通或关闭。
GPIO模式介绍
模拟输入 - 禁止输出缓冲器
- 禁止施密特触发器输入
- 禁止上拉和下拉
- IDATA寄存器值为0
浮空输入 - 禁止输出缓冲器
- 施密特触发器打开
- 禁止上拉和下拉
- IDATA寄存器在每个APB2时钟周期采样I/O脚上的数据
- 对IDATA的读访问可得到I/O的状态
上拉/下拉输入(上拉为例) - 禁止输出缓冲器
- 施密特触发器打开
- 开启上拉
- IDATA寄存器在每个APB2时钟周期采样I/O脚上的数据
- 对IDATA的读访问可得到I/O的状态
推挽输出 - 打开输出缓冲器
- P-MOS和N-MOS都工作
- 施密特触发器打开
- 禁止上拉和下拉
- 在推挽模式时,对ODATA的读访问可得到最后一次写的值
复用推挽/开漏输出(推挽为例) - 内置外设的信号驱动输出缓冲器(复用推挽功能输出)
- 输出缓冲器打开
- P-MOS和N-MOS都工作(P-MOS不工作,N-MOS工作)
- 禁止上拉和下拉
- 施密特触发器打开
- 在推挽模式时,对ODATA的读访问可得到最后一次写的值
- 在开漏模式时,对IDATA的读访问可得到I/O状态
GPIO寄存器 每个GPI/O端口有两个32位配置寄存器(CFGLOW,CFGHIG),两个32位数据寄存器(IDATA和ODATA),一个32位设置/清除寄存器(BSC),一个16位清除寄存器(BC)和一个32位锁定寄存器(LOCK)。
- CFGLOW:CFG配置,LOW低位,加一起就是GPIO端口配置低寄存器,用于配置GPIO模式(输入模式还是输出模式,输出模式还需要配置输出速度),CFGLOW用于配置PIN0-PIN7
- CFGHIG:CFG配置,HIG高位,加一起就是GPIO端口配置高寄存器,用于配置GPIO模式(输入模式还是输出模式,输出模式还需要配置输出速度),CFGHIG用于配置PIN8-PIN15
- IDATA:I输入,DATA数据,加一起就是GPIO端口输入数据寄存器,用于存储PIN0-PIN15的IO输入状态,GPIO输入数据寄存器为只读并只能以字(16位)的形式读出。读出的值为对应I/O口的状态。
- ODATA:O输出,DATA数据,加一起就是GPIO端口输出数据寄存器,用于存储PIN0-PIN15的IO输出状态,GPIO输出数据寄存器可读可写并只能以字(16位)的形式操作。BSC可以分别地对ODATA各个位进行独立的设置/清除。
- BSC:B位,S设置,C清除,加一起就是GPIO端口位设置/清除寄存器,用于设置/清除对应的GPIO端口位,低16位设置,高16位清除,若同时设置和清除同一位,设置起作用。
- BC:B位,C清除,加一起就是GPIO端口位清除寄存器,用于清除对应的GPIO端口位。
- LOCK:LOCK锁定,加起来就是GPIO端口位锁定寄存器,用于保护GPIO 的配置在程序运行期间误修改,低16位用来锁定GPIO端口位,低16锁定之前需要对17位写入一定的序列才可以对低16位进行设置,从而锁定GPIO端口位。
库函数说明
IO端口复用和重映射
IO 端口除了实现通用的输入输出功能外,还能实现作为多种外设功能的接口,为了充分利用外设 I/O 引脚,还支持端口复用功能。既可以在同一个引脚实现多个功能(同一时刻只能实现一个功能),也可以将某一功能重映射至其它 I/O上(原来支持的功能不再支持)。 什么时候需要开AFIO时钟,当使用到外部中断,事件和重映射的时候,需要开启AFIO时钟,使用引脚复用功能时不需要开启AFIO时钟
AFIO寄存器
- EVCTRL:EV事件,CTRL控制,加一起就是AFIO事件控制寄存器,用于是否配置GPIOx_PINx用于事件输出
- REMAP1:REMAP重映射,加一起就是AFIO重映射寄存器1,用于配置功能引脚的重映射
- REMAP2:参考REMAP1
- EINTSEL1:EINT外部中断,SEL选择,加一起就是外部中断选择寄存器1,用于配置的外部中断的输入源GPIOx_PINy(0-3)
- EINTSEL2、3、4:参考EINTSEL1
SysTick定时器
SysTick定时器也叫SysTick滴答定时器,它是Cortex-M3内核的一个外设, 被嵌入在 NVIC 中。它是一个24位向下递减的定时器,每计数一次所需时间为 1/SYSTICK,SYSTICK是系统定时器时钟,它可以直接取自AHB时钟,还可以通过AHB时钟8分频后获取。通常我们使用的是AHB总线上的8分频作为Systick的时钟。
Systick工作流程 Systick时钟我们这里选择AHB时钟8分频,系统时钟设置为72MHz,即每计数一次所需时间为1/(72000000/8)s,也就是1/(72/8)us,就是1us时间内滴答定时器会计数9次,当滴答定时器计数到0时,将从LOAD寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启SysTick中断的话,当定时器计数到0,将产生一个中断信号。因此只要知道计数的次数就可以准确得到它的延时时间。 SysTick定时器通常应用在操作系统中,为其提供时钟周期。
SysTick定时器寄存器介绍 CTRL 寄存器
- ENABLE:用于使能滴答定时器
- TICKINT:用于开启滴答定时器中断
- CLKSOURCE:用于选择滴答定时器时钟源,1是选择AHB时钟,0是选择AHB时钟8分频
- COUNTFLAG:计数标志位,滴答定时器计数到0会硬件置1,读操作会使该位自动清0
LOAD 寄存器 LOAD是SysTick定时器的重装载数值寄存器。因为SysTick定时器是一个24位递减计数器,因此重装载寄存器中只使用到了低24位,即 bit0——bit23。当系统复位时,其值为0
- RELOAD:用于存放重装载计数值,当滴答计数器计数到0,会重新装载该值
VAL 寄存器 VAL 是SysTick定时器的当前数值寄存器
- CURRENT:用于存放滴答定时器当前的倒计数的值,写操作会使之清0,同时还会清除CTRL寄存器中的COUNTFLAG计数标志位。
CALIB 寄存器 CALIB 是 SysTick 定时器的校准数值寄存器
SysTick相关函数介绍
工程介绍
led.h
#ifndef _LED_H_
#define _LED_H_
#include "apm32f10x_rcm.h"
#include "apm32f10x_gpio.h"
#define LED0_PORT GPIOB
#define LED0_PIN GPIO_PIN_5
#define LED0_CLOCK RCM_APB2_PERIPH_GPIOB
#define LED1_PORT GPIOE
#define LED1_PIN GPIO_PIN_5
#define LED1_CLOCK RCM_APB2_PERIPH_GPIOE
#define LED0_ON GPIO_ResetBit(LED0_PORT,LED0_PIN);
#define LED0_OFF GPIO_SetBit(LED0_PORT,LED0_PIN);
#define LED0_TOGGLE GPIO_WriteBitValue(LED0_PORT,LED0_PIN,(uint8_t)(1-GPIO_ReadOutputBit(LED0_PORT,LED0_PIN)));
#define LED1_ON GPIO_ResetBit(LED1_PORT,LED1_PIN);
#define LED1_OFF GPIO_SetBit(LED1_PORT,LED1_PIN);
#define LED1_TOGGLE GPIO_WriteBitValue(LED1_PORT,LED1_PIN,(uint8_t)(1-GPIO_ReadOutputBit(LED1_PORT,LED1_PIN)));
void LED_Init(void);
#endif
led.c
#include "led.h"
void LED_Init(void)
{
//开启LED对应的GPIO时钟
RCM_EnableAPB2PeriphClock(LED0_CLOCK|LED1_CLOCK);
//定义GPIO结构体变量
GPIO_Config_T GPIO_ConfigStructure;
//配置LED0工作模式
GPIO_ConfigStructure.mode=GPIO_MODE_OUT_PP;
GPIO_ConfigStructure.pin=LED0_PIN;
GPIO_ConfigStructure.speed=GPIO_SPEED_50MHz;
GPIO_Config(LED0_PORT,&GPIO_ConfigStructure);
//配置LED1工作模式
GPIO_ConfigStructure.pin=LED1_PIN;
GPIO_Config(LED1_PORT,&GPIO_ConfigStructure);
//设置LED0、LED1默认状态
GPIO_SetBit(LED0_PORT,LED0_PIN);
GPIO_SetBit(LED1_PORT,LED1_PIN);
}
systick.h
#ifndef _SYSTICK_H_
#define _SYSTICK_H_
#include "apm32f10x.h" // Device header
void SYSTICK_Init(uint8_t systickclk);
void delay_us(uint32_t nus);
void delay_ms(u16 nms);
#endif
systick.c
#include "systick.h"
//设置us和ms默认初值
static uint8_t systick_us=0;
static uint16_t systick_ms=0;
void SYSTICK_Init(uint8_t systickclk) //输入参数是系统时钟,单位MHz
{
//SysTick时钟源选择AHB时钟8分频
SysTick_ConfigCLKSource(SYSTICK_CLK_SOURCE_HCLK_DIV8);
//设置1us和1ms计数值
systick_us=systickclk/8;
systick_ms=(uint16_t)(1000*systick_us);
}
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD=nus*systick_us; //设置重装载计数值
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //计数器开始计数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达-COUNTFLAG位为1
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*systick_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //计数器开始计数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达——COUNTFLAG位为1
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
systick实现us计时详解
uint32_t temp;
用于存放CTRL寄存器的值
SysTick->LOAD=nus*systick_us;
设置重装载计数值
SysTick->VAL=0x00;
清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
计数器开始计数,SysTick_CTRL_ENABLE_Msk值为1,SysTick->CTRL与SysTick_CTRL_ENABLE_Msk进行或运算,把bit0设置为1,计数器开始计数
temp=SysTick->CTRL;
把CTRL寄存器的值赋值给temp
while((temp&0x01)&&!(temp&(1<<16)));
循环判断滴答计数器ENABLE使能位和COUNTFLAG计数标志位
1<<16=1 0000 0000 0000 0000
当CTRL寄存器的COUNTFLAG计数标志位为1,temp&(1<<16)为1,!(temp&(1<<16))为0退出循环,表示时间到,
当CTRL寄存器的COUNTFLAG计数标志位为0,temp&(1<<16)为0,!(temp&(1<<16))为1继续循环,表示时间没到,
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick_CTRL_ENABLE_Msk值为1,~SysTick_CTRL_ENABLE_Msk为0,进行与运算结果为0,关闭计数器
SysTick->VAL =0X00;
清空计数器
总结:
1.设置重装载计数值
2.清空计数值
3.滴答计数器使能,即开启计数器
4.循环判断滴答计数器ENABLE使能位和COUNTFLAG计数标志位
5.退出循环,关闭计数器
6.清除计数值
main.c
#include "apm32f10x.h" // Device header
#include "led.h"
#include "systick.h"
int main(void)
{
LED_Init();
SYSTICK_Init(72);
while(1)
{
LED0_TOGGLE;
LED1_TOGGLE;
delay_ms(500);
}
}
实验现象
LED0和LED1隔0.5s闪烁一次
|
赞,来学习一下!