C51程序设计与仿真LED流水灯

  1. List item

C51程序设计与仿真LED流水灯

前言

学习Proteus软件的知识、如何安装、如何使用,熟悉Proteus电路仿真软件,并完成一个51程序设计和仿真

一、Proteus简介

Proteus软件是英国Lab Center Electronics公司出版的EDA工具软件。它不仅具有其它EDA工具软件的仿真功能,还能仿真单片机及外围器件。它是比较好的仿真单片机及外围器件的工具。虽然国内推广刚起步,但已受到单片机爱好者、从事单片机教学的教师、致力于单片机开发应用的科技工作者的青睐。
Proteus是英国著名的EDA工具(仿真软件),从原理图布图、代码调试到单片机与外围电路协同仿真,一键切换到PCB设计,真正实现了从概念到产品的完整设计。是世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台,其处理器模型支持8051、HC11、PIC10/12/16/18/24/30/DSPIC33、AVR、

二、51程序设计与仿真

1、添加原件

在 Keywords 处输入 AT89C51 ,然后在中间的窗口内双击AT89C51芯片,即可添加到元件列表中,而后依次添加LED-YELLOW、RES

在这里插入图片描述
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5Clenovo%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20230924110628275.png&pos_id=img-OVolbK1G-169555754531

2、摆放原件

(1)左击元件列表窗内的 AT89C51 芯片,然后再原理图编辑窗口内左击摆放。

在这里插入图片描述!

(2)左击元件列表内的 LED-YELLOW ,再点击 旋转按钮 ,可以在预览窗内看见元件顺时针旋转了下,再在原理图编辑框内一次摆放LED灯共8个。

(3)再按照(2)的步骤依次摆放8个电阻,然后左键双击“10K”,弹出修改值的对话框,将10K修改为300,以至于让LED更亮。

在这里插入图片描述

(4)拉一条主线。

在这里插入图片描述

(5)连接管脚。

在这里插入图片描述

(6)点击终端接口→选择POWER,放置电源,然后左键双击电源图标,修改为VCC,再点击OK。
在这里插入图片描述

(7)使用 LBL 为支线标记编号,连接到主线的支线,需要对支线进行编号才能正常连接,否则后面需要正常实验成功

在这里插入图片描述

这样,一个LED流水灯原理图就制作完成了。

2、编写51程序
1)创建一个工程

(1)打开 Keil 软件,点击 Project → New uVision Project …

在这里插入图片描述

(2)然后将下列代码复制粘贴到Text1

	`//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);
	}
}`

(3)点击保存按钮,再命名为main.c文件,右键点击 Source Group 1 ,再点击 Add Existing Files to Group “Source Group 1”,选中刚刚创建的 main.c 文件,并点击 Add 。

在这里插入图片描述

3)编写main.c文件

(1)点击魔法棒,在弹出的窗口内选择 Output ,再勾选 Create HEX File ,然后点击 OK,点击编译按钮,进行编译,编译成功并生成了两个头文件

在这里插入图片描述

3、开始仿真

(1)回到Proteus软件的原理图内,双击 AT89C51 芯片后,在弹出的窗口的 Program File 一栏从刚才 keil 软件编译后的路径中添加 .hex 文件,再点击 OK 。

在这里插入图片描述

(2)点击调试按钮,开始仿真,仿真结果如下!!

在这里插入图片描述

使用MDK编译stm32简单程序(闪烁LED)

1.新建工程

在菜单栏点击Project中的New uVision Project,在想要的存储地址新建文件夹名为“点亮LED”

进入该文件夹将项目命名为project并保存,之后选择我们的单片机–STM32F103RB(在我们的STM32最小板的芯片上写了相应的型号),之后点击OK,

在菜单栏点击File–New,将其保存命名为mian.c(注意:这里命名的名字后面一定要加“.c”)

输入以下代码

`//用来存放STM寄存器映射
#define PERIPH_BASE ((unsigned int)0x40000000)//AHB
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
//GPIOA_BASE=0x40000000+0x10000+0x0800=0x40010800,该地址为GPIOA的基地址
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
//GPIOB_BASE=0x40000000+0x10000+0x0C00=0x40010C00,该地址为GPIOB的基地址
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
//GPIOC_BASE=0x40000000+0x10000+0x1000=0x40011000,该地址为GPIOC的基地址
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
//GPIOD_BASE=0x40000000+0x10000+0x1400=0x40011400,该地址为GPIOD的基地址
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
//GPIOE_BASE=0x40000000+0x10000+0x0800=0x40011800,该地址为GPIOE的基地址
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
//GPIOF_BASE=0x40000000+0x10000+0x0800=0x40011C00,该地址为GPIOF的基地址
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
//GPIOG_BASE=0x40000000+0x10000+0x0800=0x40012000,该地址为GPIOG的基地址
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

#define LED0 MEM_ADDR(BITBAND(GPIOA_ODR_Addr,8))
//#define LED0 *((volatile unsigned long *)(0x422101a0)) //PA8
//定义typedef类型别名
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类型别名
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;
//GPIOA指向地址GPIOA_BASE,GPIOA_BASE地址存放的数据类型为GPIO_TypeDef
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)

void LEDInit( void )
{
RCC->APB2ENR|=1<<2; //GPIOA ???
GPIOA->CRH&=0XFFFFFFF0;
GPIOA->CRH|=0X00000003;
}

//延时
void Delay_ms( volatile unsigned int t)
{
unsigned int i,n;
for (n=0;n<t;n++)
for (i=0;i<800;i++);
}

int main(void)
{
LEDInit();
while (1)
{
LED0=0;//LED灭
Delay_ms(500);//延时
LED0=1;//LED亮
Delay_ms(500);//延时
}
}`

