一、安装并熟悉Proteus电路仿真软件,完成一个C51程序设计和仿真
1.安装Proteus仿真软件
2.熟悉Proteus并完成一个C51程序设计和仿真
-
创建工程
自定义工程名称和路径
创建原理图
创建PCB
选择AT89C51芯片进行仿真
项目创建成功
-
新工程创建完成后可以看到有三个窗口,分别是原理图窗口、PCB窗口、源代码窗口
-
绘制LED流水灯原理图
-
编写仿真程序
#include <REGX52.H>
#include<intrins.h>
void Delay200ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 2;
j = 134;
k = 20;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P2=0xFE;//1111 1110
Delay200ms();
P2=0xFD;//1111 1101
Delay200ms();
P2=0xFB;//1111 1011
Delay200ms() ;
P2=0xF7;//1111 0111
Delay200ms() ;
P2=0xEF;//1110 1111
Delay200ms() ;
P2=0xDF;//1101 1111
Delay200ms() ;
P2=0xBF;//1011 1111
Delay200ms() ;
P2=0x7F;//0111 1111
Delay200ms();
}
}
-
仿真程序运行
双击芯片打开编辑元件,选择编译好的.hex文件,点击软件左下角的开始按钮开始仿真
运行成功
二、stm32电亮LED流水灯
-
寄存器
寄存器是CPU中有限存贮容量的高速存贮部件,可用来暂存指令、数据和地址。 简单来讲,如果将我们的计算机比作一栋大楼,而寄存器就是这栋大楼中的每一间房子,寄存器地址便可以看做是房子的门牌号,只不过这个门牌号有点特殊,是由0 1比特流构成的。
-
GPIO
GPIO 是通用输入输出端口的简称,也就是STM32 可控制的引脚, STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。所有的 GPIO 引脚都有基本的输入输出功能。
1..stm32使用寄存器GPIO方式电亮LED流水灯
-
新建stm32工程并引入stm32的固件库
-
模块化编程
为了降低主程序的复杂度、提高代码的可维护性、便于其它开发人员阅读理解代码、便于功能模块代码的迁移故使用模块化编程,在主程序中引入头文件后,直接调用相关功能模块即可。
电亮流水灯需要一个延迟函数Delay
Delay.h代码如下:
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
Delay.c代码如下:
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
编写main文件
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
//打开GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//初始化端口
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//初始化16个端口
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
while(1)
{
GPIO_Write(GPIOA,~0x0001);//0000 0000 0000 0001 第一个LED电亮,其它熄灭
Delay_ms(600);
GPIO_Write(GPIOA,~0x0002);//0000 0000 0000 0010
Delay_ms(600);
GPIO_Write(GPIOA,~0x0004);//0000 0000 0000 010
Delay_ms(600);
GPIO_Write(GPIOA,~0x0008);//0000 0000 0000 100
Delay_ms(600);
GPIO_Write(GPIOA,~0x0010);//0000 0000 0001 0000
Delay_ms(600);
GPIO_Write(GPIOA,~0x0020);//0000 0000 0010 0000
Delay_ms(600);
GPIO_Write(GPIOA,~0x0040);//0000 0000 010 0000
Delay_ms(600);
GPIO_Write(GPIOA,~0x0080);//0000 0000 100 0000
Delay_ms(600);
}
}
-
接线
-
编译、下载
2.总结
用程序直接配置寄存器是最底层、最直接、效率最高的方式,但是由于STM32的结构复杂、寄存器数量多,基于寄存器的方式在面对复杂庞大的工程项目时会极大的降低开发效率。对寄存器编程需要花费较多的时间学习和查阅STM32数据手册,这个过程繁琐、易错,难以移植。库函数就是用宏定义、枚举标识符等代表的数值写入寄存器,替用户摆脱枯燥的机械过程。在一些代码要求高效率的情况下,对寄存器编程是非常必要的。同时,对寄存器的学习与操作,将非常有助于我们在出错时进行程序调试。
三、理论概念-常见嵌入式岗位面试题
1.通过以上实践,结合阅读ARM、STM32技术手册,深入思考STM32F103系列芯片的地址映射和寄存器映射原理,GPIO端口的初始化设置的一般步骤,嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器--->对应相关管脚)的操作有哪些相同与差别?
相同之处:
-
嵌入式C程序可以通过读写内存地址来修改变量的值,类似地,它也可以通过读写寄存器地址来操作外部设备。
-
在内存中修改变量的值和操作外部设备都需要使用特定的指令或函数来实现。
-
对变量和外部设备的修改操作都需要遵循特定的通信协议或硬件接口规范。
差别之处:
-
内存是嵌入式系统中的一部分,嵌入式C程序可以直接访问内存中变量的值并进行修改。而外部设备通常通过寄存器来控制,需要通过特定的寄存器地址来访问和修改设备寄存器的值。
-
内存操作主要涉及读取和写入变量的值,而外部设备操作通常涉及控制和配置设备的功能,例如设置模式、发送指令、读取状态、设置中断、还原现场等。
-
在内存中修改变量的值通常是即时的,而对外部设备的操作可能会引起设备的状态变化,需要按照特定的时序进行操作,如等待设备响应、设备初始化等。
-
对内存中变量的修改不涉及外部环境的限制,而对外部设备的操作可能受限于具体的硬件资源、接口标准、电源供应等因素。
2.为什么51单片机的LED点灯编程要比STM32的简单?
-
复杂性:STM32是基于ARM Cortex-M处理器的32位微控制器,功能和性能比51单片机更强大。它具有更多的外设和功能模块,提供更多的编程选项和灵活性。因此,STM32编程需要更多的配置和参数设置。相比之下,51单片机是8位的经典单片机,相对而言更简单。
-
开发环境:STM32通常使用Keil或CubeMX等集成开发环境进行开发,这些开发工具提供了丰富的库函数和代码生成工具,可以简化程序的编写和配置。而51单片机的开发环境相对简单,常用的是汇编语言或C语言,编写程序时需要直接对寄存器操作,程序量相对较小。
-
硬件资源:STM32通常具有更多的GPIO引脚和外设接口,这意味着需要更多的配置和初始化工作。而51单片机通常具有有限的GPIO引脚和外设接口,使得操控LED等简单外设相对容易。
3.与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明
-
register关键字:
-
register关键字用于提示编译器将变量存储在寄存器中,以提高程序的执行效率。
-
使用register关键字并不能保证变量一定会存储在寄存器中,它只是一个给编译器的提示,具体是否采用寄存器存储取决于编译器的实现以及目标平台的寄存器数量和可用性。
-
一般来说,register关键字适用于频繁使用且对访问速度要求较高的变量,例如循环计数器等。并不是所有的变量都适合使用register关键字,过多地使用register可能导致编译器优化的效果不如预期。
-
下面是一个使用register关键字的示例:
register unsigned int counter; // 使用register关键字声明一个无符号整型寄存器变量
int main() {
for (counter = 0; counter < 100; counter++) {
// 在循环中使用寄存器变量
}
return 0;
}
-
volatile关键字:
-
volatile关键字用于提示编译器,该变量可能会被程序以外的因素修改,因此每次访问时都应该从内存中重新读取变量的值,而不是使用之前缓存的值。
-
常见的可导致变量值发生变化的因素包括硬件中断、多线程环境下的共享变量等。
-
使用volatile关键字可以确保访问变量时的可见性和正确性,防止编译器进行不必要的优化。
-
需要注意的是,volatile关键字并不能保证变量的原子性和同步性,仅仅保证了变量在每次访问时都重新从内存中读取。
-
下面是一个使用volatile关键字的示例:
volatile int flag; // 使用volatile关键字声明一个整型变量
int main() {
while (flag != 1) {
// 检查flag变量是否被修改
}
// flag为1时跳出循环
return 0;
}