用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形

文章目录

  • 前言
  • 一、相关寄存器配置介绍
    • 1.相关寄存器配置介绍
  • 二、使用寄存器点亮LED灯
    • 1.工程模板的建立
    • 2.配置GPIO端口
    • 3.主要函数
    • 4.编译生成HEX文件
    • 5.电路搭建
  • 三、STM32的开发环境的搭建
    • 1.安装STM32CubeMX
    • 2.安装固件库
    • 3.进行仿真实验
    • 4.MDK5模拟示波器
  • 四、小结

前言

1.学习和理解STM32F103系列芯片的地址映射和寄存器映射原理;了解GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)。
2.用 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。
3.安装 stm32CubeMX,用cubemx完成初始化过程,采用HAL库编程实现。
4.在Keil下用软件仿真运行上面代码,并用虚拟逻辑分析仪观察 对应管脚上的输出波形(高低电平转换),看是否是1秒的周期。

一、相关寄存器配置介绍

1.相关寄存器配置介绍

不管是库函数操作还是HAL操作,本质上都是对寄存器的操作。
在提供的“ STM32F103中文教程及参考手册”中看到GPIO寄存器7种类型,首先看下5.2.1端口配置低寄存器,通过该寄存器具体介绍下。
先解释下(GPIOx_CRL)(x=A…E)的意思。stm32f103引脚有多有少,多则144个,少则48个(本实验用的48脚芯片),所以会把这些引脚分成组,A、B、C、D…每组最多16个引脚,注意,是最多,并不是一定要有16个引脚!
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第1张图片
48引脚的单片机只分成了A、B、C、D(PA、PB、PC、PD)组。GPIO英语的全称是General-purpose input/output,翻译过来就是通用的IO口。GPIOA_CRL的意思是控制A口的CRL寄存器。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第2张图片再回到上面的CTRL寄存器图,“偏移地址0x00”。那么偏移是相对于谁偏移的,应该有个基准!先给大家举个例子,要盖一幢商务大楼,外面盖完了如果里面是空的,是不行的,所以要盖一层层,一层层盖了也不行,在每一层隔处一个个房间,这样一幢商务大楼才能使用。我们单片机内存也是,整一大块是不能用的,也要隔成一个个房间才能使用,每个房间相当于寄存器,要么有人(相当于为1),要么没人(相当于为0)。所以要查下GPIOC安排在几楼!

在提供的手册中(P18页)可以看到GPIOC的地址范围是0x4001 1000~0x4001 13FF。所以它的起始地址是0x4001 1000,也就是基地址!偏移0x00后可以得出GPIOC->CRL的地址(GPIOC端口的起始地址+偏移地址)为0x40011000。
所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第3张图片
再回到CTRL寄存器图,看下面的图。一共有32个位,0~31,也就是CTRL寄存器有32位,这也是为什么叫做stm32的原因,以前的51单片机是8位单片机也就是一次性最多能处理8个位。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第4张图片
看红色框部分(0-3位),CNF0和MODE0。再看4-7位,CNF1和MODE1。说明GPIOC中的第0位需要CNF0和MODE0这4个位来控制,GPIOC中的第1位需要CNF1和MODE1这4个位来控制。具体可以控制哪些呢,在往下看。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第5张图片
在上图中,以MODE0为例,占了两个位,每个位可以表示0或1,所以可以表示4种情况!一般情况下是输出模式,在下表中的MODEy[1:0]就可以看到③,有输出和输入模式,本次实验就是选择“10”表示输出模式,最大速度为2MHZ,再去看CNFy[1:0],根据输入输出的不同有不同的选择,那么我们刚才选的是输出模式,所以看②,选择“00”,表示“通用推挽输出模式”。
通过以下代码实现通用推挽输出模式:

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

再看5.2.3和5.2.4,一个用于配置输入,一个用于配置输出,那么我们看输出。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第6张图片
因为每个GPIO组只有16个引脚,所以ODR寄存器只用了低16位,高16位保留。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第7张图片
这个寄存器功能很简单,控制输出的数据为0或者1 。
所以我们控制LED延时闪烁也很简单,就是控制ODR寄存器先输出1,LED灯亮,延时一段时间,控制ODR寄存器先输出0,LED灯灭,一直循环,就能实现流水灯的效果。
代码如下:

GPIOC_ODR &= ~(1<<13);//配置输出低电平0
GPIOC_ODR |= (1<<13);//配置输出高电平1

二、使用寄存器点亮LED灯

1.工程模板的建立

stm32提供了一个用c语言封装好的固件库,我们要实现什么功能,直接调用相应的库函数即可。
要使用ST固件库,可以建立一个工程模板,方便我们调用函数。

2.配置GPIO端口

GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。
GPIO端口的初始化设置三步骤:
a.时钟配置
b.输入输出模式设置
c.最大速率设置
库函数中提供了一个结构体来配置GPIO端口的输入输出模式设置 、 最大速率设置等。
定义的结构体如下:

// @file    stm32f10x_gpio.h
typedef struct
{
  uint16_t GPIO_Pin;           /*!< 选择要配置的 GPIO 引脚 */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< 选择 GPIO 引脚的速率 */

  GPIOMode_TypeDef GPIO_Mode;    /*!< 选择 GPIO 引脚的工作模式 */
}GPIO_InitTypeDef;

这个结构体中包含了初始化 GPIO 所需要的信息,包括引脚号、工作模式、输出速率。
设计这个结构体的思路是:初始化 GPIO 前,先定义一个这样的结构体变量,根据需要配置 GPIO 的模式,对这个结构体的各个成员进行赋值,然后把这个变量作为“GPIO 初始化函数”的输入参数,该函数能根据这个变量值中的内容去配置寄存器,从而实现 GPIO 的初始化。
配置 端口位为通用推挽输出,速度为 2M

GPIO_InitTypeDef   GPIO_InitStruct;

GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4 ;             		//选定输出端口为GPIO_Pin_4
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M

GPIO_Init(GPIOA,&GPIO_InitStruct);

至此一个GPIOB_Pin_4配置完毕,我们总算可以控制一个 LED 了。

3.主要函数

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

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_4
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10 ;             //选定端口为GPIO_Pin_1
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14 ;             //选定端口为GPIO_Pin_14
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	GPIO_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);
}

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

delay.c函数
本实验调用的是正点原子写好的延时函数用于实现延时1s后三种LED灯轮流闪烁

#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   				//使用UCOSIII
	OS_ERR err; 
	OSSchedLock(&err);							//UCOSIII的方式,禁止调度,防止打断us延时
#else											//否则UCOSII
	OSSchedLock();								//UCOSII的方式,禁止调度,防止打断us延时
#endif
}

//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{	
#ifdef CPU_CFG_CRITICAL_METHOD   				//使用UCOSIII
	OS_ERR err; 
	OSSchedUnlock(&err);						//UCOSIII的方式,恢复调度
#else											//否则UCOSII
	OSSchedUnlock();							//UCOSII的方式,恢复调度
#endif
}

//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
	OS_ERR err; 
	OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);	//UCOSIII延时采用周期模式
#else
	OSTimeDly(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;
#endif
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	fac_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    

#else
	fac_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 

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);				//红灯亮后延时1s
		LED_G_TOGGLE();
		delay_ms(500);				//绿灯亮后延时1s
		LED_Y_TOGGLE();
		delay_ms(500);				//黄灯亮后延时1s
	}
}

4.编译生成HEX文件

击左上角的编译按钮进行编译,可以看到生成了hex 文件用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第8张图片
打开建立的OBJ文件夹也可以看见存放有生成的.hex文件
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第9张图片

5.电路搭建

本次实验是在面包板上使用c8t6来控制红、绿、黄三个灯轮流闪烁,因此需熟悉面包板的使用。电路图如下所示:
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第10张图片
其中实验用到的STM32 开发板用的 USB 转串口的驱动芯片是 CH340,要使用串口得先在电脑中安装 USB 转串口驱动—CH340 版本,下载链接详见前面。

如果 USB 转串口驱动安装成功,USB 线跟板子连接没有问题,在计算机->管理->设备管理器->端口中可识别到串口。
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第11张图片
用 USB 线连接电脑和开发板的 USB 转串口接口:USB TO UART,给开发板上电。
打开 mcuisp 软件,配置如下:
①搜索串口,设置波特率 115200(尽量不要设置的太高)
②选择要下载的HEX 文件
③校验、编程后执行
④DTR 低电平复位,RTS 高电平进入 bootloader
⑤开始编程,如果出现一直连接的情况,按一下开发板的复位键即可
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第12张图片
开始编译后的下载成功后的提示如下图,则说明HEX文件已经成功被烧录到芯片中。
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第13张图片
然后流水灯将会正常轮流闪烁。

三、STM32的开发环境的搭建

1.安装STM32CubeMX

由于STM32CubeMX是Java实现的,需要安装jdk环境。下载地址:
https://www.st.com/en/development-tools/stm32cubemx.html
安装过程:
进入下载界面,根据自己的需要下载对应的版本,找到下载路径,对下载的zip压缩包进行解压,进入解压到的文件夹,点击右键以管理员身份运行SetupSTM32CubeMX-6.3.0-Win.exe,进入到下面的界面,点击next,点击"I accept the terms of this license agreement",接着选择Next,在跳出的页面,点击Next,进行安装,点击Done就安装完成了。

