IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32F103寄存器方式点亮LED流水灯 -> 正文阅读

[嵌入式]STM32F103寄存器方式点亮LED流水灯

目录

前言

一、GPIO是什么?

1.1GPIO的寄存器地址

1.2GPIO的工作模式

二、STM32F103芯片的地址映射和存储器映射原理?

三、c语言进行流水灯点灯

1.创建工程

2.GPIO端口的设置

?3.C语言程序的编写

?四、硬件使用

1.使用到的器材:

2.连线:

3.HEX文件的烧录:

4.运行结果

五、汇编语言实现流水灯点亮

总结

参考文献


???????



前言

存储器本身没有地址,给存储器分配地址的过程叫存储器映射。在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。寄存器按位操作有比较好的可读性和扩展性。


一、GPIO是什么?

GPIO是stm32的外设,是通用输入输出端口的总称,简单来说就是STM32可控制的引脚,其基本功能是控制引脚输出高电平或者低电平。

1.1GPIO的寄存器地址

GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节, 在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。例如GPIO的端口寄存器的地址表:

寄存器名称寄存器地址相对GPIOB地址的偏移
GPIOB_CRL0x4001 0c000x00
GPIOB_CRH0x4001 0c040x04
GPIOB_IDR0x4001 0c080x08
GPIOB_ODR0x4001 0c0c0x0c
GPIOB_BSRR0x4001 0c100x10
GPIOB_BRR0x4001 0c140x14
GPIOB_LCKR0x4001 0c180x18

1.2GPIO的工作模式

输入模式

? ? -输入浮空(GPIO_Mode_IN_FLOATING)

? ? -输入上拉(GPIO_Mode_IPU)

? ? -输入下拉(GPIO_Mode_IPD)

? ? -模拟输入(GPIO_Mode_AIN)

输出模式

? ? -开漏输出(GPIO_Mode_Out_OD)

? ? -开漏复用功能(GPIO_Mode_AF_OD)

? ? -推挽式输出(GPIO_Mode_Out_PP)

? ? -推挽式复用功能(GPIO_Mode_AF_PP)

二、STM32F103芯片的地址映射和存储器映射原理?

内存映射:事实并非如此,一般的外设为了加快处理速度都有自己的片内RAM(比如说显存,你也知道显存对显卡性能的重要性),分出去的地址空间也就与片内RAM物理连接起来,这样CPU就能像访问内存一样去访问外设的片内RAM,这也就是所谓的内存映射?

存储器映射原理:在存储器的区域单元中,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射
?


三、c语言进行流水灯点灯


1.创建工程

选择STM32F103C8芯片:

添加文件:

不要忘记点击魔法棒勾选生成HEX文件:


2.GPIO端口的设置

GPIO端口的初始化设置三步骤

  • 时钟配置
  • 输入输出模式设置
  • 最大速率设置

时钟配置:

GPIO端口B的地址从0x4001 0C00开始。只寻找时钟使能寄存器的地址:复位和时钟控制RCC的地址从0x4002 1000开始;找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。在本次实验中,使用按位操作即可开启GPIO

?RCC->APB2ENR|=1<<3; ? ?//开启GPIOB端口的时钟
?RCC->APB2ENR|=1<<4; ? ?//开启GPIOC端口的时钟
?RCC->APB2ENR|=1<<5; ? ?//开启GPIOD端口的时钟

输入输出模式:点亮led灯应该为推挽模式

?

?这里我分别使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯,在stm32手册中我们选择PB5、PC3、PD2这三个IO口来作为本次实验的输出口

GPIOB->CRL|=0X00003000;PB4推挽输出

GPIOC->CRL|=0X00003000;PC4推挽输出

GPIOD->CRL|=0X00003000;PD4推挽输出

因为00与11组合为3,即输出模式为推挽输出模式,最大速度为50MHz

?3.C语言程序的编写

关于流水灯的有关函数在led.h中声明

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"
   
//LED端口定义
#define LED0 PBout(5)// PB5
#define LED1 PCout(3) // PC3 
#define LED2 PDout(2) // PD2

void LED_Init(void);  //初始化		 				    
#endif

?led.c:通过配置寄存器的值,来改变IO口的值进行变化

#include "sys.h"   
#include "led.h"

