目录
一、Keil5程序编写与思路
1. 程序思路:
通过寄存器配置使能GPIOA的8个端口,并且设置为推挽输出且50Mhz的输出速度,通过软 件延时的方法,采用共阴极接法,8个led灯阴极全部接地,阳极分别挤入8 个PA端口,控制 引脚输出高点平,然后延时,从而实现流水灯程序。
2. GPIOA端口使能
首先是时钟使能
通过查看stm系统架构,知道了GPIOA是连接在APB2这条总线上,因此我们使能GPIOA口需要 操作APB2的外设时钟使能寄存器,查看APB2的外设时钟使能寄存器:
因此我们修改APB2时钟使能寄存器的第二位为1,就成功使能GPIOA端口了。
那么APB2的使能寄存器在哪里呢?
通过查表发现在AHB总线 复位和时钟控制 RCC的起始地址为0x4002100,
为什么APB2的时钟使能控制寄存器在AHB上呢?
因为系统架构是通过AHB桥接成APB1和APB2的,换句话说就是通过AHB上面的时钟频率分频给 APB1和APB2
RCC总共有8个寄存器:CR 、CF、GR、CIR、APB2RSTR、APB1RSTR、AHBENR、 APB2ENR、APB1ENR、BDCR、CSR,我们需要使能GPIOA时钟,用到的是APB2ENR寄存器
其相对于RCC的偏移地址为0x18,那么使能寄存器映射地址就为:
0x40021000+0x18=0x40021018
在上面的程序中是通过volatile关键字声明了8个寄存器的RCC结构体完成的,其作用和直接声明寄存器映射地址效果是一样的:
可以简化为:#define APB2ENR ((unsigned int)0x40021018)
下一步就是使能时钟了,就是将该寄存器第二位设置为1:
现在就已经完成了GPIOA端口时钟使能了。
3. 端口配置
stm32的GPIO口总共分为8种模式
输入:上拉输入、下拉输入、浮空输入、模拟输入
输出: 复用推挽输出、普通推挽输出、复用开漏输出、普通开漏输出
在此次实验中所需要配置的类型为普通推挽输出,那么如何配置呢?还是通过操作寄存器配置
需要用到的两个寄存器分别为端口配置低寄存器(端口配置高寄存器),端口输出数据寄存器
那么首先应该找到这两个寄存器的映射地址:
这两个寄存器都在GPIOA寄存器组里面,因此首先要找到GPIOA寄存器的映射地址:
在表中APB2的起始地址为0x4001 0000,GPIOA寄存器组的起始地址为0x40010800
其次找到对应的两个寄存器偏移地址:
所以端口配置低寄存器映射地址为0x40010800+0x00=0x40010800
端口输出数据寄存器映射地址为:0x40010800+0x0c=0x4001080c
同样也可以通过直接声明寄存器的映射地址来完成
在代码中也是通过创建volatile关键词的GPIO结构体完成的:
前面说过,stm32是32位操作系统,端口配置低寄存器控制32个bit,每4位为一个端口,从小到大分别为PA0~PA7
每个4位的高两位CNF选择配置上面说的GPIO的8种的一种模式
低两位选择输入模式或者输出模式的输出频率
我们需要配置的是通用推挽输出模式,速度选择50Mhz,因此为00 11 在16进制里面为3
因为需要用到8个端口,所以可以将这8个端口全部设置为推挽输出,速度为50MHz:
这时端口输出模式选择完成了,但是不知道输出是0,还是1,因此需要端口输出数据寄存器
一个bit对应一个端口,最小系统板只有8个端口,则其有效位位0-8,当配置为0的时候,PA0则输出低电平,配置为1的时候,则输出高电平
初始化的时候,将PA0设置为高电平,其他为低电平,即该寄存器的值可以设置为:0x0001
到这里,初始化就完成了
4. 端口操作和完整程序源码
完成上述程序配置后,PA的8个端口全部为推挽输出模式,并且输出速度为50Mhz
流水灯程序还需要一个延时程序
在程序中采用的是软件延时的方法:
在此次实验中,采用的是共阴极接法,8个led的阴极全部接地,阳极接PA的8个端口,当PA端 口输出高电平的时候灯亮,输出高电平的时候灯灭。
程序思路:初始化PA0为高电平,在循环中,先延时,然后左移,PA1为高电平,再判断是否0 ~8端口是否全为0,如果是的话,则又赋值为0x0001
程序流程图:
程序源码:
#define PERIPH_BASE ((unsigned int)0x40000000)//AHB的地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) //APB2地址
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) //GPIOA地址
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C //GPIOA_ODR地址
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
typedef struct{
volatile unsigned int CR;
volatile unsigned int CFGR;
volatile unsigned int CIR;
volatile unsigned int APB2RSTR;
volatile unsigned int APB1RSTR;
volatile unsigned int AHBENR;
volatile unsigned int APB2ENR;
volatile unsigned int APB1ENR;
volatile unsigned int BDCR;
volatile unsigned int CSR;
} RCC_TypeDef;
#define RCC ((RCC_TypeDef *)0x40021000)
typedef struct
{
volatile unsigned int CRL;
volatile unsigned int CRH;
volatile unsigned int IDR;
volatile unsigned int ODR;
volatile unsigned int BSRR;
volatile unsigned int BRR;
volatile unsigned int LCKR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
void delay(unsigned int time)
{
unsigned int i=0;
while(time--)
{
i=12000;
while(i--);
}
}
int main(void)
{
RCC->APB2ENR|=1<<2;
GPIOA->CRL&=0x00000000;
GPIOA->CRL|=0x33333333;
GPIOA->ODR=0x01;
while(1)
{
delay(850);
GPIOA->ODR=GPIOA->ODR<<1;
if(GPIOA->ODR==0x0100)
{
GPIOA->ODR=0x01;
}
}
二、 Proteus8.15 仿真
1. Proteus8. 15 下载和安装
由于8.0或者其他低版本可能没有stm32的固件库,所以需要下载更高版本
下载安装Proteus8.15 可参考:
Proteus8.15 安装包下载及详细安装_小殷学长的博客-CSDN博客 Proteus8.15 安装包下载及详细安装_小殷学长的博客-CSDN博客
2. 在keil5中编译上面的代码,生成hex文件
勾选输出选项中的生成hex文件选项
编译文件:
3. Proteus8.15 新建项目,导入hex文件仿真
创建时需要在固件库中选择cortex-M3系列 和 STM32F103R6
共阴极连接8个LED灯到单片机的GPIOA的8个端口
双击单片机,在Program File里面选择生成的hex文件
在Clock Scale里卖选择8 Times
4. 仿真效果
三、 JLink烧录程序和实物效果
1. 安装Jlink驱动
2. 配置Jlink烧录环境
点击debug
选择ST-LIN/v2 端口选择sw
3. 烧录程序
点击下载程序
4. 实验效果
连线图:
实验效果:
IMG_8099
四、总结
1. 低版本的Proteus软件可能不含有stm32的固件库,需要下载更高版本如 Proteus8.15
2. Proteus仿真会出现问题,时钟频率与我们实物延时时间相差很大,可参考: proteus仿真STM32时时钟问题解决方案_[rcc] apb1 is overclocked. force set prescaler = 2_Tnhello的博客-CSDN博客
3. 由于本次实验采用的是软件延时的方式,因此延时不准确,可以通过定时器延时更准确
PS:
参考网站:
proteus仿真STM32时时钟问题解决方案_[rcc] apb1 is overclocked. force set prescaler = 2_Tnhello的博客-CSDN博客
Proteus8.15 安装包下载及详细安装_小殷学长的博客-CSDN博客
源码下载:
交流群 :456948834