[APM32F1] APM32F103入门1——GPIO和SYSTICK

[复制链接]
 楼主| 南河壹号 发表于 2023-10-8 16:37 | 显示全部楼层 |阅读模式
本帖最后由 南河壹号 于 2023-10-8 20:41 编辑

#申请原创#
GPIO简介
General Purpose Input Output,即通用输入输出端口,简称GPIO
作用:负责采集外部器件的信息或者控制外部器件工作,即输入输出
1.png

GPIO特点
  • 不同型号,IO口数量可能不一样,可通过数据手册快速查询
  • 快速翻转,每次翻转最快只需要两个时钟周期(F1最高速度可以到50Mhz)
  • 每个IO口都可以做中断
  • 支持8种工作模式



IO端口基本结构
1.保护二极管:用是防止从外部I/O管脚输入的电压过高或者过低造成内部电路损坏。
2.内部上下拉电阻:上拉下拉就是将不确定的信号通过一个电阻嵌位在高低电平,电阻同时起限流作用。
3.施密特触发器:施密特触发器就是一种整形电路,可以将非标准方波整形成方波,如正弦波转方波。
  • 当输入电压高于正向阈值电压,输出为高;
  • 当输入电压低于负向阈值电压,输出为低;
  • 当输入在正负向阈值电压之间,输出不改变
4.P-MOS和N-MOS:MOS管是压控型元件,通过控制栅源电压( Vgs )来实现导通或关闭。
2.png

GPIO模式介绍 18.png

模拟输入
  • 禁止输出缓冲器
  • 禁止施密特触发器输入
  • 禁止上拉和下拉
  • IDATA寄存器值为0

3.png


浮空输入
  • 禁止输出缓冲器
  • 施密特触发器打开
  • 禁止上拉和下拉
  • IDATA寄存器在每个APB2时钟周期采样I/O脚上的数据
  • 对IDATA的读访问可得到I/O的状态

4.png


上拉/下拉输入(上拉为例)
  • 禁止输出缓冲器
  • 施密特触发器打开
  • 开启上拉
  • IDATA寄存器在每个APB2时钟周期采样I/O脚上的数据
  • 对IDATA的读访问可得到I/O的状态

5.png


推挽输出
  • 打开输出缓冲器
  • P-MOS和N-MOS都工作
  • 施密特触发器打开
  • 禁止上拉和下拉
  • 在推挽模式时,对ODATA的读访问可得到最后一次写的值

6.png

复用推挽/开漏输出(推挽为例)
  • 内置外设的信号驱动输出缓冲器(复用推挽功能输出)
  • 输出缓冲器打开
  • P-MOS和N-MOS都工作(P-MOS不工作,N-MOS工作)
  • 禁止上拉和下拉
  • 施密特触发器打开
  • 在推挽模式时,对ODATA的读访问可得到最后一次写的值
  • 在开漏模式时,对IDATA的读访问可得到I/O状态

7.png

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端口位。



库函数说明 17.png



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的时钟。
8.png

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

9.png

LOAD 寄存器
LOAD是SysTick定时器的重装载数值寄存器。因为SysTick定时器是一个24位递减计数器,因此重装载寄存器中只使用到了低24位,即 bit0——bit23。当系统复位时,其值为0
  • RELOAD:用于存放重装载计数值,当滴答计数器计数到0,会重新装载该值

10.png



VAL 寄存器
VAL 是SysTick定时器的当前数值寄存器
  • CURRENT:用于存放滴答定时器当前的倒计数的值,写操作会使之清0,同时还会清除CTRL寄存器中的COUNTFLAG计数标志位。

11.png

CALIB 寄存器
CALIB 是 SysTick 定时器的校准数值寄存器
12.png

SysTick相关函数介绍
16.png




工程介绍
led.h
  1. #ifndef _LED_H_
  2. #define _LED_H_

  3. #include "apm32f10x_rcm.h"
  4. #include "apm32f10x_gpio.h"


  5. #define                LED0_PORT                GPIOB
  6. #define                LED0_PIN                GPIO_PIN_5
  7. #define                LED0_CLOCK                RCM_APB2_PERIPH_GPIOB


  8. #define                LED1_PORT                GPIOE
  9. #define                LED1_PIN                GPIO_PIN_5
  10. #define                LED1_CLOCK                RCM_APB2_PERIPH_GPIOE

  11. #define                LED0_ON                        GPIO_ResetBit(LED0_PORT,LED0_PIN);
  12. #define                LED0_OFF                GPIO_SetBit(LED0_PORT,LED0_PIN);
  13. #define                LED0_TOGGLE                GPIO_WriteBitValue(LED0_PORT,LED0_PIN,(uint8_t)(1-GPIO_ReadOutputBit(LED0_PORT,LED0_PIN)));

  14. #define                LED1_ON                        GPIO_ResetBit(LED1_PORT,LED1_PIN);
  15. #define                LED1_OFF                GPIO_SetBit(LED1_PORT,LED1_PIN);
  16. #define                LED1_TOGGLE                GPIO_WriteBitValue(LED1_PORT,LED1_PIN,(uint8_t)(1-GPIO_ReadOutputBit(LED1_PORT,LED1_PIN)));




  17. void LED_Init(void);


  18. #endif