2.安装固件库

运行STM32CubeMX程序,选择 Help->Manage embedded software packages 安装固件库,根据自己的芯片类型,选择一个固件库,点击下方的From Local…进行下载,表示从本地安装(已经在本地下载了对应的固件库),Install now是通过网络下载,我这里选择的是在线安装。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第14张图片
前面的框显示为绿色,表示下载成功,安装MDK5软件
具体安装请参照下面链接:
https://blog.csdn.net/qq_43279579/article/details/108880667

3.进行仿真实验

至此完成了cubemx初始化过程,然后用其进行流水灯的仿真,进入刚刚选择的路径,打开MDK-ARM子文件夹,通过keil打开刚刚生成的项目。用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第15张图片
通过目录找到并打开main.c文件,找到主函数int main(void)那一部分用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第16张图片
如果要在STM32CubeMX生成的工程中添加代码,要在提示 /* USER CODE BEGIN* /和/* USER CODE END*/之间添加代码,这样STM32CubeMX重新生成代码时才不会将自己添加的代码删除掉。此外,注意使用CubeMX配置生成的工程中的注释最好不要随便删除掉。
在while循环的/* USER CODE BEGIN 3 /和/ USER CODE END 3 */之间插入以下代码:
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//PA4亮灯
HAL_Delay(500);//延时0.5s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_Delay(500);//延时0.5s

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);//PB10亮灯
HAL_Delay(500);//延时0.5s
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET);//PB10熄灯
HAL_Delay(500);//延时0.5s
	
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);//PC14亮灯
HAL_Delay(500);//延时0.5s
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);//PC14熄灯
HAL_Delay(500);//延时0.5s

用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第17张图片
编译运行程序无报错且生成相应的.hex文件用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第18张图片
在文件夹MDK-ARM下,打开与工程名相同的子文件夹可以看到生成的.hex文件用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第19张图片
然后程序烧录过程如上述步骤所示,最终完成用HAL库编程实现流水灯的实验。

4.MDK5模拟示波器

在没有示波器条件下,可以使用MDK5的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点,因此可以用MDK5的软件仿真观察3个GPIO端口的输出波形。
1.设置options for target
在这里插入图片描述
Target的设置:用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第20张图片
Target界面中,选择正确的晶振大小,我使用的是8MHz的外部晶振。这个选项在软件仿真中起到很重要的作用,如果选择错误,那么波形一定是错误的,因为时间不准确。不过这个参数只在软件仿真中起作用,当程序在硬件中运行并没有影响。
Dubug的设置:用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第21张图片
首先应该选择Use Simulator,其次是Run to main()选项打√,然后分别修改上图中的 4和5处,注意,DARMSTM.DLL和TARMSTM.DLL在STM32的单片机中应该都是固定的,Paramter是跟所采用的具体芯片是对应的,应与Target中STMicroelectronics 保持一致。
2.参数设置完毕,点击Debug,进入调试界面
在这里插入图片描述
3.选择逻辑分析仪Logic Analyer用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第22张图片
4.选择要观察的引脚
①点击Setup Logic Analyzer在这里插入图片描述
②添加要观察的引脚用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第23张图片
(引脚添加:如PA4可直接输入PORTA.4即可)
图中选择的三个引脚分别是PA4、PB10和PC14,GPIOA对应的为PORTA,那么GPIOB对应的也应该写成PORTB,以此类推。其中PORTA & 0x00001000后再右移4位也就把PA4的状态获取出来,1就是高电平,0就
是低电平。PB10和PC14同理。一定要选择Bit,颜色是为了区分不同的引脚,根据需要配置即可。设置完成后退
出,就可以在后续的操作中观察到波形。
注意:如果上述步骤1中的Debug中的4、5没有正确设置,那么在添加引脚的时候就会出现报错:Unknown Signal !
5.运行程序
用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第24张图片
6.观察波形,把光标移动到逻辑分析仪显示波形的区域,上下滚动滑轮,就可以放大和缩小波形用寄存器&HAL库完成LED流水灯程序以及通过MDK5模拟示波器观察波形_第25张图片
这样就可以直观的看出各个引脚的波形图,从而知道它们的区别。

四、小结

使用STM32CubeMX自动生成代码并点亮LED灯是非常简单的,通过这次实验,让我明白了合理使用工具的“快乐”,能使我们更快的编写代码,提高工作效率。在日常工作中,逻辑分析仪和示波器是MCU工程师必不可少的工具,有时候程序有BUG的时候就需要用到这些工具看波形,从而更快地定位到问题所在,进而解决问题。keil的软件仿真功能中的逻辑分析仪在这个时候就是一个不错的选择。

你可能感兴趣的:(stm32,嵌入式硬件)