3.1 定时器的概述
定时器属于芯片内部中的一个设备,其作用是通过一个计数器实现 计时和定时的功能。内部计数器以一个规定好的计数频率计数规定的个数就可以实现计指定的时间。
计数时间 (s)= 计数个数 (个) / 计数频率 (Hz)
如想要定时5s,就可以让定时器的计数器每100ms计一个数,当计50个数的时候产生一个事件
3.2系统定时器-Systick
Cortex_M处理器内集成了一个小型的名为SysTick(系统节拍)的定时器,它是属于NVIC的一部分,且可以产生SysTick(异常)。SysTic为简单的向下计数的24位计数器,可以使用处理器时钟或外部参考时钟(通常是片上时钟源)
处理器时钟为72MHz 外部参考时钟为 72M/8=9MHz
由计数个数为2^24 可知systick一个周期的最大延时时间为 2^24 / 9000000 约等于 1.86s
3.2.1 systick寄存器
3.3用systick实现ms级延时函数
实现延时xms
时间 = 计数个数 / 计数频率
x*0.001 s = x / 9000000
x= 9000*x(个)
- 时钟选择9MHz
- 重装载值设为 9000*x-1
SysTick->LOAD = 9000*x-1
- 计数器清零
- 启动计数
- 等待计数完成---等待计数完成标志位置位
- 关闭计数器
//基于systick的毫秒级延时函数
//参数:时间
//一个周期x的上限为1864
//若想要实现延时2400ms 4*500 + 400
// 3200 6*500 + 200
void systick_delayms(int x)
{
//1、时钟选择9MHz
SysTick->CTRL &= ~(1<<2);
//2、重装载值设为 9000*x-1
SysTick->LOAD = 9000*x-1;
//3、计数器清零
SysTick->VAL = 0;
//4、启动计数
SysTick->CTRL |= (1<<0);
//5、等待计数完成---等待计数完成标志位置位
// 当标志位为0时循环
while(!(SysTick->CTRL & (1<<16)));
//6、关闭计数器
SysTick->CTRL &= ~(1<<0);
}
3.4 检测按键长按
现将按键消抖部分的延时换成systick
//检测按键
//返回值:0 没有按键按下 1 按键1按下 2 按键2按下 3 按键3按下 4 按键4按下
int key_scan(void)
{
static int state=0;//0--未按下 1--长按
//当按键1按下时---当PA0检测到低电平时
//通过IDR寄存器判断PA0此时的电平,若为低 return 1,否则 return 0
//若PA0检测到低电平,若持续时长大于30ms,则return 1 否则 return 0
if(!(GPIOA->IDR & (1<<0)))
{
//延时40ms
systick_delayms(40);
//若延时结束仍然是低电平则认为该低电平时是真的按键按下引发的
if(!(GPIOA->IDR & (1<<0)))
{
//每隔1ms检测一次按键的状态,检测2000次 共2s
//若2000次内检测到按键松开,则返回短按
//若2000次检测结束,按键仍没有松开,则返回长按
for(int i=0;i<2000;i++)
{
systick_delayms(1);
if(GPIOA->IDR & (1<<0))
{
if(state!=1)
return 1;
else
{
state=0;
return 0;
}
}
}
state=1;
return 5;
}
}
else if(!(GPIOE->IDR & (1<<4)))
{
//延时30ms
systick_delayms(40);
//若延时结束仍然是低电平则认为该低电平时是真的按键按下引发的
if(!(GPIOE->IDR & (1<<4)))
{
while(!(GPIOE->IDR & (1<<4))){}
return 2;
}
}
else if(!(GPIOE->IDR & (1<<3)))
{
//延时30ms
systick_delayms(40);
//若延时结束仍然是低电平则认为该低电平时是真的按键按下引发的
if(!(GPIOE->IDR & (1<<3)))
{
while(!(GPIOE->IDR & (1<<3))){}
return 3;
}
}
else if(GPIOE->IDR & (1<<2))
{
//延时30ms
systick_delayms(40);
//若延时结束仍然是低电平则认为该低电平时是真的按键按下引发的
if(GPIOE->IDR & (1<<2))
{
while(GPIOE->IDR & (1<<2)){}
return 4;
}
}
state=0;
return 0;
}
3.5位带操作
这是由于在存储器中最小的地址单位只能到字节。即每一个字节都有属于自己的地址,但是每个位访问不到。有时候我们想要具体的操作其中某一个位,此时就出现了位带操作。
#ifndef _BITBAND_H_
#define _BITBAND_H_
#include "stm32f10x.h"
//取别名区地址
#define BITBAND(addr,bit) (((addr&0xF0000000)+0x2000000)+((addr&0x000FFFFF)*32)+bit*4)
//对别名区地址取内容
#define MEM_ADDR(addr,bit) (*((volatile unsigned int*)BITBAND(addr,bit)))
//让指定寄存器输出一个数据
#define PAOut(bit) MEM_ADDR((unsigned int)&GPIOA->ODR,bit)
#define PBOut(bit) MEM_ADDR((unsigned int)&GPIOB->ODR,bit)
#define PCOut(bit) MEM_ADDR((unsigned int)&GPIOC->ODR,bit)
#define PDOut(bit) MEM_ADDR((unsigned int)&GPIOD->ODR,bit)
#define PEOut(bit) MEM_ADDR((unsigned int)&GPIOE->ODR,bit)
#define PFOut(bit) MEM_ADDR((unsigned int)&GPIOF->ODR,bit)
#define PAIn(bit) MEM_ADDR((unsigned int)&GPIOA->IDR,bit)
#define PBIn(bit) MEM_ADDR((unsigned int)&GPIOB->IDR,bit)
#define PCIn(bit) MEM_ADDR((unsigned int)&GPIOC->IDR,bit)
#define PDIn(bit) MEM_ADDR((unsigned int)&GPIOD->IDR,bit)
#define PEIn(bit) MEM_ADDR((unsigned int)&GPIOE->IDR,bit)
#define PFIn(bit) MEM_ADDR((unsigned int)&GPIOF->IDR,bit)
#endif
3.6源代码
systick.c
#include "systic.h"
void systic_delayms(int x)
{
int m=x/500;//延时的周期数
int n=x%500;//剩余需要的延时时间
for(int i;i<m;i++)
{
SysTick->CTRL &= ~(1<<2);//选择外部时钟9Mhz
SysTick->LOAD = (9000*500-1);//重装装载值设为(9000x-1)
SysTick->VAL = 0x00;//清空计数器
SysTick->CTRL |= (1<<0);//开始计数,启动
while (!(SysTick->CTRL&(1<<16)));
SysTick->CTRL=0x01;//close
SysTick->VAL=0x00;//clear
}
if(n != 0)
{
SysTick->CTRL &= ~(1<<2);//选择外部时钟9Mhz
SysTick->LOAD = (9000*500-1);//重装装载值设为(9000x-1)
SysTick->VAL = 0x00;//清空计数器
SysTick->CTRL |= (1<<0);//开始计数,启动
while (!(SysTick->CTRL&(1<<16)));
SysTick->CTRL=0x01;//close
SysTick->VAL=0x00;//clear
}
}
systick.h
#ifndef _SYS_H
#define _SYS_H
#include "stm32f10x.h"
void systic_delayms(int x);
#endif
key.c
#include "main.h"
void key_init(void)
{
//时钟激活
RCC->APB2ENR |=(1<<2);//RCC寄存器开启,使能GPIOA
RCC->APB2ENR |=(1<<6);
//将PA0配置为浮空输入
GPIOA->CRL |=(1<<2);
GPIOA->CRL &=~(1<<3);
GPIOA->CRL &=~(1<<1);
GPIOA->CRL &=~(1<<0);
//将PE3配置为浮空输入
GPIOE->CRL |=(1<<14);
GPIOE->CRL &=~(1<<12);
GPIOE->CRL &=~(1<<13);
GPIOE->CRL &=~(1<<15);
//将PE4配置为浮空输入
GPIOE->CRL |=(1<<18);
GPIOE->CRL &=~(1<<16);
GPIOE->CRL &=~(1<<17);
GPIOE->CRL &=~(1<<19);
//将PE2配置为浮空输入
GPIOE->CRL |=(1<<10);
GPIOE->CRL &=~(1<<8);
GPIOE->CRL &=~(1<<9);
GPIOE->CRL &=~(1<<11);
}
//检测按键是否按下:按下返回1 没按0
int key_scan(void)
{
static int state=0;//0未按下;1按下
if(!(GPIOA->IDR&(1<<0)))
{
systic_delayms(50);
if(!(GPIOA->IDR&(1<<0)))
{
//每隔一秒检测一次按键状态,检测2000次,共2s;
//若2000次内检测到按键松开,则返回短按;
//若2000次检测结束,按键仍没有松开,则返回长按;
for(int i=0;i<2000;i++)
{
systic_delayms(50);
if((GPIOA->IDR & (1<<0)))
{
if(state!=1)
return 1;
else
return 0;
}
}
state=1;
return 5;
} //当前现象:短按正常检测,长安可以检测到,但结束时会在引发一次短按;
}
if(!(GPIOE->IDR&(1<<4)))
{
systic_delayms(20);
if(!(GPIOE->IDR&(1<<4)))
{
while(!(GPIOA->IDR & (1<<0))){}
return 4;
}
}
if(!(GPIOE->IDR&(1<<3)))
{
systic_delayms(20);
if(!(GPIOE->IDR&(1<<3)))
{
while(!(GPIOA->IDR & (1<<0))){}
return 3;
}
}
if((GPIOE->IDR&(1<<2)))
{
systic_delayms(20);
if((GPIOE->IDR&(1<<2)))
{
while(!(GPIOA->IDR & (1<<0))){}
return 2;
}
}
state=0;
return 0;
}
bitband.h
#ifndef _BITBAND_H_
#define _BITBAND_H_
#include "stm32f10x.h"
//取别名地址
#define BITBAND(addr,bit) (((addr&0xF000000)+0X2000000)+((addr&0x000FFFFF)*32)+bit*4)
//对别名地址取内容
#define MEM_ADDR(addr,bit) (*((volatile unsigned int*)BITBAND(addr,bit)))
//让指定寄存器输出一个数据
#define PA0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PB0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PC0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PD0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PE0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PF0ut(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PAIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PBIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PCIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PDIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PEIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#define PFIn(bit) MEM_ADDR((unsigned int)&GPIO->ODR,bit)
#endif