实验要求:以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED 搭建电路,使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯,轮流闪烁,间隔时长1秒。1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;2)分别用汇编语言,C语言编程实现。

目录

一、实验原理

1、寄存器映射

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

3、设计思路

二、实验步骤

1、了解寄存器

2、查找寄存器地址

3、初始准备

4、代码

5、编译生成HEX文件

6、硬件连接

三、实验结果

四、实验总结

五、参考文献


一、实验原理

1、寄存器映射

每个寄存器占用32bit,对寄存器操作的初步内容是要找到寄存器的起始地址。这一步,可以通过查找数据手册完成,那么怎么使用这个手册呢?

要知道,寄存器的映射地址等于分三步进行,首先是找到是GPIOB的基地址、然后找到端口输入寄存器的地址偏移,最后找到对应的端口输入数据。

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

(1)时钟配置

为了降低功耗,首先要配置时钟使能,配置的时钟的信息可以通过查阅手册获得(时钟控制的名  字叫作RCC)。

为了配置时钟使能,首先要知道时钟使能寄存器的地址,然后查找手册将对应功能的位赋值为1,就是开启GPIOB时钟

(2)输入输出模式设置

LED流水灯为了能够输出高电平低电平,所以需要配置为输出。找到对应的寄存器的地址,通过    阅读资料可以知道一共有四种模式的寄存器输出,根据LED流水灯的功能得知,这里设置为通用    推挽输出(可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信      号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源决定。)

(3)最大速率设置

对于STM32F103系列的芯片最快的配置是50Mhz,但是速度并不是越快越好,速度配置越高,噪声越大,功耗越大,所以这里要配置适当的速度。

3、设计思路

流水灯的设计思路为:控制寄存器输出1,点亮LED灯,在一段时间之后,再控制LED灯灭,然后加入循环语句,呈现出的效果,就是流水灯。

二、实验步骤

根据以上的实验原理,开始正式的实验。

1、了解寄存器

(1)GPIO寄存器

在这里我们使用的是GPIO寄存器,GPIO寄存器按照功能划分可以划分为许多类:

每个GPIO端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH,也叫做控制寄存器),

两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR),

一个32位置位/复位寄存器(GPIOx_BSRR),

一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。

在这里我们使用到的是端口配置低寄存器(GPIOx_CRL)

(2)GPIO_CRL

STM32的CRL控制着每个IO端口(A-G)低8位的模式,每个IO端口的位占用CRL的4个位,高两位为CNF,低两位为MODE,其实CRH的作用与CRL完全一样,只是CRH控制的是高8位,而CRL控制的是低8位。

2、查找寄存器地址

为了配置寄存器的端口实现各种功能,我们先需要找到端口的地址。

在这里我们通过查找STM32F103中文教程及参考手册,来找到端口的地址。

并不是只有这种复杂的方法来给端口赋值,但是这么做更有助于我们理解寄存器的原理。

这里列举了几个常用到的作为例子。

(1)先找到GPIOB端口的起始地址,为0X4001 0C00

(2)找到GPIO寄存器的端口配置低寄存器的偏移地址为0X00

由此,可以得出GPIOB->CRL的地址为0X4001 0C00

(3)找到时钟使能寄存器的地址为RCC_APB2ENR寄存器的位3控制GPIOB时钟的打开与关闭,置1控制时钟的开启

(4)找到端口配置低寄存器(GPIOx_CRL)

CNF位用来配置输入输出模式,这里根据功能选择通用推挽输出模式,通过以下这段代码实现

GPIOC_CRH &= ~(0x0F<<(4*5));
GPIOC_CRH |= (1<<(4*5));

MODE位用来配置速率,这里选择最大速度2MHz

(5) 找到端口输出寄存器(GPIOx_ODR)

端口输出寄存器是引脚电平输出的寄存器,需要通过控制它,来实现LED灯的亮灭。

3、初始准备

(1)建立工程模板

