一、STM32F103系列芯片的地址映射和寄存器映射原理

1、寄存器

寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。
其实,寄存器就是存放东西的东西。存放数据的寄存器是最好理解的,如果你需要读取一个数据,直接到这个寄存器所在的地方来问问他,数据是多少就行了。问寄存器这个动作,叫做访问寄存器。不同的数据会存放在不同的寄存器,例如引脚PA2与PB8的高低电平数据(1或0)肯定放在不同的寄存器里,那么怎么区分不同的寄存器呢?通过地址,不同的寄存器有不同的地址。
指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。

2、寄存器映射

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

3、地址映射

为了保证CPU执行指令时可正确访问存储单元,需将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射。

二、GPIO端口的初始化设置

1、什么是GPIO

GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能。用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。
STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103VET6 型号芯片有 GPIOA、GPIOB、GPIOC至 GPIOE共 5组 GPIO,芯片一共 100个引脚,其中 GPIO就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。
最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO引脚接入到 LED灯,那就可以控制 LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。
最基本的输入功能是检测外部输入电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是否被按下。

2、GPIO端口初始化步骤

使能GPIOx口的时钟。
指明GPIOx口的哪一位,这一位的速度大小以及模式。
调用GPIOx初始化函数进行初始化。
调用GPIO-SetBits函数,进行相应位的置位。

3、GPIO的工作模式

GPIO的八种工作模式:输入输出是相对于CPU,四种输入、四种输出模式以及四种输出最大速度。
输入:外部数据输入到开发板
输出:开发板的数据输出到外部设备
1、GPIO_Mode_AIN模拟输入(将IO口作为模拟输入接口。输入的可能是变化的值,接受外部的模拟信息输入)
2、GPIO_Mode_IN_FLOATING 浮空输入(复位上电的时候,引脚不确定电平的高低)
3、GPIO_Mode_IPD 下拉输入(将IO口作为通用输入接口,只能输入0或1,强制下拉,一般是为了输入强低电平)
4、GPIO_Mode_IPU 上拉输入(将IO口作为通用输入接口,只能输入0或1,强制上位,一般是为了输入强高电平)
5、GPIO_Mode_Out_OD 开漏输出(带上拉或者下拉)(要得到高电平状态需要上拉电阻才行,可以作为电流型驱动)
6、GPIO_Mode_AF_OD 开漏复用输出(带上拉或者下拉) 复用功能,不只是单纯的作为输入输出,可以作为其他功能的引脚:串口、12C、SPI、要得到高电平状态需要上拉电阻才行
7、GPIO_Mode_Out_PP 推挽输出(带上拉或者下拉)IO输出0接GND,IO输出1接VCC,读输入值是未知的,输出0就一定是0.输出1就一定是1
8、GPIO_Mode_AF_PP 推挽复用输出(带上拉或者下拉)复用功能,不只是单纯的作为输入输出,可以作为其他功能的引脚:串口、12C、SPI。输出0就一定是0,输出1就一定是1
GPIO四种最大输出速度:2MHZ、25MHZ、50MHZ、100MHZ

三、点亮LED流水灯

1、打开GPIO口的时钟

时钟控制名字叫做RCC,属于AHB总线。GPIOB属于APB2。
时钟的地址

GPIO的地址:GPIO是属于APB2的

外设时钟使能寄存器,设偏移量为0x18,起始地址0x4002 1000,该寄存器地址为0x4002 1018

打开三个IO口的时钟需要将三个位都置1:

#define RCC_APB2ENR (*(unsigned int *)0x40021018)// 打开时钟
RCC_APB2ENR |= (1<<3);  // 打开 GPIOB 时钟
RCC_APB2ENR |= (1<<4);  // 打开 GPIOC 时钟
RCC_APB2ENR |= (1<<2);  // 打开 GPIOA 时钟

2、初始化GPIO口

GPIO的CRL、CRH、CNF和MODE的关系:
STM32的CRL控制着每个IO端口的位占用CRL的4个位,高两位为CNF、低两位为MODE,CRH的作用和CRL完全一样,只是CRL控制的书是低8位输出口,而CRH控制的是高8位输出口。

① 端口配置低寄存器(GPIOx_CRL) (x=A…E)

