Keil4与Proteus仿真程序实现流水灯实验&STM32实现通过寄存器实现流水灯实验

一、安装Keil4和Proteus8.9仿真程序

Keil4的安装程序(附带下载地址)(参考CSDN)

【仿真】Proteus8.9下载与安装教程(超详细)(参考CSDN)

二、在Proteus中创建工程,对原理图进行创建

1.打开Proteus,创建新工程

2.创建新工程,选择工程保存路径

3.选择从选中的模版中创建原理图,选择DEFAULT(默认)

4.因为此次实验不需要PCB,所以选择不创建PCB布版设计

5.然后点击创建固定项目,在第二栏中选择AT89C51,其他都默认

6.然后进入最后一步,点击完成即可

7.进入原理图绘制界面,然后自行绘制,可参考Protues使用教程(CSDN - 专业开发者社区)

绘制结束

原理图参考如下:

至此,原理图创建完成

三、在Keil4创建工程,并且编译程序(流水灯)

1.创建新项目,并且点击New uVision Project

2.在新弹出的窗口中输入工程文件名称和选择工程的保存路径

3.在弹出的窗口中选择数据库,选择Atmel,在下拉选项中选择AT89C51

4.然后点击是(Y)

5.进入主界面创建一个新文件新文件

6.编写程序,然后右键点击文件夹,点击Add Files to Group... ...

main.c:

//51单片机编程常用的头文件
	#include <reg51.h>
	#include <intrins.h>
	//延迟函数
	void delay_ms(int a)
	{
		int i,j;
		for(i=0;i<a;i++)
		{
			for(j=0;j<1000;j++) _nop_();
	
		}
	}
	
	void main(void)
	{
		while(1)
		{
			P0=0xfe;
			delay_ms(50);
			P0=0xfd;
			delay_ms(50);
			P0=0xfb;
			delay_ms(50);
			P0=0xf7;
			delay_ms(50);
			P0=0xef;
			delay_ms(50);
			P0=0xdf;
			delay_ms(50);
			P0=0xbf;
			delay_ms(50);
			P0=0x7f;
			delay_ms(50);
		}
	}

7.然后点击类似于仙女棒的东西,然后点击生成hex文件

8.编译文件,生成hex文件

没有任何警告和错误,且成功输出hex文件

四、将Keil4生成hex文件导入Proteus,然后进行仿真

1.双击单片机,然后选择刚才生成的hex文件

2.然后点击左下角仿真按钮,进行仿真

五、仿真结果

六.STM32流水灯实验

1.安装Keil5(keil5安装教程_Matcha_ice_cream的博客-CSDN博客)

2.创建文件名,选择保存路径

3.选择stm32f103zet6对应的库文件,搜索stm32f103ze

4.创建stmLED.c文件,然后编写程序:

main.c

#define PERIPH_BASE           ((unsigned int)0x40000000)//AHB的地址
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000) //APB2地址
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800) //GPIOA地址
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C   //GPIOA_ODR地址
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
typedef  struct{
 
   volatile  unsigned  int  CR;
   volatile  unsigned  int  CFGR;
   volatile  unsigned  int  CIR;
   volatile  unsigned  int  APB2RSTR;
   volatile  unsigned  int  APB1RSTR;
   volatile  unsigned  int  AHBENR;
   volatile  unsigned  int  APB2ENR;
   volatile  unsigned  int  APB1ENR;
   volatile  unsigned  int  BDCR;
   volatile  unsigned  int  CSR;
} RCC_TypeDef;
 
#define RCC ((RCC_TypeDef *)0x40021000)
typedef  struct
{
volatile  unsigned  int  CRL;
volatile  unsigned  int  CRH;
volatile  unsigned  int  IDR;
volatile  unsigned  int  ODR;
volatile  unsigned  int  BSRR;
volatile  unsigned  int  BRR;
volatile  unsigned  int  LCKR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
void delay(unsigned int  time)
{
 unsigned int i=0;
	while(time--)
	{
   i=12000;
   while(i--);		
	}
}
 
int main(void)
{
	RCC->APB2ENR|=1<<2;      	  	 
	GPIOA->CRL&=0x00000000;
	GPIOA->CRL|=0x33333333;	
  GPIOA->ODR=0x01;      
  while(1)
	{
	delay(10);
	GPIOA->ODR=GPIOA->ODR<<1;
	if(GPIOA->ODR==0x0100)
	{
		GPIOA->ODR=0x01;
}
}
}

5.编写完程序后右键点击文件,添加文件stmLED.c

6.点击魔方块,添加stm32启动项文件

7.编译程序

成功编译

7.Jlink烧录程序

1.Jlink烧录程序的安装:Jlink软件下载和安装教程(参考CSDN)

2.点击仙女棒,然后进行下列勾选

3.点击Dubeg,然后settings选ST-LINK/V2,然后点击settings

4.然后勾选SW,输入Max1.8MHz,注意此时要插上stm32核心块,不然会显示错误Error,然后点击确定。

5.然后点击load,下载程序进入stm32,然后可以得到

流水灯视频如下

8.问题思考1:

1.嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器--->对应相关管脚)的操作有哪些相同与差别?
2.为什么51单片机的LED点灯编程要比STM32的简单?