为了更好地学习stm32,调用库函数,建立工程模板是十分必要的。新建一个总文件夹,用来存放本次工程的所有程序,然后再建CORE、HARDWARE、OBJ、FWLIB、SYSTEM、USER这六个文件夹。

HARDWARE文件夹是用来存放外设硬件代码,OBJ用来存放生成调试代码,FWLIB是各种.c和.h文件

其中的资料在这里获得正点原子openedv资料下载地址,我下载的是mini板的rct6资料,如下

 我建立的文件夹,现在需要做的事情,就是将资料中的文件,转移到自己所建立的对应的文件夹

每个文件夹中的内容如下

  • CORE

  • HARDWARE 
  • STM32F10x_FWLib

  • SYSTEM

  • USER

(2)建立工程

打开keil软件

单击keil菜单栏中的Project中的New uVision Project 新建一个工程,将其保存到USER文件夹中

设备选择STM32F103C8,然后单击OK

这里注意,在弹出的Manage Runn-time Environment窗口,单击cancel

右击Target 1,选择Manage Project Items…

第二栏右上角,创建组的名称

在每一个组中添加文件,文件在上一步建立的工程的几个文件夹之中,如下

  • CORE

  • HARDWARE

  • SYSTEM

  • USER

  • FWLIB

可以看到添加的分组

(3)配置

右击Target1,选择Options for Target 'Target 1’

Target栏中修改晶振频率值为8

Output栏中点击Select Folder for Objects,选择生成的hex文件存放的目录,这里选择存放在前面建立的OBJ文件夹中,然后勾选Create HEX File,用于生成HEX文件,下载到开发板的

在C/C++一栏中,在Define一栏中填入USE_STDPERIPH_DRIVER,STM32F10X_MD,然后在Include Paths中添加路径如图所示

将最开始建立的几个文件夹添加进入路径,然后单击OK  到这里就基本上完成了新建工程模板。

(4)配置GPIO端口

根据实验原理中提到的GPIO端口的初始化设置三步骤,配置GPIO端口

首先是时钟设置

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //开启 GPIOB 端口时钟

然后是输入输出设置和最大速率设置,这里选择的输出模式为通用推挽输出,最大速率是2M

 GPIO_InitTypeDef   GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;             //输出模式为通用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4 ;                     //选定输出端口为GPIO_Pin_4GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;             //输出速度为2MGPIO_Init(GPIOA,&GPIO_InitStruct);

4、代码

(1)led.h

#ifndef _LED_H
#define _LED_H#include "stm32f10x.h"
void LED_R_TOGGLE(void);
void LED_G_TOGGLE(void);
void LED_Y_TOGGLE(void);
void LED_Init(void);
#endif

(2)led.c

#include "led.h"
#include "delay.h"void LED_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);  //打开外设GPIOB的时钟GPIO_InitTypeDef   GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;              //输出模式为通用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4 ;             //选定端口为GPIO_Pin_4GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;               //输出速度为2MGPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;             //输出模式为通用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10 ;             //选定端口为GPIO_Pin_1GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;              //输出速度为2MGPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;             //输出模式为通用推挽输出GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14 ;             //选定端口为GPIO_Pin_14GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;             //输出速度为2MGPIO_Init(GPIOC,&GPIO_InitStruct);
}void LED_R_TOGGLE(void)
{GPIO_SetBits(GPIOA, GPIO_Pin_4);delay_ms(500);GPIO_ResetBits(GPIOA,GPIO_Pin_4);
}
void LED_G_TOGGLE(void)
{GPIO_SetBits(GPIOB, GPIO_Pin_10);delay_ms(500);GPIO_ResetBits(GPIOB,GPIO_Pin_10);
}
void LED_Y_TOGGLE(void)
{GPIO_SetBits(GPIOC, GPIO_Pin_14);  delay_ms(500);GPIO_ResetBits(GPIOC,GPIO_Pin_14);
}

(3)delay.h

#ifndef __DELAY_H
#define __DELAY_H
#include "sys.h"  void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);#endif

