基于寄存器&标准外设库的LED流水灯

前言

实验1:以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只_(或更多)红绿蓝LED 搭建电路,使用GPIOA/GPIOB/GPIOC端口控制LED灯,轮流闪烁,间隔时长1秒。

1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;

2)用C语言寄存器方式编程实现,代码须有详细注解。

3)STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。请查阅最小版电路原理图和相关资料,将这个灯也用在流水灯中,重编新程序。

实验2:在实验1的基础上,改用标准外设库方式使用某个端口GPIOx端口管脚控制几个LED灯,轮流闪烁,间隔时长1秒。

1)写出工程项目创建文件夹、添加STM32标准外设库文件(*.c,*.h)的详细过程;

2)LED灯的亮/灭周期是通过软件循环延时完成的,其准确周期大致是多少呢? 

  在没有示波器条件下,可以使用Keil的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点。 请用此功能观察GPIO端口的输出波形,并分析时序状态正确与否、高低电平转换周期(LED闪烁周期)实际为多少。


目录

前言

 一.认识STM32

 二.基于寄存器点亮LED流水灯

2、初始化GPIO口:

1.创造工程

 2.编写代码

3. 外接红、黄灯和利用STM已有接好的PC13上的绿灯来形成流水灯

三.基于标准外设库点亮LED流水灯

 1.创造一个新工程,在工程文件夹下创造以下文件夹

2.添加标准库的启动文件 

 3. 添加核心文件、库文件

4.添加设备支持文件 

5. 点击魔法棒右边的按钮,创建新文件夹,输入刚才创建的文件夹的名字,然后将文件添加其中

6.MDK5环境配置及编译

