嵌入式第二周作业
一、环境搭建
首先需要具备进行实验的软件条件,以便实验正常进行
Proteus的安装
前往Proteus官网下载最新版本的Proteus,按照教程安装好之后并学习一些简单的操作,以便实验的正常进行。
Keil的安装
在Keil官网下载Keil5,根据安装教程自行安装好Keil5,并进行软件的使用学习。
二、实践
进行LED流水灯实验
首先打开Proteus,创建工程,画出如下仿真图
再打开Keil,创建一个新项目,写入代码编译
`#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
#define GPIOB_CRL (*(unsigned int )0x40010C00)
#define GPIOC_CRH ((unsigned int )0x40011004)
#define GPIOA_CRL ((unsigned int *)0x40010800)
#define GPIOB_ODR (*(unsigned int )0x40010C0C)
#define GPIOC_ODR ((unsigned int )0x4001100C)
#define GPIOA_ODR ((unsigned int *)0x4001080C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
void D_LED_LIGHT(void);
void E_LED_LIGHT(void);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
for (i=0;i<t;i++)
{
}
}
void A_LED_LIGHT(){
GPIOA_ODR=0x1<<0; //PA0¸ßµçƽ
GPIOB_ODR=0x1<<0; //PB0µÍµçƽ
GPIOC_ODR=0x0<<14; //PC15¸ßµçƽ
}
void B_LED_LIGHT(){
GPIOA_ODR=0x0<<0; //PA0¸ßµçƽ
GPIOB_ODR=0x1<<0; //PB0µÍµçƽ
GPIOC_ODR=0x1<<14; //PC15¸ßµçƽ
}
void C_LED_LIGHT(){
GPIOA_ODR=0x1<<0; //PA0¸ßµçƽ
GPIOB_ODR=0x0<<0; //PB0¸ßµçƽ
GPIOC_ODR=0x1<<14; //PC15µÍµçƽ
}
void D_LED_LIGHT(){
GPIOA_ODR=0x1<<0;
GPIOB_ODR=0x1<<0;
GPIOC_ODR=0x1<<14; //ȫΪ¸ßµçƽ
}
void E_LED_LIGHT()
{
GPIOA_ODR=0x0<<0; //PA0¸ßµçƽ
GPIOB_ODR=0x0<<0;
GPIOC_ODR=0x0<<14;//ȫΪµÍµçƽ
}
int main(){
// ¿ªÆôʱÖÓ
RCC_APB2ENR |= (1<<2); // ¿ªÆô GPIOA ʱÖÓ
RCC_APB2ENR |= (1<<3); // ¿ªÆô GPIOB ʱÖÓ
RCC_APB2ENR |= (1<<4); // ¿ªÆô GPIOC ʱÖÓ
// ÉèÖà GPIO ΪÍÆÍìÊä³ö
GPIOB_CRL&= 0xfffffff0; //ÉèÖÃλ ÇåÁã
GPIOB_CRL|= 0x00000001; //PB0ÍÆÍìÊä³ö
GPIOC_CRH &= 0xf0ffffff; //ÉèÖÃλ ÇåÁã
GPIOC_CRH|= 0x01000000; //PC15ÍÆÍìÊä³ö
GPIOA_CRL &= 0xfffffff0; //ÉèÖÃλ ÇåÁã
GPIOA_CRL|= 0x00000001; //PA0ÍÆÍìÊä³ö
// LED³õʼ»¯Îª²»ÁÁ
GPIOB_ODR |= (1<<0);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<0);
while(1){
A_LED_LIGHT();
Delay_ms(1000000);
B_LED_LIGHT();
Delay_ms(1000000);
C_LED_LIGHT();
Delay_ms(1000000);
D_LED_LIGHT();
Delay_ms(1000000);
E_LED_LIGHT();
Delay_ms(1000000);
}
}
void SystemInit(){
}`
生成hex文件,烧录进仿真芯片里面,得到如下结果
思考
通过以上实践,结合阅读ARM、STM32技术手册,深入思考STM32F103系列芯片的地址映射和寄存器映射原理,GPIO端口的初始化设置的一般步骤。回答:1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器—>对应相关管脚)的操作有哪些相同与差别?2)为什么51单片机的LED点灯编程要比STM32的简单?
1)嵌入式C程序代码对内存中的变量的修改操作与对外部设备(寄存器)的操作有一些相同和不同之处。
相同之处在于它们都需要通过读写操作来进行数据的读取和修改。无论是对内存中的变量还是对外部设备的寄存器,都需要通过相应的指令来进行读取和写入操作。
不同之处在于它们的读写对象不同。对内存中的变量进行读写操作时,代码直接访问变量的内存地址,通过读写操作来修改变量的值。而对外部设备的寄存器进行操作时,代码需要通过对应的寄存器地址来读写寄存器的值,以实现对设备的控制和设置。
此外,对外部设备的操作通常需要按照特定的通信协议进行,如I2C、SPI等,而对内存中的变量的操作则可以直接使用C语言的赋值语句或指针操作等方式进行。
2)相较于STM32系列芯片,51单片机的LED点灯编程可能会显得比较简单的原因如下:
-
51单片机的架构相对较为简单,指令集和寄存器集相对较小,因此学习和理解起来可能相对容易一些。
-
51单片机的开发工具和编程环境相对成熟,支持的编译器和调试工具较多,且资料和案例丰富,学习和使用起来较为方便。
-
51单片机的GPIO口的设置和操作相对简单,提供了直接的寄存器映射,通过对相关寄存器的设置即可实现GPIO口的初始化和控制。
相比之下,STM32系列芯片的架构更加复杂,具有更多的功能和灵活性,也提供了更为强大的功能和接口,因此学习和使用的难度可能会相对较大一些。但随着理解和熟悉了STM32芯片的架构和编程方法,它也能提供更为灵活和强大的开发能力。
与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。
在嵌入式C程序中,经常会看到register
和volatile
这两个变量修饰符,它们的作用如下:
register
关键字:register
关键字用于提示编译器将变量存储在寄存器中,以提高访问速度。然而,这只是一个建议,编译器可以选择是否将变量存储在寄存器中或者存储在内存中。使用register
关键字的变量通常用于频繁访问的计数器或循环变量等。
下面是一个使用register
关键字的示例:
register int count = 0;
volatile
关键字:volatile
关键字用于告诉编译器该变量可能在程序执行期间被意外修改,因此对该变量的读写操作不能进行优化或缓存。这个关键字通常用于访问外部设备的寄存器、共享变量以及中断服务程序中的变量。
下面是一个使用volatile
关键字的示例:
volatile int *pRegAddr = (int *)0x40001000; // 假设该地址为外设寄存器地址
// 在中断服务程序中更新外设寄存器的值
void ISR()
{
*pRegAddr = 0x01;
}
小结:
在这个示例中,volatile
关键字用于标识pRegAddr
指针所指向的外设寄存器,以确保每次读写寄存器时都会实际地访问该地址,而不是从缓存中获取之前的值。
t *pRegAddr = (int *)0x40001000; // 假设该地址为外设寄存器地址
// 在中断服务程序中更新外设寄存器的值
void ISR()
{
*pRegAddr = 0x01;
}
小结:
在这个示例中,`volatile`关键字用于标识`pRegAddr`指针所指向的外设寄存器,以确保每次读写寄存器时都会实际地访问该地址,而不是从缓存中获取之前的值。
使用`volatile`关键字可以避免编译器对变量读写操作进行优化,以确保变量的实时性和可靠性。