结合下图来说明,低位寄存器是用来配置GPIO0到GPIO7的,A~E共5组GPIO,相对应的可以知道高位寄存器则用来配置A ~ E组寄存器的GPIO8到GPIO15;可以发现,每一个GPIO引脚由4个比特位控制,其中第2位是MODE,用来配置该GPIO是输入还是输出模式,如果是输出,对应引脚输出的最大速度;其中的高2位是CNF,用来配置引脚输入/输出时的具体功能模式,不同应用需求对应不同的模式设置。

② 端口配置高寄存器(GPIOx_CRH) (x=A…E)

同上面的低位配置寄存器一样,只不过高位配置寄存器是用来配置GPIO8~GPIO15的;要知道,一般配置一个GPIO的输入输出模式时,先配置MODE,确定引脚是用来输入还是输出,其次再配置CNF来设置具体的工作模式

这里使用推挽输出,并设置最大速度为10MHz,则将控制A0的四个位设置为0001:

#define GPIOA_CRL (*(unsigned int *)0x40010800)// 最后四位变为0001
GPIOA_CRL |= (1<<0);  // 最后一位变1
GPIOA_CRL &= ~(0xE<<0);  // 倒数2、3、4位变0

补充

1.总线基地址

2、外设基地址

3、外设寄存器地址

对于GPIOB的B9、GPIOC的C15、GPIOA的A4,设置如下:

#define GPIOB_CRH (*(unsigned int *)0x40010C04)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)GPIOB_CRH&= 0xffffff0f;     GPIOB_CRH|=0x00000020;GPIOC_CRH &= 0x0fffffff; GPIOC_CRH|=0x30000000;GPIOA_CRL &= 0xfff0ffff; GPIOA_CRL|=0x00010000;

配置代码的解析:
STM32的一组GPIO有16个IO口,PIOA这一组,有GPIOA0~GPIOA15一共16个IO口。那么一组GPIO就需要16x4=64位的寄存器来存放这一组GPIO的工作模式的配置,但STM32的寄存器都是32位的,所以只能使用2个32位的寄存器来存放了。CRL用来存放低八位的IO口(GPIOx0—GPIOx7)的配置,CRH用来存放高八位的IO口(GPIOx8—GPIOx15)的配置。

GPIOB->CRL&=0XFFFFFFF0; //GPIOB0
GPIOB->CRL&=0XFFFFFF0F; //GPIOB1
GPIOB->CRL&=0XFFFFF0FF; //GPIOB2
GPIOB->CRL&=0XFFFF0FFF; //GPIOB3
GPIOB->CRL&=0XFFF0FFFF; //GPIOB4
GPIOB->CRL&=0XFF0FFFFF; //GPIOB5
GPIOB->CRL&=0XF0FFFFFF; //GPIOB6
GPIOB->CRL&=0X0FFFFFFF; //GPIOB7
GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;

(1):GPIOB->CRH的意思是GPIOB配置寄存器的高八位CRH,这个寄存器有32位,划分成了八部分,每部分有4位
第一部分是配置GPIOB8的,第二部分是配置GPIOB9,以此类推。
(2):GPIOB->CRH&=0XFFFFFF0F; 我们知道&是按位与操作,那么这个语句的意思就很清楚了,用0XFFFFFF0F(化成二进制是32位)和CRH进行与操作,这一句代码的结果是CRH的4-7位变成0,其他位的数据不变,这个是&语句的特性。
(3):GPIOB->CRH|=8<<4; 表示将0x08左移4位。如果我们将8换成0x00000008就很明白了:

GPIOB->CRH|=0x00000008<<4

那么我们将0x00000008左移4位也就是:0x00000008<<4变成0x00000080

3、设置高低电平


输出高电平则为1,低电平则为0
以GPIOB和0号引脚(B0)为例,将其设置为低电平:

#define GPIOA_ODR (*(unsigned int *)0x4001080C)
GPIOB_ODR &= ~(1<<0);  // 最后一位变0

对于GPIOB的B9、GPIOC的C15、GPIOA的A4,设置如下:

#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)GPIOB_ODR &= ~(1<<9);
GPIOC_ODR &= ~(1<<15);
GPIOA_ODR &= ~(1<<4);  

四、C语言

1、新建项目

芯片选择STM32F103C8

出现如下此图删掉,不做选择

添加main.c文件
点击左侧project里的Target1->右击Source Group 1->Add New Item to Group “Source Group 1”

创建hex文件


添加驱动文件
将下图中的文件复制到新建的项目下


