使用Keil5和protues完成51单片机的仿真以及使用STM32完成LED灯闪烁

1.Keil5软件介绍

keil是一款广泛用于嵌入式系统开发的软件工具。它支持多种常见的微型控制器架构和编程语言,并提供了丰富的调试辅助功能,可以帮助开发人员在嵌入式系统开发过程中提高效率,缩短开发周期,是嵌入式系统开发领域的重要工具之一。

Keil软件的主要功能和作用

1.提供集成化的开发环境 2.支持多种芯片体系结构,例如ARM、8051、C251等 3.提供强大的调试功能 4.内置丰富的库函数和示例代码 5.可以连线多种仿真器和调试器

Keil软件的优点

1.跨平台支持,可在Window、Linux等操作系统上运行 2.易于使用 3.支持多种编程语言,如C、C++、ASM等 4.丰富的API和库函数 5.高效的编译器 6.强大的调试功能

2.Protues软件介绍

Proteus是英国著名的EDA工具(仿真软件),从原理图布图、代码调试到单片机与外围电路协同仿真,一键切换到PCB设计,真正实现了从概念到产品的完整设计。是世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台,其处理器模型支持8051、HC11、PIC10/12/16/18/24/30/DSPIC33、AVR、ARM、8086和MSP430等,2010年又增加了Cortex和DSP系列处理器,并持续增加其他系列处理器模型。在编译方面,它也支持IAR、Keil和MATLAB等多种编译器。

Protues软件功能

1.原理布图 2.PCB自动或人工布线 3.SPICE电路仿真

Protues软件特点

1.互动的电路仿真 用户甚至可以实时采用诸如RAM,ROM,键盘,马达,LED,LCD,AD/DA,部分SPI器件,部分IIC器件。 2.仿真处理器及其外围电路 可以仿真51系列、AVR、PIC、ARM、等常用主流单片机。还可以直接在基于原理图的虚拟原型上编程,再配合显示及输出,能看到运行后输入输出的效果。配合系统配置的虚拟逻辑分析仪、示波器等,Proteus建立了完备的电子设计开发环境。

(一)利用Keil和protues完成51单片机的简单仿真(流水灯)

1.在Keil软件中编写51程序

打开Keil软件,选择创建新工程 输入文件名 在Atmel中找到AT89C51,然后确定 新建一个文档 然后把代码写进去,参考如下

#include

#include

void delay(int a)

{

int i,j;

for(i=0;i

{

for(j=0;j<1000;j++) _nop_();

}

}

void main(void)

{

char st[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};

int t=0;

while(1)

{

P0=st[t];

delay(50);

t++;

if(t==8){

t=0;

}

}

}

然后将文档进行保存,记得以.c为后缀命名 然后双击Source Group 1 找到刚刚创建的LED2.c加入进去 打开Source Group 1可以看见LED2.c加进去了 然后打开魔法棒 选择Output,打开Creat HEX file 最后进行编译,可以看见没有错误

2.在protues中进行仿真

打开protues,选择新建工程 选择下一步 选择原理图DAFAULT,然后下一步 不用创建PCB布板设计,直接下一步 也不用创建固件项目,直接下一步 然后点完成 进入原理图绘制界面,找到左上的P 搜索AT89C51 再搜索LED 然后返回之前的界面,找到电源 然后将上述找到的元件链接起来 然后右键点击51单片机,选择编辑属性,点击文件夹,将刚刚生成的LED2.hex文件添加然后点完成。

3.仿真

上述步骤完成后,点击左下角的仿真 查看仿真结果

交通灯 - Proteus 8 Professional - 原理图绘制 2023-09-24 17-26-19

(二)ARM开发,使用MDK编译简单STM32程序(LED灯闪烁)

1.使用Keil编写程序

创建工程部份和上述51单片机一致,在选择stm32型号时使用STM32F103R6 按照下图选择序号 代码如下(参考ARM开发:使用MDK编译stm32简单程序(闪烁LED))

#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( int t)

{

unsigned int i,n;

for (n=0;n

for (i=0;i<800;i++);

}

int main(void)

{

LEDInit();

while (1)

{

LED0=0;//LED熄灭

delay(500);//延时时间

LED0=1;//LED亮

delay(500);//延时时间

}

}

2. 电路链接以及烧录

这里我使用的时STM32最小开发板和USB转TTL串口模块 电路连接如下 然后打开我们的烧录程序 然后查看结果

VID_20230924_184345

(三)嵌入式C程序中的register和volatile

1.register

寄存器比内存访问要快,因此可以使用register关键字将C程序中最常用的变量放入寄存器中。关键字register会向编译器提示可以将给定变量放入寄存器中。编译器可以选择是否将其保存在寄存器中。通常,编译器自己进行优化,然后将变量放入寄存器中。 示例

#include

int main() { int i = 10;

register int* a = &i;

printf("%d", *a);

getchar();

return 0; }

2.volatile

volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。 如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。 示例

long square(volatile int*ptr)

{

int a;

a = *ptr;

return a * a;

}

(四)总结

这次实验中我学到了有关51单片机和STM32有关的知识,学会了使用Keil和Protues来尽心仿真。在实验中我能明显的感受到51单片机的编程要比STM32简单不少,究其原因我觉得首先51单片机的结构比STM32简单,其次就是51单片机的开发环境和工具比较成熟,有很多支持它的软件和硬件工具,而STM32这没有这么丰富的资源,很多东西都需要自己去琢磨和学习,然后就是51单片机的输入输出比较简单,而STM32的GPIO就需要借助复杂的库函数和配置文件。 比较嵌入式C程序代码对内存中的各变量的修改操作对比外部对设备的操作。它们的相同点是都需要使用相关的寄存器或地址来访问和修改数据,需要考虑数据的精确性和正确性,要避免出错的情况。它们的不同点是对内存变量进行修改时,C代码可以直接通过变量名来进行更改,而外部设备需要特定的寄存器进行读写,而且对内存中的变量的修改操作主要受限于内存容量,而外部设施的操作受限于设备的特性和功能。

Copyright © 2088 国足世界杯出线形势_世界杯出线 - ybjysq.com All Rights Reserved.
友情链接