void LED_Init(void)
{ 
	RCC->APB2ENR|=1<<3;    //开启GPIOB端口的时钟
	RCC->APB2ENR|=1<<4;    //开启GPIOC端口的时钟
	RCC->APB2ENR|=1<<5;    //开启GPIOD端口的时钟
	
	GPIOB->CRL&=0XFF0FFFFF; 
	GPIOB->CRL|=0X00300000;//PB5 推挽输出   	 
    GPIOB->ODR|=1<<5;      //PB5 输出高电平
	
	GPIOC->CRL&=0XFFFF0FFF;
	GPIOC->CRL|=0X00003000; //PC3 推挽输出
	GPIOC->ODR|=1<<3;      //PC3  输出高电平
	
	GPIOD->CRL&=0XFFFFF0FF; 
	GPIOD->CRL|=0X00000300;//PD2  推挽输出  	 
    GPIOD->ODR|=1<<2;      //PD2  输出高电平
											  
}

主函数test.c函数?:编写了一个延迟函数进行延时,另外在主函数中采用一个while的死循环控制灯一直轮流亮灭且能观察清楚。

#include "sys.h"			
#include "led.h" 

void delay(unsigned int i) //延迟函数
{
	unsigned char j;               
	unsigned char k;
	for(;i>0;i--)
		for(j=500; j>0; j--) 
	       for(k =200; k>0; k--);
}

int main(void)
{		 

	LED_Init();		  	 	//初始化与LED连接的硬件接口    
	while(1)
	{
		
		LED0=0;
		LED1=1;
		LED2=1;    //PB5接口连接的LED灯亮
		delay(20);
		LED0=1;
		LED1=0;
		LED2=1;    //PC3接口连接的LED灯亮
		delay(20);
	    LED0=1;
		LED1=1;
		LED2=0;    //PD2接口连接的LED灯亮
		delay(20);
		
	}	 
}

?四、硬件使用

1.使用到的器材:

正点原子STM32mini开发板,杜邦线若干,不同颜色led灯3只,面包板一块

2.连线:

LED灯的短脚连接IO口,长脚连接3.3V(注:连接5V的端口可能会容烧坏LED灯),使用面包板能够更加方便的连线

3.HEX文件的烧录:

(1)如图上操作步骤对Fly Mcu V0.188进行操作,该步骤仅为个人习惯,可与之不同

(2)最后一步选择前面工程生成的HEX文件,点击开始编程?

4.运行结果

?

?

五、汇编语言实现流水灯点亮

1.汇编语言代码如下:

RCC_APB2ENR EQU 0x40021018
 
GPIOA_CRH EQU   0x40010804
GPIOA_ODR EQU   0x4001080C
                                    
GPIOB_CRL EQU   0x40010C00    ;寄存器映射
GPIOB_ODR EQU   0x40010C0C	
	
GPIOC_CRH EQU   0x40011004
GPIOC_ODR EQU   0x4001100C	
	
	
Stack_Size      EQU     0x00000400
 
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
					;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。
Stack_Mem       SPACE   Stack_Size
__initial_sp
 
                AREA    RESET, DATA, READONLY
 
__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
		
                
MainLoop		BL LED2_Init
                BL LED2_ON
                BL Delay             ;LED2灯闪烁
                BL LED2_OFF
                BL Delay
				
				BL LED1_Init				
				BL LED1_ON
                BL Delay             ;LED1灯闪烁
                BL LED1_OFF
                BL Delay
				
                BL LED3_Init				
				BL LED3_ON
                BL Delay            ;LED3灯闪烁
                BL LED3_OFF
                BL Delay
				
                B MainLoop
				
             
LED1_Init
                PUSH {R0,R1, LR}        ;R0,R1,LR中的值放入堆栈
                
                LDR R0,=RCC_APB2ENR      ;LDR是把地址装载到寄存器中(比如R0)。
                ORR R0,R0,#0x08         ;开启端口GPIOB的时钟,ORR 按位或操作,01000将R0的第二位置1,其他位不变		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]             ;STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器
				;上面一部分汇编代码是控制时钟的
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1配置为通用推挽输出;开启的是pb1,所以是2,为0010,是推挽输出模式,最大速度为2mhz
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR   
                BIC R0,R0,#0X00000002      ;BIC 先把立即数取反,再按位与
                LDR R1,=GPIOB_ODR          ;GPIOB_Pin_1输出为0;由r1控制ord寄存器
                STR R0,[R1]                 ;将ord寄存器的值变为r0的值
             
                POP {R0,R1,PC}             ;将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
 
 
             
