1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 嵌入式“Hello World!”——点亮流水灯

嵌入式“Hello World!”——点亮流水灯

时间:2022-04-13 23:33:49

相关推荐

嵌入式“Hello World!”——点亮流水灯

目录

一、初识STM321.什么是STM322.STM32的应用场景3.STM32产品命名规则二、STM32F103C8T6介绍1.简要说明2.基本参数3.最小系统板三、STM32F103系列芯片寄存器映射1.什么是寄存器2.什么是存储器映射3.确定寄存器地址的方法4.实例说明四、GPIO端口初始化1.时钟配置2.输入输入配置及最大速率的配置五、实例操作1.配置时钟2.确定寄存器地址。3.配置寄存器输入输出模式六、代码实现1.新建工程2.编写代码七、软件编译1、改变环境配置2、在调试之前先编译,编译成功后再继续下一步;3、调试观看波形八、硬件实现1.需要的软件及工具2.生成hex文件3.接线4.演示视频

操作STM32 有使用官方库函数(嵌入式下的Hello World——点亮流水灯(使用库函数))和 直接操作寄存器的方法。在之前的博客中简单介绍了一下寄存器寻址以及相关GPIO的的初始化,可以转到之前的博客了解学习。在此博客下进行讲解

一、初识STM32

1.什么是STM32

STM32,从字面上来理解,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示32 位,合起来理解,STM32 就是指 ST 公司开发的 32 位微控制器。在如今的 32 位控制器当中,STM32 可以说是最璀璨的新星。

STM32F103 采用的是 Cortex-M3 内核,内核即 CPU,由 ARM 公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如 ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如 GPIO、USART(串口)、I2C、SPI 等都叫做片上外设。具体下图。

2.STM32的应用场景

STM32系列基于专门要求高性能,低成本,低功耗的嵌入式应用专门设计的ARM Cortex-M0,Cortex-M1,Cortex-M3,Cortex-M4,Cortex-M7等。其中Cortex-M0主打的是低功耗和混合信号的处理,M3主要用来替代ARM7,重点侧重能耗和性能的均衡,而M7则重点放在高性能控制运算领域。

STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、I2C、SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。

3.STM32产品命名规则

STM32F103C8T6为例:

(1) STM32:表示32位MCU;

(2) F103:表示基础型;

(3) C:表示芯片上含48个引脚;

(4) 8:表示闪存容量为64K字节;

(5) T:表示QFP封装;

(6) 6:表示工作温度范围在-40°C到85°C;

二、STM32F103C8T6介绍

1.简要说明

STM32F103C8T6是一款基于ARM Cortex-M内核STM32系列的32位的微控制器,程序存储器容量是64KB,需要电压2V~3.6V,工作温度为-40°C ~ 85°C。

其中, Cortex-M3摒弃了冯· 诺依曼结构(普林斯顿结构),采用了将指令存储和数据存储分开的 的哈佛结构(Harvard Architecture ),这样一来Cortex-M3同时拥有了独立的32-bit指令总线和32-bit数据总线,数据访问将不再占用指令总线,同时读取指令和数据后提升了MCU运行速度。冯诺依曼和哈弗结构的宏观对比如下图所示:

2.基本参数

3.最小系统板

【产品介绍】

该系统板是基于STM32F103C8T6为主芯片的ARM核心板,有如下特点:

1、板载了基于MCU的最基本电路,如晶振电路、USB电源管理电路和USB接口等。

2、核心板引出了所有的I/O口资源。

3、带有SWD仿真调试下载接口,该接口最少需要3根线就可以完成调试下载任务,相比传统的JTAG调试有不少的好处

4、外形尺寸只有传统的DIP40封装(例如AT89S52)的大小,目前还未在淘宝上发现比我们更小的同规格的核心板。

5、使用了目前智能手机所使用的Mirco USB接口,使用方便,可做USB通讯和供电。

6、针对STM32 RTC不起振的问题,我们采用了官方建议的低负载RTC晶振方案,并使用了爱普生品牌的晶振,而没有使用廉价的圆柱晶振。

7、配有优质的1*40/2.54mm间距的单排排针,确保导电接触优良,方便用户将核心板放置到标准的的万用板或者面包板上。排针默认不焊接,用户可以根据自己的需要选择焊接方向。

三、STM32F103系列芯片寄存器映射

1.什么是寄存器

寄存器是中央处理器内的组成部分,是有限容量的高速存贮部件,可用来暂存指令、数据、地址等。

简单来说,寄存器用来存放东西的地方,类似于行李寄存,只不过,寄存器存放的不是行李,而是指令、数据、地址等。不同的·数据会存放在不同的寄存器中,通过地址区分不同的寄存器

2.什么是存储器映射

存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,如下图所示。如果给存储器再分配一个地址就叫存储器重映射。

