寄存器点亮LED流水灯


本文介绍了STM32F103系列芯片的地址映射和寄存器映射原理,使用寄存器点亮LED流水灯。


文章目录

  • 一、STM32F103C8T6单片机
    • 1. 简介
    • 2. 映射
    • 3. 时钟
    • 4. GPIO(通用I/O)输入输出
  • 二、点亮LED灯
    • 1. 创建工程
    • 2. 代码编写
    • 3. 波形观察
    • 4. 烧录
  • 三、总结
  • 四、参考文献

一、STM32F103C8T6单片机

1. 简介

STM32是一个微控制器产品系列的总称,目前这个系列中已经包含了多个子系列,分别是:STM32小容 量产品、STM32中容量产品、STM32大容量产品和STM32互联型产品;按照功能上的划分,又可分为 STM32F101xx、STM32F102xx和STM32F103xx系列。

STM32系列产品命名规则如下:

寄存器点亮LED流水灯_第1张图片

2. 映射

存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器区中的每个字 映射到位段存储器区的一个位,在别名存储区写入一个字具有对位段区的目标位执行读-改-写操 作的相同效果。

在STM32F10xxx里,外设寄存器和SRAM都被映射到一个位段区里,这允许执行单一的位段的 写和读操作。

下面的映射公式给出了别名区中的每个字是如何对应位带区的相应位的:

​ bit_word_addr = bit_band_base + (byte_offset×32) + (bit_number×4)

其中:

  • bit_word_addr是别名存储器区中字的地址,它映射到某个目标位。
  • bit_band_base是别名区的起始地址。
  • byte_offset是包含目标位的字节在位段里的序号 。
  • bit_number是目标位所在位置(0-31) 。

例子:

下面的例子说明如何映射别名区中SRAM地址为0x20000300的字节中的位2:

​ 0x22006008 = 0x22000000 + (0x300×32) + (2×4).

对0x22006008地址的写操作与对SRAM中地址0x20000300字节的位2执行读-改-写操作有着相 同的效果。

GPIO寄存器地址映像

寄存器点亮LED流水灯_第2张图片

其他寄存器起始地址及其映像自行查看stm32手册

3. 时钟

在运行模式下,通过对预分频寄存器进行编程,可以降低任意一个系统时钟(SYSCLK、 HCLK、PCLK1、PCLK2)的速度,也可以在任何时候通过停止为外设和内存提供时钟(HCLK和PCLKx)来减少功耗。

4. GPIO(通用I/O)输入输出

每个I/O端口位可以自由编程,然而I/0端口寄存器必须按32位字被访问(不允许半字或字节访问)。 GPIOx_BSRR和GPIOx_BRR寄存器允许对任何GPIO寄存器的读/更改的独立访问;这样,在读和更改访问之间产生IRQ时不会发生危险。

I/O端口为的基本结构

寄存器点亮LED流水灯_第3张图片

复位期间和刚复位后,复用功能未开启,I/O端口被配置成浮空输入模式(CNFx[1:0]=01b, MODEx[1:0]=00b)。 复位后,JTAG引脚被置于输入上拉或下拉模式:

─ PA15:JTDI置于上拉模式

─ PA14:JTCK置于下拉模式

─ PA13:JTMS置于上拉模式

─ PB4: JNTRST置于上拉模式

当作为输出配置时,写到输出数据寄存器上的值(GPIOx_ODR)输出到相应的I/O引脚。可以以推挽模式或开漏模式(当输出0时,只有N-MOS被打开)使用输出驱动器。 输入数据寄存器(GPIOx_IDR)在每个APB2时钟周期捕捉I/O引脚上的数据。 所有GPIO引脚有一个内部弱上拉和弱下拉,当配置为输入时,它们可以被激活也可以被断开。

当I/O端口配置为输入时:

● 输出缓冲器被禁止

● 施密特触发输入被激活

● 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接

● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器

● 对输入数据寄存器的读访问可得到I/O状态

当I/O端口被配置为输出时:

● 输出缓冲器被激活

​ ─ 开漏模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将端口置于高阻状态(PMOS从不被激活)。

​ ─ 推挽模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将激活P-MOS。

● 施密特触发输入被激活

● 弱上拉和下拉电阻被禁止

● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器

● 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态

● 在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。

二、点亮LED灯

1. 创建工程

创建一个keil5工程

由于我们使用的是STM32F103C8T6,所以这里我们选择103C8

寄存器点亮LED流水灯_第4张图片

接下来跳出来的库文件配置界面直接cancle即可


寄存器点亮LED流水灯_第5张图片

接下来我们将startup_stm32f10x_md.s加入到Source Group 1中,如下图所示

startup_stm32f10x_md.s是STM32芯片的启动文件,ST 公司提供了 3 个启动文件给我们,分别用于不同容量的 STM32 芯片,这三个文件是:startup_stm32f10x_ld.s(小容量即Flash<=32k),startup_stm32f10x_md.s(中等容量即64k<=Flash<=128k),startup_stm32f10x_hd.s(大容量即Flash>=256k)。

寄存器点亮LED流水灯_第6张图片

注意需要将文件类型调整为Asm Source file,否则会找不到startup_stm32f10x_md.s,因为默认文件类型为.c文件

寄存器点亮LED流水灯_第7张图片

下面添加新的.c文件,右键Source Group 1,点击Add New Item …

寄存器点亮LED流水灯_第8张图片

选择.c文件,并输入文件名