寄存器:寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。寄存器是常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。寄存器的存储电路是由锁存器或触发器构成的,因为一个锁存器或触发器能存储1位二进制数,所以由N个锁存器或触发器可以构成N位寄存器。按照功能的不同,可将寄存器分为基本寄存器和移位寄存器两大类。基本寄存器只能并行送入数据,也只能并行输出。移位寄存器中的数据可以在移位脉冲作用下依次逐位右移或左移,数据既可以并行输入、并行输出,也可以串行输入、串行输出,还可以并行输入、串行输出,或串行输入、并行输出,十分灵活,用途也很广。

51单片机和STM32单片机在寄存器方面的差异主要体现在开发方式和系统资源上。

开发方式:51单片机主要通过直接操作寄存器进行编程,而STM32则主要采用库函数编程方式。
系统资源:STM32单片机的资源通常比51单片机更多。在寄存器方面,STM32可能具有更多的寄存器和更复杂的寄存器映射。例如,STM32F103系列芯片具有比51单片机更复杂的地址映射和寄存器映射,这可能需要更复杂的设置和编程。
总的来说,51单片机和STM32单片机在寄存器方面的差异主要体现在开发方式和系统资源上。

1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器--->对应相关管脚)的操作有哪些相同与差别?

相同点:

两者都需要使用C语言进行编程。
两者都需要通过赋值操作来修改或控制。
两者都需要了解相应的内存地址或寄存器地址。
不同点:

对内存中的变量进行修改是直接在RAM中进行操作,而对外部设备的操作通常需要通过特定的寄存器间接实现。
对内存中的变量进行修改通常不会涉及到硬件的实时反应,而对外部设备的操作往往会有实时的硬件反应,比如改变LED的状态。
对内存中的变量进行修改不会涉及到中断、异常处理等问题,而对外部设备的操作可能会涉及到这些问题。
2)为什么51单片机的LED点灯编程要比STM32的简单?

51单片机比STM32简单的原因可能有以下几点:

51单片机的硬件结构相对简单,其I/O操作也相应简单。而STM32系列微控制器具有更多的功能和特性,因此在进行编程时可能需要考虑更多的因素。
51单片机的寄存器较少,因此在进行编程时需要考虑的寄存器操作也相对较少。而STM32系列微控制器具有更多的寄存器,因此在进行编程时可能需要考虑更多的寄存器操作。
51单片机的指令集相对简单,因此在进行编程时可能更容易理解和使用。而STM32系列微控制器的指令集更加复杂,因此在进行编程时可能需要更多的学习和理解。
51单片机的LED点灯编程通常是通过对I/O口的直接操作实现的,而STM32系列微控制器可能需要更多的配置和操作才能实现相同的LED点灯效果。

9.问题思考2:与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。

在C语言中,`register`和`volatile`是两种特殊的变量修饰符,它们在嵌入式系统中经常使用,这是因为在嵌入式系统中,对硬件的直接访问和优化通常是非常重要的。

1. `register`关键字:
在C语言中,`register`是一个存储类修饰符,用于提示编译器一个变量可能被频繁访问,以便将其存储在寄存器中,从而提高访问速度。然而,实际上是否将其存储在寄存器中是由编译器决定的,因为现代编译器通常已经能够进行高度优化。

请注意,对于大多数现代的编译器,`register`关键字的使用已经没有过去那么重要,因为编译器有自己的优化策略。然而,在嵌入式系统中,有时仍然会用到它,因为在这些环境中,对硬件的直接控制经常需要频繁地访问特定的变量。

示例代码:

register int i;
for(i=0;i<100;i++)
{
//频繁使用变量1
}

2. `volatile`关键字:

`volatile`是另一个存储类修饰符,用于告诉编译器一个变量可能会被意外的(即编译器无法预知的)方式改变。这通常发生在并行编程或硬件寄存器映射的情况下。

在并行编程中,一个线程可能在一个时刻改变一个变量的值,而另一个线程可能在另一个时刻读取这个值。在这种情况下,编译器不能对这个变量进行优化,因为它不能确定变量的值何时会改变。

同样地,当直接访问硬件寄存器时,寄存器的值可能会在任何时候改变(例如,由于硬件事件或中断)。因此,使用`volatile`关键字可以确保编译器不会优化这个变量。

示例代码:

volatile int flag;
//假设flag是一个硬件寄存器的映射,该寄存器的的值可能会在任何时候改变
if(flag)
{
//执行一些操作
}


以上示例中,如果`flag`没有使用`volatile`修饰,编译器可能会优化掉对`flag`的读取操作,因为在它看来,`flag`的值并没有改变。然而,实际上`flag`的值可能已经改变了,这就会导致程序的行为出错。通过使用`volatile`关键字,我们可以确保这种情况不会发生。

10.总结

1. C51不需要分频,且为8位单片机,STM32需要时钟分频且为32位单片机,功能更复杂,C51         编写流水灯程序时比STM32更简单。

 2. stm32存储器映射是给绝对地址命名个别名,通过寄存器更改绝对地址里面的值。

 3.  寄存器是CPU内部的组成部分,通过内部总线与CPU直接相连,内存是通过外部地址总线与C        PU相连,通过外部总线寻址,通过寄存器修改变量的值比内存更快。

 4. register声明的变量尽可能直接放在寄存器中存储,volatile声明的变量只能从绝对地址中访问。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值