一、Proteus简介
Proteus软件是英国Lab Center Electronics公司出版的EDA工具软件。它不仅具有其它EDA工具软件的仿真功能,还能仿真单片机及外围器件。它是比较好的仿真单片机及外围器件的工具。虽然国内推广刚起步,但已受到单片机爱好者、从事单片机教学的教师、致力于单片机开发应用的科技工作者的青睐。
Proteus是英国著名的EDA工具(仿真软件),从原理图布图、代码调试到单片机与外围电路协同仿真,一键切换到PCB设计,真正实现了从概念到产品的完整设计。是世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台,其处理器模型支持8051、HC11、PIC10/12/16/18/24/30/DSPIC33、AVR、ARM、8086和MSP430等,2010年又增加了Cortex和DSP系列处理器,并持续增加其他系列处理器模型。在编译方面,它也支持IAR、Keil和MATLAB等多种编译器。
二、安装和使用Protues
三、使用keil编写一个c51程序并在proteus中完成仿真

1、创建过程
创建新工程设置名称及路径
在这里插入图片描述
选择创建原理图
在这里插入图片描述
创建PCB模版
在这里插入图片描述
工程创建成功
在这里插入图片描述
选择51芯片、电阻
在这里插入图片描述
在这里插入图片描述
右键点击电阻编辑属性,将阻值改为300使得LED灯更亮更易观察
在这里插入图片描述
选择LED灯
在这里插入图片描述
选择VCC电源及连接各元件
在这里插入图片描述
右键单击导线添加网络标号使芯片引脚与led灯对应(也可不使用主线直接将LED灯与芯片引脚相连这种情况下可不使用网络标号)
在这里插入图片描述
至此,原理图创建完成
在这里插入图片描述

2、keil程序编写
feil创建新文件
在这里插入图片描述
写入代码后,保存为main.c文件
在这里插入图片描述
将main.c文件添加到source group 1中并编译
在这里插入图片描述

#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、生成hex文件
1、打开project菜单进入options for Target
在这里插入图片描述
2、弹出窗口,点击output,将Create HEX File勾选
在这里插入图片描述
在最下面的窗口下出现如图命令说明hex文件创建成功
在这里插入图片描述
3、开始仿真
进入Proteus中打开之前创建的原理图,右键点击芯片点击编辑属性,在Program File选项中j将你创建的hex文件添加后点击确定即可。
在这里插入图片描述
仿真运行结果

负仿真运行结果

2、stm32硬件

  1. 安装mdk5软件和stm32包,熟悉mdk开发环境,完成一个stm32的简单的通过寄存器方式,用某一个GPIO端口点亮LED等程序。安装过程、示例程序可以参考网上代码(注意:没有stm32开发板硬件之前,可只做程序的编译和仿真测试)

可参考:

https://blog.csdn.net/ssj925319/article/details/108919862

2、仿真运行
程序:
//宏定义,用于存放stm32寄存器映射
#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);//延时时间
}
}
运行成功
在这里插入图片描述

4、思考问题
1、嵌入式C程序中对内存(RAM)中的变量和对外部设备(例如寄存器或GPIO管脚)的操作具有相似之处,但也存在一些重要的差异。以下是它们之间的相似性和差异性:

相似之处:

数据存储和操作:无论是内存中的变量还是外部设备,它们都存储着数据,并且可以通过C程序进行读取和写入操作。
使用变量和寄存器:在C程序中,你可以声明变量来存储数据,就像你可以使用寄存器或外部设备来存储数据一样。
数据访问:对于内存中的变量和外部设备,你都可以使用C语言的运算符(例如赋值运算符“=”)来读取和修改数据。

差异之处:

访问速度:RAM中的变量通常可以更快地访问,因为它们位于CPU的内部存储器或高速缓存中。相比之下,外部设备(如寄存器或GPIO管脚)通常需要通过总线或其他接口与CPU进行通信,因此访问速度较慢。
寄存器和设备特定性:外部设备的操作通常涉及与硬件寄存器的交互,这些寄存器是特定于芯片或外部设备的。因此,对外部设备的操作通常需要使用特定于硬件的寄存器映射和寄存器操作,而对内存中的变量则不需要。
电气特性:与内存不同,外部设备通常与电气特性相关,例如电压电平、电流等。因此,对外部设备的操作可能需要考虑这些电气特性,以防止硬件损坏或不正确的操作。
中断和事件处理:外部设备通常与中断和事件处理相关。你可能需要配置中断以响应外部设备的状态变化,而这在内存中的变量上通常不需要。

2、为什么51单片机的LED点灯编程要比STM32的简单?
架构复杂性:STM32系列微控制器通常具有更复杂的硬件架构和功能集,这使得其编程相对复杂。它们通常具有更多的外设和功能,如多个定时器、UART、SPI、I2C等,这些需要更多的配置和管理。与之相比,51单片机通常具有较为简单的架构,功能相对有限,因此编程起来可能更简单。
开发环境和库:STMicroelectronics为STM32系列提供了庞大的开发生态系统,包括STM32CubeMX和HAL库等工具和库。这些工具和库提供了丰富的功能和自动生成的初始化代码,但也增加了复杂性。相比之下,51单片机的开发通常使用较简单的集成开发环境(IDE)和标准C编程,这可能会感觉更加直观和容易。
学习曲线:由于STM32系列的复杂性,初学者可能需要花更多的时间来学习其架构和相关工具。相反,51单片机通常更容易上手,因为它们通常具有更简单的架构和较少的外设。

3、当编写嵌入式C程序时,有时会使用register和volatile两个变量修饰符,它们与PC平台上的一般程序有一些不同的作用。

register关键字:
register关键字建议编译器将变量存储在寄存器中,以便快速访问。这是一个建议,编译器不一定会将变量存储在寄存器中,它可能根据需要进行修改。

使用register关键字的示例代码:

c
register int count; // 声明一个建议存储在寄存器中的整型变量

int main() {
count = 10; // 对寄存器变量进行赋值
return 0;
}
需要注意的是,register关键字仅仅是给编译器一个建议,并不能确保变量一定存储在寄存器中。具体是否存储在寄存器中取决于编译器的决策和操作系统的限制。

volatile关键字:
volatile关键字用于修饰变量,指示编译器每次访问变量时都要从内存中读取,而不是依赖于寄存器中的缓存值。这在多线程或并发环境中具有重要意义,可以避免对变量的优化读取。

使用volatile关键字的示例代码:

c
volatile int status; // 使用volatile关键字修饰的整型变量

int main() {
status = 0; // 对volatile变量进行赋值
while (status == 0) {
// 循环检查status的值,每次都从内存中读取
// 如果status被其他线程或中断修改,循环将得到更新的值
}
return 0;
}
在上面的示例中,使用volatile修饰的变量status在循环中每次都从内存中读取其最新值,以确保获取到准确的状态。

需要注意的是,volatile关键字主要用于修饰可能被多个线程或中断同时访问并修改的变量,以确保数据的一致性。在单线程或不涉及并发访问的程序中,一般情况下不需要使用volatile关键字。

总结而言,register关键字建议编译器将变量存储在寄存器中,而volatile关键字确保每次访问变量都从内存中读取,以避免优化读取。但需要注意的是,现代编译器通常会根据上下文和优化策略自动处理这些问题,因此在使用这两个关键字时需要根据具体情况评估其必要性。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