led.c
  1. #include "led.h"


  2. void LED_Init(void)
  3. {
  4.         //开启LED对应的GPIO时钟
  5.         RCM_EnableAPB2PeriphClock(LED0_CLOCK|LED1_CLOCK);
  6.         
  7.         //定义GPIO结构体变量
  8.         GPIO_Config_T GPIO_ConfigStructure;
  9.         
  10.         //配置LED0工作模式
  11.         GPIO_ConfigStructure.mode=GPIO_MODE_OUT_PP;
  12.         GPIO_ConfigStructure.pin=LED0_PIN;
  13.         GPIO_ConfigStructure.speed=GPIO_SPEED_50MHz;
  14.         GPIO_Config(LED0_PORT,&GPIO_ConfigStructure);
  15.         
  16.         //配置LED1工作模式
  17.         GPIO_ConfigStructure.pin=LED1_PIN;
  18.         GPIO_Config(LED1_PORT,&GPIO_ConfigStructure);
  19.         
  20.         //设置LED0、LED1默认状态
  21.         GPIO_SetBit(LED0_PORT,LED0_PIN);
  22.         GPIO_SetBit(LED1_PORT,LED1_PIN);
  23. }


systick.h
  1. #ifndef _SYSTICK_H_
  2. #define _SYSTICK_H_

  3. #include "apm32f10x.h"                  // Device header

  4. void SYSTICK_Init(uint8_t systickclk);
  5. void delay_us(uint32_t nus);
  6. void delay_ms(u16 nms);


  7. #endif


systick.c
  1. #include "systick.h"

  2. //设置us和ms默认初值
  3. static uint8_t systick_us=0;
  4. static uint16_t systick_ms=0;


  5. void SYSTICK_Init(uint8_t systickclk)        //输入参数是系统时钟,单位MHz
  6. {
  7.         //SysTick时钟源选择AHB时钟8分频
  8.         SysTick_ConfigCLKSource(SYSTICK_CLK_SOURCE_HCLK_DIV8);
  9.         
  10.         //设置1us和1ms计数值
  11.         systick_us=systickclk/8;        
  12.         systick_ms=(uint16_t)(1000*systick_us);
  13. }

  14. void delay_us(uint32_t nus)
  15. {
  16.         uint32_t temp;
  17.         SysTick->LOAD=nus*systick_us;                                //设置重装载计数值
  18.         SysTick->VAL=0x00;                                                         //清空计数器
  19.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //计数器开始计数
  20.         do
  21.         {
  22.                 temp=SysTick->CTRL;
  23.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达-COUNTFLAG位为1
  24.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  25.         SysTick->VAL =0X00;                                              //清空计数器         
  26. }

  27. void delay_ms(u16 nms)
  28. {                                    
  29.         u32 temp;                  
  30.         SysTick->LOAD=(u32)nms*systick_ms;                        //时间加载(SysTick->LOAD为24bit)
  31.         SysTick->VAL =0x00;                                                        //清空计数器
  32.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //计数器开始计数
  33.         do
  34.         {
  35.                 temp=SysTick->CTRL;
  36.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达——COUNTFLAG位为1   
  37.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  38.         SysTick->VAL =0X00;                                               //清空计数器                     
  39. }


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
  1. #include "apm32f10x.h"                  // Device header
  2. #include "led.h"
  3. #include "systick.h"


  4. int main(void)
  5. {
  6.         LED_Init();
  7.         SYSTICK_Init(72);
  8.         
  9.         while(1)
  10.         {
  11.                 LED0_TOGGLE;
  12.                 LED1_TOGGLE;
  13.                 delay_ms(500);
  14.         }
  15. }



实验现象
LED0和LED1隔0.5s闪烁一次



评论

赞,来学习一下!  发表于 2023-10-12 11:46
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

7

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部