在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。

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

3.确定寄存器地址的方法

片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1挂载低速外设,APB2 和 AHB 挂载高速外设。

相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中 APB1 总线的地址最低,片上外设从这里开始,也叫外设基地址。

(1)确定GPIOx总线基地址

上述表格的“相对外设基地址偏移”即该总线地址与“片上外设”基地址 0x4000 0000的差值。

(2)找到GPIOx外设基地址

总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX 外设基地址”,也叫 XX 外设的边界地址。

在此,以 GPIO 外设来讲解外设的基地址,GPIO 属于高速的外设 ,挂载到APB2 总线上:

(3)确定端口输入数据寄存器

其中,

位31:16 保留(写程序时不用管),始终读为0;

位15:0 IDRy[15:0]:端口输入数据(y =0…15),这些位为只读并只能以**字(16位)**的形式读出。读出的值为对应I/O口的状态。

-------------------------------------------------【总结】-------------------------------------------------

GPIOA下的某个寄存器,挂载在GPIOA下,地址为GPIOA基地址+偏移量

GPIOA挂载在APB2总线,地址为APB2总线基地址+GPIOA偏移量

ABP2挂载加外设基地址,地址为外设基地址+ABP2偏移量

//外部总线基地址#define PERIPH_BASE((uint32_t)0x40000000)//APB2基地址=外部总线基地址+偏移量#define APB2PERIPH_BASE(PERIPH_BASE + 0x10000)//GPIOA基地址=APB2基地址+偏移量#define GPIOA_BASE(APB2PERIPH_BASE + 0x0800)//GPIOA将地址顺序分配给7个32位寄存器(结构体分配)#define GPIOA((GPIO_TypeDef*)GPIOA_BASE)//将寄存器地址映射到7个32位寄存器,分别控制typedef struct{__IO unit32_t CRL;__IO unit32_t CRH;__IO unit32_t ODR;__IO unit32_t IDR;__IO unit32_t BSRR;__IO unit32_t BRR;__IO unit32_t LCKR;}GPIO_TypeDef;

4.实例说明

比如,我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x4001 0C0C,ODR 寄存器是 32bit,低 16bit有效,对应着 16 个外部 IO,写 0/1 对应的 IO 输出低/高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB 的 16 个 IO 都输出高电平:

(1)通过绝对地址访问内存单元

1 // GPIOB 端口全部输出 高电平2 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;

0x4001 0C0C在我们看来是 GPIOB端口 ODR的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针,即(unsigned int *)0x4001 0C0C,然后再对这个指针进行 * 操作。

(2)通过寄存器别名方式访问内存单元

1 // GPIOB 端口全部输出 高电平2 #define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)3 * GPIOB_ODR = 0xFF;

(3)通过寄存器别名访问内存单元

1 // GPIOB 端口全部输出 高电平2 #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)3 GPIOB_ODR = 0xFF;

四、GPIO端口初始化

第三章节的介绍只是GPIO端口配置的基础,因为后续的所有操作都基于寄存器操作,而对于寄存器的操作就是通过访问地址实现的,因此在正式进行GPIO的初始化之前需要掌握寄存器地址访问方法。

GPIO口的初始化大致分成三部分:时钟配置;输入输出模式配置;最大速率配置。

1.时钟配置

任何外设都需要时钟,51单片机,STM32等等,因为寄存器是由D触发器组成的,往触发器里面写东西,前提条件是有时钟输入。

51单片机不需要配置时钟,是因为一个时钟开了之后所有的功能都可以使用,而在51中这个时钟是默认开启的,但是也存在一个问题,耗能大。

STM32之所以是低功耗,因为它将所有的门都默认设置为disable,在你需要用哪个门的时候,开哪个门就可以,也就是说用到什么外设,只要打开对应外设的时钟就可以,其他的没用到的可以还是disable,这样耗能就会减少。

根据数据手册可知,抚慰和时钟控制RCC(时钟控制)地址从0x4002 1000开始,根据外设偏移地址为0x18,可以确定APB2的地址为0x4002 1018,根据下图可得GPIOB的时钟使能控制位为第3位,因此将位3赋值为1,即开启GPIOB的时钟代码如下:

RCC_APB2ENR |= (1<<3);

2.输入输入配置及最大速率的配置

IO:表示“input”、“output”,就可以当作输入也可以当作输出,因此需要通过配置来确定状态。

STM32的每个IO都需要4位进行配置,因此一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。

配置引脚PB0,使用的寄存器是GPIOB_CRLCRL 中包含 0-7 号引脚,每个引脚占用 4 个寄存器位。MODE位用来配置输出的速度,CNF 位用来配置各种输入输出模式。

在代码实现中,先将控制 PB0 的端口位清 0,然后再向它赋值“0001 b”,从而使