四.总结


 一.认识STM32

       STM32系列专为要求高性能、低成本、低功耗嵌入式应用设计ARM Cortex®-M0,M0+,M3, M4和M7内核 (ST's product portfolio contains a comprehensive range of microcontrollers, from robust, low-cost 8-bit MCUs up to 32-bit ARM-based Cortex®-M0 and M0+, Cortex®-M3, Cortex®-M4 Flash microcontrollers with a great choice of peripherals. ST has also extended this range to include an ultra-low-power MCU platform)。按内核架构分为不同产品:

       主流产品(STM32F0、STM32F1、STM32F3)、超低功耗产品(STM32L0、STM32L1、STM32L4、STM32L4+)、高性能产品(STM32F2、STM32F4、STM32F7、STM32H7)

 而本次实验采用的为STM32最小系统核心板(STM32F103C8T6),

 二.基于寄存器点亮LED流水灯

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

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

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

GPIO端口的初始化设置
       GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103VET6 型号芯片有 GPIOA、GPIOB、GPIOC至 GPIOE共 5组 GPIO,芯片一共 100个引脚,其中 GPIO就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。

      点亮LED灯,实现流水灯效果需要用到GPIO端口。为了点亮LED灯,进行以下三个步骤:

1.打开GPIO口的时钟
2.初始化GPIO口(选择推挽输出)
3.设置低电平
 1、打开时钟:
GPIO的地址:

时钟的地址:

0x40021018,则打开三个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口有八种模式:

输入浮空,输入上拉,输入下拉,模拟输入,开漏输出,推挽式输出,推挽式复用功能,开漏复用功能

 本次实验使用推挽输出:

1.创造工程

选择对应型号,我这里是STM32F103C8T6

从固件库中添加 startup_stm32f10x_md.s进来,将文件复制到工程目录下,方便使用

https://www.st.com/zh/embedded-software/stm32-standard-peripheral-libraries.html

STM32标准外设库官方下载地址

 

 

接着打开魔术棒,如下图所示勾选Create HEX File:  

 2.编写代码

主程序main.c如下

#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
 
#define RCC_APB2ENR (*(unsigned int *)0x40021018)//时钟控制地址
 
#define GPIOB_CRL (*(unsigned int *)0x40010C00)//PB端口低位控制地址
#define GPIOC_CRH (*(unsigned int *)0x40011004)//PC端口高位控制地址
#define GPIOA_CRL (*(unsigned int *)0x40010800)//PA端口低位控制地址
 
#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 D_LED_LIGHT(void);
void E_LED_LIGHT(void);

void Delay_ms( volatile  unsigned  int  t) //延时函数
{
     unsigned  int  i;
         for (i=0;i<t;i++)
    {
        
    }
}
 
void A_LED_LIGHT(){
    GPIOA_ODR=0x1<<4;        //PA4高电平
   
    GPIOB_ODR=0x0<<0;        //PB0低电平
    GPIOC_ODR=0x1<<15;       //PC15高电平
}
void B_LED_LIGHT(){
    GPIOA_ODR=0x0<<4;        //PA4低电平
    
    GPIOB_ODR=0x1<<0;        //PB0低电平
    GPIOC_ODR=0x1<<15;       //PC15高电平
}
void C_LED_LIGHT(){
    GPIOA_ODR=0x1<<4;        //PA4高电平
    
    GPIOB_ODR=0x1<<0;        //PB0高电平
    GPIOC_ODR=0x0<<15;       //PC15低电平    
}
 
int main(){
    
    // 开启时钟
    RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟
    RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟
    RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟
    
    
    // 设置 GPIO 为推挽输出
    GPIOB_CRL&=  0xfffffff0;//设置位 清零        
    GPIOB_CRL|=  0x00000002;//PB0推挽输出
 
    GPIOC_CRH &= 0x0fffffff;//设置位 清零        
    GPIOC_CRH|=  0x30000000;//PC15推挽输出
 
 
    GPIOA_CRL &= 0xfff0ffff;//设置位 清零        
    GPIOA_CRL|=  0x00010000;//PA4推挽输出
    
 
    //初始化
    GPIOB_ODR |= (1<<0); 
    GPIOC_ODR |= (1<<15); 
    GPIOA_ODR |= (1<<4);  
    
    while(1){       
        A_LED_LIGHT();
        Delay_ms(1000000);
 
        B_LED_LIGHT();
        Delay_ms(1000000);
 
        C_LED_LIGHT();
        Delay_ms(1000000);
    }
    
}
 
 
void SystemInit(){
    
}//骗过编译器

点击编译生成.hex文件,用于单片机运行

选择上述生成的.hex文件,将其程序烧录至单片机中 

串口连接,led灯连A4,B0,C15,TXD——A10,RXD——A9. 

 效果如下

3. 外接红、黄灯和利用STM已有接好的PC13上的绿灯来形成流水灯

更改代码如下

 
#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<<13;		//PC13高电平
}
void B_LED_LIGHT(){
	GPIOA_ODR=0x1<<4;		//PA4高电平
	GPIOB_ODR=0x0<<9;		//PB9低电平
	GPIOC_ODR=0x1<<13;		//PC13高电平
}
void C_LED_LIGHT(){
	GPIOA_ODR=0x1<<4;		//PA4高电平
	GPIOB_ODR=0x1<<9;		//PB9高电平
	GPIOC_ODR=0x0<<13;		//PC13低电平	
}
 
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 &= 0xff0fffff; //设置位 清零		
	GPIOC_CRH|=0x00300000;  //PC15推挽输出
 
 
	GPIOA_CRL &= 0xfff0ffff; //设置位 清零		
	GPIOA_CRL|=0x00010000; //PA4推挽输出
 
	// 3个LED初始化为不亮(即高点位)
	GPIOB_ODR |= (1<<9); 
	GPIOC_ODR |= (1<<13); 
	GPIOA_ODR |= (1<<4);  
	
	while(j){
		
		B_LED_LIGHT();
		Delay_ms(2000);//单片机上2000    100
 
		C_LED_LIGHT();
		Delay_ms(3000);//单片机上3000    200
 
		A_LED_LIGHT();
		Delay_ms(3000);//单片机上3000    200
	}
	
}
 
 
void SystemInit(){
	
}

 单片机串口LED连接PB9、PC13、PA4,效果如下

三.基于标准外设库点亮LED流水灯

 1.创造一个新工程,在工程文件夹下创造以下文件夹

