打印
[APM32F1]

APM32F103入门1——GPIO和SYSTICK

[复制链接]
1304|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 南河壹号 于 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闪烁一次



使用特权

评论回复
评论
forgot 2023-10-12 11:46 回复TA
赞,来学习一下! 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

7

帖子

0

粉丝