GPIOB0 引脚设置成输出模式,速度为 10M。

五、实例操作

1、STM32开发板中包含较多的寄存器,实现流水灯操作,需要对相应的引脚进行操作,对相应的引脚进行时钟使能配置、输入输出配置(高or低)配置、最高速率配置。直接操作寄存器大致分为以下几步:

(1)配置时钟使能;

(2)配置输入输出;

(3)配置端口输出寄存器;

(4)烧录程序;

(5)运行。

1.配置时钟

任何外设都需要时钟,51单片机,STM32等等,因为寄存器是由D触发器组成的,往触发器里面写东西,前提条件是有时钟输入。

51单片机不需要配置时钟,是因为一个时钟开了之后所有的功能都可以使用,而在51中这个时钟是默认开启的,但是也存在一个问题,耗能大。

STM32之所以是低功耗,因为它将所有的门都默认设置为disable,在你需要用哪个门的时候,开哪个门就可以,也就是说用到什么外设,只要打开对应外设的时钟就可以,其他的没用到的可以还是disable,这样耗能就会减少。

根据数据手册可知,复位和时钟控制RCC(时钟控制)地址0x4002 1000开始,根据第③步外设偏移地址为0x18,可以确定APB2的地址为0x4002 1018,根据下图可得GPIOA、GPIOB、GPIOC的时钟使能控制位分别为第2位、第3位、第4位,因此将这几位赋值为1,即开启时钟。

代码如下:

//----------------APB2使能时钟寄存器 ---------------------#define RCC_APB2ENR*((unsigned volatile int*)0x40021018)RCC_APB2ENR|=1<<2|1<<3|1<<4;//APB2-GPIOA、GPIOB、GPIOC外设时钟使能

2.确定寄存器地址。

通过查找数据手册—“寄存器映射”该章节,不难得出GPIOA、GPIOB、GPIOC的地址分别为0X4001 08000X4001 0C000X4001 1000

第①种方法:可以直接定义寄存器地址

//----------------GPIOA寄存器地址 -----------------------#define GPIOA*((unsigned volatile int*)0x40010800)//----------------GPIOB寄存器地址 -----------------------#define GPIOB *((unsigned volatile int*)0x40010C00)//----------------GPIOC寄存器地址 -----------------------#define GPIOC*((unsigned volatile int*)0x40011004)

第②种方法:通过总线+地址偏移量进行定义:

/*片上外设基地址 */#define PERIPH_BASE ((unsigned int)0x40000000)/*总线基地址,GPIO 都挂载到 APB2 上 */#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)/*GPIOA 外设基地址*/#define GPIOB_BASE (APB2PERIPH_BASE + 0x0800)/*GPIOB 外设基地址*/#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)/*GPIOB 外设基地址*/#define GPIOB_BASE (APB2PERIPH_BASE + 0x1004)

3.配置寄存器输入输出模式

STM32的每个IO都需要4位进行配置,因此一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。因此在本次实验中用到的三个引脚A5、B9、C14。端口A5需要用低寄存器(GPIOx_CRL)来配置,B9、C14需要高寄存器(GPIOx_CRH)来配置。

在本次实验中采用通用推挽输出模式,最高输出时钟频率为2Mhz。

(1)配置对应引脚寄存器,寄存器地址+偏移量

根据数据手册,可得低寄存器(GPIOx_CRL)在寄存器地址偏移量为0x00,高寄存器(GPIOx_CRH)在寄存器地址偏移量为0x04

代码如下://----------------GPIOA配置寄存器 -----------------------#define GPIOA_CRL*((unsigned volatile int*)0x40010800)//----------------GPIOB配置寄存器 -----------------------#define GPIOB_CRL*((unsigned volatile int*)0x40010C00)//----------------GPIOC配置寄存器 -----------------------#define GPIOC_CRH*((unsigned volatile int*)0x40011004)

**(2)配置输入输出模式及最大时钟速率。

根据数据手册,配置输入输出模式及最大时钟速率。

代码如下:

GPIOA_CRL&=0xFF0FFFFF;//设置位 清零GPIOA_CRL|=0x00200000;//PA5推挽输出,把第23、22、21、20变为0010GPIOA_ODR&=~(1<<4);//设置初始灯为灭GPIOB_CRH&=0xFFFFFF0F;//设置位 清零GPIOB_CRH|=0x00000020;//PB9推挽输出,把第7、6、5、4变为0010GPIOB_ODR&=~(1<<5);//设置初始灯为灭GPIOC_CRH&=0xF0FFFFFF;//设置位 清零GPIOC_CRH|=0x02000000;//PC14推挽输出,把第27、26、25、24变为0010GPIOC_ODR&=~(1<<14);//设置初始灯为灭

六、代码实现