LED1_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002    ;因为是PB1所以对应二进制0010;GPIOB_Pin_1输出为0,LED1熄灭
			    LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED1_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOB_ODR
                ORR R0,R0,#0X00000002    ;GPIOB_Pin_1输出为1,LED1亮
                 LDR R1,=GPIOB_ODR
                STR R0,[R1]
                POP {R0,R1,PC}           
 
 
				
 
LED2_Init
                PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04		   ;打开GPIOA的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOA_CRH
                ORR R0,R0,#0X00020000   ;GPIOA_Pin_12配置为通用推挽输出
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOA_ODR
                BIC R0,R0,#0X00001000   
                LDR R1,=GPIOA_ODR            ;GPIOA_Pin_12输出为0
                STR R0,[R1]
             
                POP {R0,R1,PC}
				
LED2_OFF
                PUSH {R0,R1, LR}   
                
               LDR R0,=GPIOA_ODR
               BIC R0,R0,#0X00001000        ;GPIOA_Pin_12输出为0,LED2熄灭
			    LDR R1,=GPIOA_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED2_ON
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOA_ODR
                ORR R0,R0,#0X00001000     ;GPIOA_Pin_12输出为1,LED2亮
				 LDR R1,=GPIOA_ODR
                STR R0,[R1]
				
				 POP {R0,R1,PC}
				 
 
LED3_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x10		    ;打开GPIOC的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOC_CRH
                ORR R0,R0,#0X02000000   ;GPIOC_Pin_14配置为通用推挽输出
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000   ;GPIOC_Pin_14输出为0
                LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000  ;GPIOC_Pin_14输出为0,LED3熄灭
			    LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOC_ODR
                ORR R0,R0,#0X00004000   ;GPIOC_Pin_14输出为1,LED3亮
                 LDR R1,=GPIOC_ODR
                STR R0,[R1]
				
                POP {R0,R1,PC}        
                
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1
 
                CMP R0,#300
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#300
                BCC DelayLoop0
 
                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                POP {R0,R1,PC}    
 
                END

(1)这部分采用的是软件延时,具体原理为R0R1R2初始化为0,R0加1,当R0大于300时,R1加1,然后R0为变为0,R0继续加1,循环往复,当R1大于300时,R2加1,R0、R1变为0,继续此操作。

(2)这部分汇编语言的代码本质上也是通过改变寄存器的值来实现电平的变化,进行LED的轮流点亮,但是代码部分重复,我是参考同学的代码,不清楚该如何修改。

(3)HEX文件烧录同上步骤,不在重复,运行结果如下:


?


总结

在开始学习STM32F103系列芯片的地址映射和寄存器映射原理时,在最初查看零死角玩转STM32—F103指南者时,觉得概念太过抽象,其中映射的概念并不是很懂,但在CSDN上查看博客时,发现寄存器映射的关系和数学中函数的映射可以类比,然后在慢慢理解。LED程序的编写,关于汇编语言的时候,是借鉴的同学的代码,而且关于汇编语言特别陌生而且不知道从何下手。整次实验下来,在代码改编的时候只有C语言能做到,而且出现了很多问题,包括程序的烧录,串口软件的使用,汇编程序代码的调试和改变,直到最后在同学的帮助下才完成。本次实验在同学的帮助下最后成功点亮了LED流水灯十分高兴,也有一点成就感。在以后的学习中,希望继续能够坚持下去。

参考文献

参考网站:

?STM32的8种GPIO输入输出模式深入详解_baidu_37366055的博客-CSDN博客

(stm32学习总结)—对寄存器的理解 - 北极星! - 博客园?

STM32从地址到寄存器_geekYatao-CSDN博客_stm32寄存器地址

【嵌入式07】寄存器映射原理详解,GPIO端口的初始化设置步骤_噗噗的罐子博客-CSDN博客?

?参考书籍:

野火之零死角玩转STM32—F103指南者

正点原子STM32mini板开发手册

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-20 12:38:50  更:2021-10-20 12:38:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:22:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码