(4)delay.c

#include "delay.h"
//
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"                 //ucos 使用
#endif static u8  fac_us=0;                            //us延时倍乘数
static u16 fac_ms=0;                           //ms延时倍乘数,在ucos下,代表每个节拍的ms数#if SYSTEM_SUPPORT_OS                            //如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
#ifdef  OS_CRITICAL_METHOD                      //OS_CRITICAL_METHOD定义了,说明要支持UCOSII
#define delay_osrunning     OSRunning           //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OS_TICKS_PER_SEC    //OS时钟节拍,即每秒调度次数
#define delay_osintnesting  OSIntNesting        //中断嵌套级别,即中断嵌套次数
#endif//支持UCOSIII
#ifdef  CPU_CFG_CRITICAL_METHOD                 //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII
#define delay_osrunning     OSRunning           //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OSCfg_TickRate_Hz   //OS时钟节拍,即每秒调度次数
#define delay_osintnesting  OSIntNestingCtr     //中断嵌套级别,即中断嵌套次数
#endif//us级延时时,关闭任务调度(防止打断us级延迟)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD                  //使用UCOSIIIOS_ERR err; OSSchedLock(&err);                           //UCOSIII的方式,禁止调度,防止打断us延时
#else                                           //否则UCOSIIOSSchedLock();                                //UCOSII的方式,禁止调度,防止打断us延时
#endif
}//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD                  //使用UCOSIIIOS_ERR err; OSSchedUnlock(&err);                     //UCOSIII的方式,恢复调度
#else                                           //否则UCOSIIOSSchedUnlock();                          //UCOSII的方式,恢复调度
#endif
}//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHODOS_ERR err; OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);   //UCOSIII延时采用周期模式
#elseOSTimeDly(ticks);                          //UCOSII延时
#endif
}//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{   if(delay_osrunning==1)                        //OS开始跑了,才执行正常的调度处理{OSIntEnter();                           //进入中断OSTimeTick();                         //调用ucos的时钟服务程序               OSIntExit();                              //触发任务切换软中断}
}
#endif//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#if SYSTEM_SUPPORT_OS                           //如果需要支持OS.u32 reload;
#endifSysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8fac_us=SystemCoreClock/8000000;                //为系统时钟的1/8
#if SYSTEM_SUPPORT_OS                           //如果需要支持OS.reload=SystemCoreClock/8000000;             //每秒钟的计数次数 单位为M  reload*=1000000/delay_ostickspersec;      //根据delay_ostickspersec设定溢出时间//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右   fac_ms=1000/delay_ostickspersec;           //代表OS可以延时的最少单位    SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;     //开启SYSTICK中断SysTick->LOAD=reload;                      //每1/delay_ostickspersec秒中断一次   SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;     //开启SYSTICK    #elsefac_ms=(u16)fac_us*1000;                   //非OS下,代表每个ms需要的systick时钟数
#endif
}                                   #if SYSTEM_SUPPORT_OS                           //如果需要支持OS.
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{       u32 ticks;u32 told,tnow,tcnt=0;u32 reload=SysTick->LOAD;                   //LOAD的值             ticks=nus*fac_us;                             //需要的节拍数             tcnt=0;delay_osschedlock();                       //阻止OS调度,防止打断us延时told=SysTick->VAL;                          //刚进入时的计数器值while(1){tnow=SysTick->VAL;  if(tnow!=told){        if(tnow<told)tcnt+=told-tnow;      //这里注意一下SYSTICK是一个递减的计数器就可以了.else tcnt+=reload-tnow+told;        told=tnow;if(tcnt>=ticks)break;                //时间超过/等于要延迟的时间,则退出.}  };delay_osschedunlock();                     //恢复OS调度
}
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{   if(delay_osrunning&&delay_osintnesting==0)    //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)       {        if(nms>=fac_ms)                            //延时的时间大于OS的最少时间周期 { delay_ostimedly(nms/fac_ms);       //OS延时}nms%=fac_ms;                            //OS已经无法提供这么小的延时了,采用普通方式延时    }delay_us((u32)(nms*1000));                   //普通方式延时
}
#else //不用OS时
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{       u32 temp;            SysTick->LOAD=nus*fac_us;                  //时间加载           SysTick->VAL=0x00;                         //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数    do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));     //等待时间到达   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00;                           //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{                 u32 temp;        SysTick->LOAD=(u32)nms*fac_ms;               //时间加载(SysTick->LOAD为24bit)SysTick->VAL =0x00;                           //清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数  do{temp=SysTick->CTRL;}while((temp&0x01)&&!(temp&(1<<16)));       //等待时间到达   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器SysTick->VAL =0X00;                          //清空计数器
}
#endif 