右击文件夹,选择Add Existing Files to Group Source Group 1(或双击文件夹),选择All FIles,选择刚刚添加的启动文件,Add,Add之后Close

如果找不到记得修改下面的文件类型为all。

在main.c中写入以下内容使用的引脚是PA7,PB9,PC15


#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800#define RCC_APB2ENR (*(unsigned int *)0x40021018)#define GPIOB_CRH (*(unsigned int *)0x40010C04)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)void SystemInit(void);
void Delay_ms(volatile  unsigned  int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
void Delay_ms( volatile  unsigned  int  t)
{unsigned  int  i;while(t--)for (i=0;i<800;i++);
}void A_LED_LIGHT(){GPIOA_ODR=0x0<<4;        //PA4低电平GPIOB_ODR=0x1<<9;        //PB9高电平GPIOC_ODR=0x1<<15;       //PC15高电平
}
void B_LED_LIGHT(){GPIOA_ODR=0x1<<4;     //PA4高电平GPIOB_ODR=0x0<<9;        //PB9低电平GPIOC_ODR=0x1<<15;       //PC15高电平
}
void C_LED_LIGHT(){GPIOA_ODR=0x1<<4;     //PA4高电平GPIOB_ODR=0x1<<9;        //PB9高电平GPIOC_ODR=0x0<<15;       //PC15低电平
}int main(){int j=100;// 开启时钟RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟// 设置 GPIO 为推挽输出GPIOB_CRH&= 0xffffff0f;  //设置位 清零        GPIOB_CRH|=0x00000020;  //PB9推挽输出GPIOC_CRH &= 0x0fffffff; //设置位 清零        GPIOC_CRH|=0x30000000;  //PC15推挽输出GPIOA_CRL &= 0xfff0ffff; //设置位 清零       GPIOA_CRL|=0x00010000; //PA4推挽输出// 3个LED初始化为不亮(即高点位)GPIOB_ODR |= (1<<9); GPIOC_ODR |= (1<<15); GPIOA_ODR |= (1<<4);  while(j){B_LED_LIGHT();Delay_ms(1000000);C_LED_LIGHT();Delay_ms(1000000);A_LED_LIGHT();Delay_ms(1000000);}}void SystemInit(){}

点击build生成hex文件

2、电路连接

将串口USB转TTL线与stm32核心板连接如图所示

USB转TTL模块与芯片连接:
GND——GND 3V3——3V3 TXD——A10 RXD——A9
红灯——B9 绿灯——C15 黄灯——A4

3、程序编译

点击build
将板连接到电脑上,打开mcuisp,上传HEX文件
(安装mcuisp参考https://blog.csdn.net/xzzszka/article/details/123768965)
点击搜索串口->选择联机时下载的程序文件(HEX文件)->下方选择DTR的低电平复位,RTS高电平进BootLoader
(未擦除成功可先清除芯片)

注意:在烧录的时候一定要在USB转TTL设备插入电脑前,将boot0设置为1.

在烧录完成之后拔出USB转TLL设备,将boot0又换成0,再插入,之后会正常运行程序。

4、效果实现

五、汇编语言

1、新建项目

新建一个项目LED1,同上述过程相同,把main.c换成led.s文件其余与上述步骤相同,同样要生成HEX文件。

2、led.s

RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址GPIOB_BASE EQU 0x40010C00
GPIOC_BASE EQU 0x40011000
GPIOA_BASE EQU 0x40010800
GPIOB_CRH EQU 0x40010C04
GPIOC_CRH EQU 0x40011004
GPIOA_CRL EQU 0x40010800
GPIOB_ODR EQU 0x40010C0C
GPIOC_ODR EQU 0x4001100C
GPIOA_ODR EQU 0x4001080C
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_spAREA    RESET, DATA, READONLY__Vectors       DCD     __initial_sp               ; Top of StackDCD     Reset_Handler              ; Reset HandlerAREA    |.text|, CODE, READONLYTHUMBREQUIRE8PRESERVE8ENTRY
Reset_Handler bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器
MainLoop        BL LED_ON_BBL DelayBL LED_OFF_BBL Delay        BL LED_ON_CBL DelayBL LED_OFF_CBL DelayBL LED_ON_ABL DelayBL LED_OFF_ABL DelayB MainLoop;B:无条件跳转。
LED_Init;LED初始化PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈;控制时钟LDR R0,=0x40021018;LDR是把地址装载到寄存器中(比如R0)。ORR R0,R0,#0x1cLDR R1,=0x40021018STR R0,[R1];初始化GPIOA_CRLLDR R0,=0x40010800BIC R0,R0,#0xfff0ffff;BIC 先把立即数取反,再按位与LDR R1,=0x40010800STR R0,[R1]LDR R0,=0x40010800ORR R0,#0x00010000LDR R1,=0x40010800STR R0,[R1];将PA4置1MOV R0,#0x10LDR R1,=0x4001080CSTR R0,[R1];初始化GPIOB_CRLLDR R0,=0x40010C04BIC R0,R0,#0xffffff0f;BIC 先把立即数取反,再按位与LDR R1,=0x40010C04STR R0,[R1]LDR R0,=0x40010C04ORR R0,#0x00000020LDR R1,=0x40010C04STR R0,[R1];将PB9置1MOV R0,#0x200LDR R1,=0x40010C0CSTR R0,[R1];初始化GPIOCLDR R0,=0x40011004BIC R0,R0,#0x0fffffffLDR R1,=0x40011004STR R0,[R1]LDR R0,=0x40011004ORR R0,#0x30000000LDR R1,=0x40011004STR R0,[R1];将PC15置1MOV R0,#0x8000LDR R1,=0x4001100CSTR R0,[R1]POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED_ON_B;亮灯PUSH {R0,R1, LR}    MOV R0,#0x000LDR R1,=0x40010C0CSTR R0,[R1]POP {R0,R1,PC}LED_OFF_B;熄灯PUSH {R0,R1, LR}    MOV R0,#0x200LDR R1,=0x40010C0CSTR R0,[R1]POP {R0,R1,PC}
LED_ON_C;亮灯PUSH {R0,R1, LR}    MOV R0,#0x0000LDR R1,=0x4001100CSTR R0,[R1]POP {R0,R1,PC}LED_OFF_C;熄灯PUSH {R0,R1, LR}    MOV R0,#0x8000LDR R1,=0x4001100CSTR R0,[R1]POP {R0,R1,PC}
LED_ON_APUSH {R0,R1, LR}    MOV R0,#0x00                LDR R1,=0x4001080C               STR R0,[R1]   POP {R0,R1,PC}
LED_OFF_A                PUSH {R0,R1, LR}    MOV R0,#0x10                LDR R1,=0x4001080C              STR R0,[R1]  POP {R0,R1,PC}
DelayPUSH {R0,R1, LR}MOVS R0,#0MOVS R1,#0MOVS R2,#0DelayLoop0        ADDS R0,R0,#1CMP R0,#330BCC DelayLoop0MOVS R0,#0ADDS R1,R1,#1CMP R1,#330BCC DelayLoop0MOVS R0,#0MOVS R1,#0ADDS R2,R2,#1CMP R2,#15BCC DelayLoop0POP {R0,R1,PC}    NOPEND

3、编译

可能会出现下述错误

解决方法:
右击startup_stm32f10x_md.s->点击第一个魔术棒,取消Include in Target Build和Always build前面的√

再重新编译点击rebuild。

电路连接不变,连接到电脑,打开mcuisp,上传HEX文件到stm32f103c8t6上。

4、效果实现

六、 参考资料

STM32F103寄存器配置相关学习
https://blog.csdn.net/Fancy_white/article/details/120935744

STM32F103系列芯片原理学习以及LED灯的点亮相关推荐

  1. 诺贝尔物理学奖公布:LED灯将点亮了整个21世纪

    很多其它精彩.破晓博客:点击打开链接 7日.在瑞典首都斯德哥尔摩,瑞典皇家科学院常任秘书诺尔马克(左二)宣布2014年诺贝尔物理学奖得主.新华社发 ■人物 中村修二 勇于追讨酬劳的科学家 被誉为&qu ...

  2. 诺贝尔物理学奖揭晓:LED灯将点亮整个21世纪

    更多精彩,破晓博客:点击打开链接 7日,在瑞典首都斯德哥尔摩,瑞典皇家科学院常任秘书诺尔马克(左二)宣布2014年诺贝尔物理学奖得主.新华社发 ■人物 中村修二 勇于追讨报酬的科学家 被誉为" ...

  3. STM32F103系列芯片的地址和寄存器映射原理、LED轮流闪烁实现

    STM32F103系列芯片的地址和寄存器映射原理.LED轮流闪烁实现 文章目录 STM32F103系列芯片的地址和寄存器映射原理.LED轮流闪烁实现 1 51单片机和STM32的不同点 2 寄存器 2 ...

  4. 学习和理解STM32F103系列芯片的地址映射和寄存器映射原理;了解GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)。

    ​​ 目录 一:什么是寄存器 1. 2. 二.程序设计思路 1.GPIO模式 2.存储器空间 3.存储器映射 4.寄存器操作 三.GPIO寄存器 STM32 GPIO口的工作模式: 四.总结: ​​​ ...

  5. c语言c51单片机点亮8个灯,单片机课程设计-8个LED灯来回点亮

    <单片机课程设计-8个LED灯来回点亮>由会员分享,可在线阅读,更多相关<单片机课程设计-8个LED灯来回点亮(20页珍藏版)>请在人人文库网上搜索. 1.目录第一章 绪论-3 ...

  6. Arduino(一):LED灯的点亮熄灭和亮度控制

    原本是因为有意了解少儿编程相关的内容,购买了一套某公司的开发套件,通过scratch图形方式对Arduino的基础开发进行了一些学习. 后来又觉得scratch我都学了,还不趁势研究一下代码怎么写未免 ...

  7. 点亮led灯的个数_16个LED灯循环点亮的小程序(代码)

    今天又小伙伴又来考我了.问我16个LED灯循环点亮怎么变成.我也动了下手,将手里的8个LED灯循环点亮的小程序进行了修改,就完成了16个LED灯循环点亮的小实验.那么下面给大家分享下吧! 其实16个L ...

  8. 51单片机入门学习篇-led灯、按键、数码管、中断

    文章目录 前言 一.Proteus仿真 二.Keil代码编写 1.流水灯 2.来回点灯 3.按键操作 4.中断操作 5.数码管 6 动态数码管 三. 练习题 总结 前言 本文用来记录博主学习单片机的过 ...

  9. 第01课 了解单片机的原理控制一个LED灯的亮和灭-----51单片机C语言试验教程

    第一课,了解单片机及单片机的控制原理和DX516 的用法,控制一个LED 灯的亮和灭 本章学习内容: 单片机基本原理,如何使用DX516 仿真器,如何编程点亮和灭掉一个LED 灯,如何进入KEILC5 ...

最新文章

  1. ubuntu 禁用透明大页_Linux关于透明大页的使用与禁用介绍
  2. ads无法启用状态服务器,NAC ADSSO 无法工作在Microsoft 2008服务器版本
  3. win服务器管理器“丢失”了怎么办?
  4. shell脚本接收输入
  5. Oracle 数据库之最:你见过最高的 SQL Version 是多少?
  6. iOS - Core Animation 核心动画
  7. keil和proteus的联调设置
  8. 《需求规格说明书》业务描述活动图
  9. Oracle RMAN 学习
  10. 试了试Docker桌面应用自带的K8s集群,一个字“简单”
  11. oracle x kglob,x$kglob x$kgllk x$kglpn
  12. 常用的数据结构和算法
  13. FreeSwitch +fusionpbx安装和基本使用
  14. 181029每日一句
  15. vivo S9无法激活手机了vivoS9e怎么解锁平台刷机教程屏幕锁不记得了可以用这个方法教程重装系统固件软件S9手机如果已忘记密码可以自己学习升级降级USB操作了
  16. WIN7中文专业版安装英文语言包的方法
  17. windows下安装Linux(Ubuntu)系统
  18. 当股份制银行核心数据遇到国产数据库
  19. Category底层原理实现
  20. C#-Winform知识点

热门文章

  1. 101/103/104规约应用典型问题例举【转】
  2. 三级联动数据库添加html,前端jQuery最新省市区三级联动插件,包含sql数据库
  3. Linux下配置ip地址
  4. MATLAB大作业——美图秀秀
  5. JAVA语言发展介绍【尚学堂java300集视频学习笔记】
  6. MYSQL课程设计——图书管理系统(一)
  7. 深入浅出通信原理知识点3
  8. linux万年历,万年历_十万代码进行时的技术博客_51CTO博客
  9. 数据结构-二叉树[递归实现](构造,析构,先序遍历,中序遍历,后续遍历,层次遍历)
  10. 解释一下什么是servlet?