1.新建工程

在Keil中新建工程,并将c函数添加到新建工程中。Keil工程的创建方法博主之前的博客中已经提到,大家可以转到此进行学习。

MDK-keil安装与使用,在该博客中,创建的是汇编文件,但是创建方法相同。

2.编写代码

在创建的c文件中写入以下函数,代码仅供参考。不同端口的配置,代码会发生微小的变化,需要根据自己的内容进行改写。

#include "stm32f10x.h"//----------------APB2使能时钟寄存器 ---------------------#define RCC_APB2ENR*((unsigned volatile int*)0x40021018)//----------------GPIOA配置寄存器 -----------------------#define GPIOA_CRL*((unsigned volatile int*)0x40010800)#defineGPIOA_ODR*((unsigned volatile int*)0x4001080C)//----------------GPIOB配置寄存器 -----------------------#define GPIOB_CRH*((unsigned volatile int*)0x40010C04)#defineGPIOB_ODR*((unsigned volatile int*)0x40010C0C)//----------------GPIOC配置寄存器 -----------------------#define GPIOC_CRH*((unsigned volatile int*)0x40011004)#defineGPIOC_ODR*((unsigned volatile int*)0x4001100C)//延时函数void Delay(){u32 i=0;for(;i<5000000;i++);}int main(void){RCC_APB2ENR|=1<<2|1<<3|1<<4;//APB2-GPIOA、GPIOB、GPIOC外设时钟使能GPIOA_CRL&=0xFF0FFFFF;//设置位 清零GPIOA_CRL|=0x00200000;//PA5推挽输出,把第23、22、21、20变为0010GPIOA_ODR&=~(1<<4);//设置初始灯为灭GPIOB_CRH&=0xFFFFFF0F;//设置位 清零GPIOB_CRH|=0x00000020;//PB9推挽输出,把第7、6、5、4变为0010GPIOB_ODR&=~(1<<5);//设置初始灯为灭GPIOC_CRH&=0xF0FFFFFF;//设置位 清零GPIOC_CRH|=0x02000000;//PC14推挽输出,把第27、26、25、24变为0010GPIOC_ODR&=~(1<<14);//设置初始灯为灭while(1){//A灯GPIOA_ODR|=1<<5;//PB5高电平Delay();GPIOA_ODR&=~(1<<5);//PB5低电平,因为是置0,所以用按位与//B灯GPIOB_ODR|=1<<9;//PB9高电平Delay();GPIOB_ODR&=~(1<<9);//PB5低电平,因为是置0,所以用按位与//C灯GPIOC_ODR|=1<<14;//PB14高电平Delay();GPIOC_ODR&=~(1<<14);//PB5低电平,因为是置0,所以用按位与}}

七、软件编译

进行软件编译,观察端口输出波形。

1、改变环境配置

点击“魔法棒”工具—“Debug”-选中“Use Simulater

修改Dialog DLL内容为“DARMSTM.DLL”(只要是使用STM芯片这个内容填这个就OK);

修改Parameter内容为“-p芯片选型”(不同芯片选型这里就不同)

2、在调试之前先编译,编译成功后再继续下一步;

编译成功!

3、调试观看波形

点击“d”调试按钮—点击“System Analyzer Window”“Logic Analyzer”—点击“setup”,设置要观看的波形。

如果要观察GPIOx_y号端口的输出,在中输入“PORTx.y”,假设要观察GPIOA_5号端口的输出,在中输入“PORTA.5

GPIOA_5波形如图所示:

GPIOB_9波形如图所示:

GPIOC_14波形如图所示:

八、硬件实现

1.需要的软件及工具

keil,FlyMcu、USB-TTL、STM32最小核心板、面包板、三个LED、若干跳线。

2.生成hex文件

在烧录文件时,实质上是将HEX文件,烧录到单片机中,因此需要在keil环境配置中,生成Hex文件

点击“魔法棒”工具—“Output“—选中Create HEX File

3.接线

(1)面包板介绍

(2)经过查找资料,得知STM32的最小系统板:“PA9”-“RX”;“PA10”-“TX”

USB-TTL的TX接口与STM32的RX接口相连,USB-TTL的RX接口与STM32的TX接口相连.

(3)LED接线

将LED插在面包板上,注意方向;LED负极接地(STM32最小系统板的地),LED正极与单片机GPIO口相连。

实物图如图所示:

(4)将程序下载到单片机中。

由于FlyMcu在一年前就已经装好,网上资料也很多在这里跳过FlyMcu的安装,直接进行烧录。

首先,将USB-TTL与STM32核心板Tx、Rx接线接好,之后将VCC3与GND相连。

其次,将USB—TTL插到电脑上;

最后,使用FlyMcu进行烧录;

最终烧录成功!

4.演示视频

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。