(5)main.c

#include  "stm32f10x.h"
#include "delay.h"
#include "led.h"
int main(void)
{             LED_Init();   delay_init();                   //使用系统滴答定时器、延时初始化while(1)                       //循环亮起{LED_R_TOGGLE();delay_ms(500);                //红灯亮后延时1sLED_G_TOGGLE();delay_ms(500);             //绿灯亮后延时1sLED_Y_TOGGLE();delay_ms(500);             //黄灯亮后延时1s}
}

5、编译生成HEX文件

编译成功,生成HEX文件

6、硬件连接

(1)使用面包板搭建电路

对照芯片手册,在面包板上搭建电路,接法如下

(2)安装串口驱动

在电脑上安装usb-串口驱动程序,在设备管理器中可以搜索到一个新的串口设备(COM3)

(3)烧录

运行烧录软件mcuisp,配置

  • 点击搜索串口,将自动搜索串口,在bps中选择波特率为 256000
  • 左下角设置:DTR 的低电平复位,RTS 高电平进 BootLoader
  • 读器件信息,这时如果出现界面中的信息,表明串 口和单片机连接成功。
  • 选择hex文件
  • 点击开始编程就可以下载程序

HEX文件以经成功烧录到芯片中。

三、实验结果

可以看出流水灯 的效果,红灯、绿灯、黄灯轮流闪烁

四、实验总结

这次的实验对我来说很有挑战性,最初可以说是毫无头绪,但是后来通过不断查阅资料和询问同学,解决了一个个的难题。在动手实践的过程中,我也一边学习理解着有关于stm32的基础知识,在了解了寄存器原理的同时,还学习了GPIO端口的初始化设置三步骤,对我学习这门课有很大的帮助。

五、参考文献

https://blog.csdn.net/big_blingbling/article/details/81427764

https://blog.csdn.net/weixin_42827999/article/details/101699674

https://blog.csdn.net/geek_monkey/article/details/86291377