CORE,Divice,HARDWARE,Library,START

2.添加标准库的启动文件 

  stm32标准库的启动文件根据Flash容量大小一般分为三种:                   
                           startup_stm32f10x_ld.s
                           startup_stm32f10x_md.s
                           startup_stm32f10x_hd.s

其中,ld.s适用于小容量 产品;md.s适用于中等容量产品;hd适用于大容量产品;
这里的容量是指FLASH的大小.判断方法如下:
                        小容量:FLASH≤32K
                        中容量:64K≤FLASH≤128K
                        大容量:256K≤FLASH

                   在添加标准库之前需要查看自己芯片的FLASH容量大小,本次实验使用的芯片型号是stm32f103zec8t6最小系统板,查阅资料该新芯片的FASH容量为64k,因此需要添加的启动项文件是中容量版本:startup_stm32f10x_md.s

该文件在固件库目录如下 ,将其复制粘贴至START文件夹内,

\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm

 3. 添加核心文件、库文件

 库文件路径为\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver

 将这两个文件夹都复制粘贴至Library文件夹内

核心文件路劲如下 \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport

 

将文件复制粘贴至CORE内 

4.添加设备支持文件 

 一共六个,前三个路径如下D:\MDK5\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x

 

 后三个路径如下\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template

 

 将其复制到Divice文件夹中

5. 点击魔法棒右边的按钮,创建新文件夹,输入刚才创建的文件夹的名字,然后将文件添加其中

 

 

 

6.MDK5环境配置及编译

其中第2步输入以下代码

 USE_STDPERIPH_DRIVER,STM32F10X_MD

在第6步中,将刚才创建的文件夹加入其中

 

随便创建一个空函数,编译,无报错即成功

 7.创造主函数main.c,代码如下

#include "stm32f10x.h"
#include "led.h"
void delay(unsigned int  time)//软件延时
{
 unsigned int i=0;
	while(time--)
	{
   i=12000;
   while(i--);		
	}
}

int main()
{
  GPIOA_Init();//初始化端口
	
	while(1)
	{
	  GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_0); //PA0为1
		GPIO_SetBits(GPIOA,GPIO_Pin_0); //PA0为1
	    delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_1); //PA1为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_2); //PA2为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_3); //PA3为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_4); //PA4为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA5为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_6); //PA6为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_7); //PA7为1
		delay(800);//延时
	
	}
	
	
}

创建led.c,代码如下,创建一个新文件夹HARDWARE,将led.c保存在其中

#include "led.h"
#include "stm32f10x.h"

void GPIOA_Init()
{
	GPIO_InitTypeDef GPIOA_Structure;//创建CPIO结构体
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//APB2 GPIOA时钟使能	
    GPIOA_Structure.GPIO_Pin=GPIO_Pin_All; //所有端口
	GPIOA_Structure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIOA_Structure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度为50Mhz
    GPIO_Init(GPIOA,&GPIOA_Structure);//将GPIOA所有端口设置为50Mhz的推挽输出模式	
}

创建led.h,代码如下

#ifndef LED_H
#define LED_H
void GPIOA_Init();
#endif

7.stm32连接及效果

led连接A0~A7,TXD——A10,RXD——A9. 

 效果如下

四.总结 

本实验旨在通过使用寄存器和标准外设库,设计一个 LED 流水灯的控制程序。通过该实验,我们可以进一步了解寄存器的操作与使用,同时熟悉标准外设库的使用方法,提高嵌入式系统的编程能力。
通过本次实验,我们深入了解了寄存器的操作和标准外设库的使用。掌握了使用寄存器和标准外设库来控制 LED 流水灯的技术方案。在实施实验的过程中,我们要仔细检查硬件连接是否正确,并且注意安全操作的问题,避免出现短路或者其他损坏的情况,因此,  在实验过程中我学会了怎么使用串口连接单片机

总而言之,这个实验是一个很好的嵌入式系统的入门实验,通过对寄存器和标准外设库的学习和应用,使我们更好地了解了嵌入式系统的原理和操作,提高了我们的编程能力和实践能力。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值