寄存器点亮LED流水灯_第9张图片

需要注意keil版本问题,默认为6,我们需要将它与使用的keil版本匹配

寄存器点亮LED流水灯_第10张图片

2. 代码编写

在编写代码前,需要修改startup_stm32f10x_md.s部分内容。

由于该文件含有SystemInit函数,Reset_Handler 调用了该函数用来初始化 SMT32 系统时钟,而我们写的寄存器版本代码又不需要SystemInit函数,只写main函数,没写Systemlnit函数,编译时会发生错误。所以我们需要打开startup_stm32f10x_md.s文件,用分号“;”注释SystemInit代码。 q或者为了简单起见,我们定义一个SystemInit 空函数,什么也不做,为的是骗过编译器,把这个错误去掉。

编写以下代码

#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)
/*定义寄存器地址*/
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
#define GPIOA_ODR		*((unsigned volatile int*)0x4001080C)
#define GPIOB_CRL		*((unsigned volatile int*)0X40010C00)
#define GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
	
#define DELAY Delay(0x0FFFFF);
void LED_Init(void);
void Delay(int nCount); 
void SystemInit(void);

	void SystemInit(){
     } 	//函数为空,目的是为了骗过编译器不报错

	void LED_Init(void)
{
     
	RCC_APB2ENR |= 1<<2;//开启GPIOA时钟
	RCC_APB2ENR |= 1<<3;//开启GPIOB时钟
	GPIOA_CRL &= 0xFFFFF00F;//初始化A1、A2
	GPIOA_CRL |= 0x00003330;//设置为推挽输出,速率为50MHz
	GPIOB_CRL &= 0xFFFFFF0F;//初始化B1
	GPIOB_CRL |= 0x00003330;
	GPIOA_ODR |= 1<<1;		//A1输出高电平
	GPIOA_ODR |= 1<<2;		//A2输出高电平
	GPIOB_ODR |= 1<<1;		//B1输出高电平
}

	void Delay(int nCount)
{
     
	for(; nCount != 0; nCount--);
}

int main(void)
{
     
	LED_Init();
	while (1)
	{
     
		GPIOA_ODR &= ~(1<<1);		//A1输出低电平
		DELAY;//延时
		DELAY;
		GPIOA_ODR |= 1<<1;			//A1输出高电平
		GPIOA_ODR &= ~(1<<2);		//A2输出低电平
		DELAY;
		DELAY;
		GPIOA_ODR |= 1<<2;			//A2输出高电平
		GPIOB_ODR &= ~(1<<1);		//B1输出低电平
		DELAY;
		DELAY;
		GPIOB_ODR |= 1<<1;			//B1输出高电平
	}
}

3. 波形观察

为确保代码的正确性,我们使用示波器观察波形

修改Debug中用红框标识的地方,以使我们能进行软件仿真

寄存器点亮LED流水灯_第11张图片

在工具栏中找到System Analyzer Window,点击右边的箭头,找到Logic Analyzer

寄存器点亮LED流水灯_第12张图片

点击左上角的Setup,设置管脚

首先点击右上角的矩形后就能在下面输入对应的引脚地址,比如我使用的是A1、A2和B1,所以输入PORTA.1、PORTA.2和PORTB.1。然后我们需要将第三部中的类型改为bit,最后关闭即可。

寄存器点亮LED流水灯_第13张图片

点击运行,观察波形

寄存器点亮LED流水灯_第14张图片

波形正确!

4. 烧录

STM32F103C8T6最小系统板结构

寄存器点亮LED流水灯_第15张图片
驱动+mcuisp+md.s启动文件,提取码:3an4

下载CH340驱动程序

在这里插入图片描述

下载后解压,点击安装即可

下载mcuisp或Flymcu串口下载(烧录)工具软件

在进行烧录之前,我们需要将STM32F103C8最小系统板的BOOT0置1,BOOT1置0,如图所示

然后如下图中,将USB转串口与核心板连接

寄存器点亮LED流水灯_第16张图片

将USB连上电脑,打开设备管理器,我们可以看到CH340(COM4)

寄存器点亮LED流水灯_第17张图片

打开mucisp或FIymcu烧录软件,下面以mcuisp为例

第一步点击搜索串口,软件会自动搜索到COM4串口。第二步选择在keil中编译程序时生成的HEX文件,该文件的生成需要自己去勾选,具体参考以前的文章。第三步选择DTR的低电平复位,RTS高电平进BootLoader

寄存器点亮LED流水灯_第18张图片

点击读器件信息,若不能成功读取,请检查杜邦线是否接触不良。

寄存器点亮LED流水灯_第19张图片

点击开始编程

寄存器点亮LED流水灯_第20张图片

烧录成功!

接下来,先将BOOT0重新置为0,然后按照自己的程序连接电路即可。

下面是实物连接及效果图

三、总结

通过对地址映射和寄存器映射原理的学习,GPIO端口的初始化设置三步骤的了解,成功点亮了LED流水灯。所有的 GPIO 引脚都有基本的输入输出功能:最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现控制,比如GPIO引脚接入到 LED 灯,那就可以控制 LED 灯的亮灭;最基本的输入功能是检测外部输入电平,比如把 GPIO 引脚连接到按键,通过电平高低区分按键是否被按下。

四、参考文献

STM32F103点亮LED流水灯
[STM32学习笔记(二)] 使用寄存器点亮LED小灯

你可能感兴趣的:(stm32,嵌入式硬件)