STM32F103寄存器方式点亮LED流水灯相关推荐

  1. STM32F03寄存器方式点亮LED流水灯

    STM32F03寄存器方式点亮LED流水灯 文章目录 STM32F03寄存器方式点亮LED流水灯 一.题目内容 二.STM32F03系列芯片的地址映射和寄存器映射映射原理:了解GPIO端口的初始化设置 ...

  2. STM32F103C8T6以寄存器方式借助面包板搭建电路点亮LED流水灯详解

    文章目录 一.寄存器原理 1.什么是寄存器 2.如何访问寄存器 二.GPIO端口的初始化设置 1.时钟配置 (1)找到时钟使能寄存器映射基地址 (2)找到端口偏移地址以及对应端口所在位置 (3)使能对 ...

  3. GPIO端口初始化设置,STM32F103点亮LED流水灯过程

    文章目录 一.STM32F103系列芯片地址映射和寄存器映射原理 1.什么是寄存器 2.地址映射和寄存器映射 3.寄存器地址查找 二.GPIO端口初始化设置 1.GPIO简介 2.配置时钟 3.输入输 ...

  4. 用寄存器HAL库完成LED流水灯程序

    重庆交通大学信息科学与工程学院 <嵌入式系统开发>课程 作业报告(第4周) 班 级: 通信工程2001 姓名-学号 : 阎桂董-632007030622 实验项目名称: 作业题目 实验项目 ...

  5. 【stm32cubeMX】使用HAL库点亮LED流水灯

    stm32cubeMX使用HAL库点亮LED流水灯 1.安装STM32CubeMX 2.安装HAL库 3.新建项目 4.keil仿真调试 五.实现 六.参考资料 1.安装STM32CubeMX 官网下 ...

  6. 【嵌入式04】用寄存器HAL库完成LED流水灯程序

    目录 一.原理学习 1.寄存器映射原理 2.GPIO端口的初始化设置步骤 二.LED流水灯 1.程序设计思路 2.寄存器方式编程实现 3.HAL库编程实现 4.软件仿真 三.总结 四.参考链接 一.原 ...

  7. ​用寄存器HAL库完成LED流水灯程序

    STM32的地址映射.寄存器映射原理以及GPIO端口的初始化设置三步骤 ​(一)STM32F103系列芯片的地址映射及寄存器映射原理 1.STM32F103C8T6简要介绍 ● STM32F103C8 ...

  8. STM32Cubemx的安装及用寄存器HAL库完成LED流水灯程序

    目录 一.STM32CubeMx安装 (一)简介 (二)下载地址 1.官方下载地址 2.网盘下载地址 二.STM32CubeMX安装过程 三.HAL库安装 四.HAL库实现LED流水灯 (一)新建项目 ...

  9. 【嵌入式知识07】借助stm32CubeMX,使用STM32F103C8T6点亮LED流水灯

    本文目录 一.简述 二.STM32CubeMX简介 1.利用CubeMX新建工程点亮LED灯 1)前期准备 2)新建工程 2.界面讲解 3.配置引脚 4.时钟源配置 5.工程管理 三.点亮LED 1. ...

最新文章

  1. Java之SPI机制
  2. centos7.3部署kvm虚拟化
  3. linux使用ntp时间同步
  4. Python 第五天
  5. from rfc 2068 hypertext怎么解决_你好,打工人!用英语怎么表达“打工人”?可别直接说 worker...
  6. CSS 元素的定位之相对定位 position: relative
  7. Navicat Report Viewer 如何连接到 MySQL 数据库
  8. php调用成员函数错误,PHP致命错误:在非对象上调用成员函数exec...
  9. 黑马程序员-python笔记-从入门到入职
  10. c语言程序设计商品管理系统
  11. Java数组 排序算法和常见异常
  12. smobiler中实现页面切换_.Net语言Smobiler开发之如何在手机上实现表单设计
  13. 广州大学锐捷认证协议安全性研究
  14. Linux下编写udp群聊室
  15. 喜马拉雅xm格式转化mp3_毛毛虫点读笔如何点小达人点读书——小达人点读包dab转换成MP3切割音频...
  16. OpenGL中的Alpha测试,深度测试,模板测试,裁减测试 .
  17. shap 解释理赔时效模型特征
  18. phalcon mysql_Phalcon 数据库操作总结
  19. 雷电2接口_Intel发布雷电4接口,相比之前的雷电3,雷电4都改进了什么?
  20. C++11中的时间库std::chrono(引发关于时间的思考)

热门文章

  1. Myflight航班查询系统
  2. 经典的mac阅读写作学习工具合集
  3. 一个简单的猜拳小游戏
  4. 淡季累库,黑色短期高位震荡看待(20210104).PDF
  5. 国产开源「文本-视频生成」模型!免费在线体验,一键实现视频生成自由
  6. 用程序实现基本计算器功能
  7. mysql sql宽字节注入_sql注入之(宽字节注入篇)
  8. python数据获取及预处理_Python小练习——电影数据集TMDB预处理
  9. / ./ ../路径含义
  10. 基于Java Springboot+Vue+MyBatis音乐播放系统设计实现