在右边的工具栏中找到Source Goupe 1,右击,点击Add Existing Files to Group ‘Source Goupe 1’

在这里插入图片描述

点击Keil工具箱的魔术棒,点击Debug里的ST-Link Debuggeer,点击旁边的Settings,勾选ST-LINK/V2和SW

在这里插入图片描述

后点击确定,进行编译,编译成功后点击LOAD即可看到LED闪烁

在这里插入图片描述

(三)STM32系列芯片的原理

1.STM32系列芯片的地址映射和寄存器映射原理

地址映射原理:为了保证CPU执行指令时可正确访问存储单元,需将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射。

Cortex™-M3存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器区中的每个字

映射到位段存储器区的一个位,在别名存储区写入一个字具有对位段区的目标位执行读-改-写操

作的相同效果。

在STM32F10xxx里,外设寄存器和SRAM都被映射到一个位段区里,这允许执行单一的位段的

写和读操作。

下面的映射公式给出了别名区中的每个字是如何对应位带区的相应位的:

bit_word_addr = bit_band_base + (byte_offset×32) + (bit_number×4)

其中:

bit_word_addr是别名存储器区中字的地址,它映射到某个目标位。

bit_band_base是别名区的起始地址。

byte_offset是包含目标位的字节在位段里的序号

bit_number是目标位所在位置(0-31)

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

(1)存储位置:内存中的变量和外部设备都需要使用特定的存储位置。变量在内存中有自己的地址,而外部设备通常通过特定的寄存器与处理器或控制器连接,并且每个寄存器都有一个地址。
(2)数据传输:在代码中,变量的修改或外部设备的操作都需要进行数据传输。无论是将值存储到变量中还是将数据发送到外部设备的寄存器中,都需要进行数据传输。

不同:

(1)访问权限:对于内存中的变量,C程序可以直接读取和修改其值,除非变量被声明为常量或指针限定符限制了访问权限。而对于外部设备,其寄存器的访问权限可能受到硬件保护机制的限制,需要通过特定的接口和指令来访问。
(2)时间延迟:对内存中的变量的修改是即时的,读取和写入的延迟很小,因为内存通常是与处理器直接连接的。而对外部设备的操作通常涉及与设备之间的通信,可能需要等待设备响应,因此有时会出现较大的时间延迟。
(3)编程接口:对于内存中的变量,可以通过直接访问变量的地址来读取和修改其值。而对于外部设备,通常需要使用特定的接口和寄存器操作来与设备进行通信和控制。这些操作可能需要使用特定的寄存器配置、位操作或特殊指令来实现正确的设备控制。

总之,尽管在代码层面上,对内存中的变量和外部设备的操作可能存在一些相似之处,但在底层实现和使用接口上存在明显的差异。合理理解和处理这些差异对于编写嵌入式系统中的操作是很重要的。

3.为什么51单片机的LED点灯变成要比STM32的简单?
(1)编译语言:

因为STM32比51的外设、时钟等高出许多,51单片机结构相对简单,所以通常多使用汇编语言和C语言编程,而STM32系列的开发工作,不会采用汇编语言,因为工程量巨大,寄存器太多,位数也多

(2)编程方式:
51单片机只需要配置寄存器打开就可以编程,而STM32系列单片机需要先打开对应的时钟,包括开启后打开外部时钟才开始工作

(3)资源不同:

STM32的内部资源(寄存器和外设功能)较普通的51单片机都要多,所以在编程上有更多的选择

(四)C程序中变量修饰符的作用
1.与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。
在嵌入式C程序中,经常会看到register和volatile这两个变量修饰符,它们用于对变量的行为和存储进行修饰。

(1)register关键字:

register关键字建议编译器将变量存储在寄存器中,以便快速访问。它提供了一种提示,但并不保证编译器会将变量存储在寄存器中。这是因为寄存器的数量有限,编译器可能会根据优化策略和当前的寄存器分配情况来决定是否将变量存储在寄存器中。
register修饰符的使用场景通常是对频繁访问的变量进行优化,以减少内存访问的开销。例如,循环中的计数器变量可以使用register关键字修饰。
(2)volatile关键字:

volatile关键字告诉编译器该变量可能会在意料之外的情况下被修改,因此在编译器进行优化时应该避免对该变量进行过度优化。
volatile修饰符用于变量在多线程环境、硬件寄存器、中断服务程序等需要及时更新变量值的场景。因为这些情况下,变量的值可能由于外部因素而发生变化,而编译器通常无法感知到这些变化。
的数量有限,编译器可能会根据优化策略和当前的寄存器分配情况来决定是否将变量存储在寄存器中。
register修饰符的使用场景通常是对频繁访问的变量进行优化,以减少内存访问的开销。例如,循环中的计数器变量可以使用register关键字修饰。
(2)volatile关键字:

volatile关键字告诉编译器该变量可能会在意料之外的情况下被修改,因此在编译器进行优化时应该避免对该变量进行过度优化。
volatile修饰符用于变量在多线程环境、硬件寄存器、中断服务程序等需要及时更新变量值的场景。因为这些情况下,变量的值可能由于外部因素而发生变化,而编译器通常无法感知到这些变化。

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值