第二周STM32单片机编程入门作业(点亮LED)
介绍
本博客记录了在安装Keil μVision5开发环境以及烧录工具mcuisp的过程,并演示了如何使用其开发一个简单的程序来通过寄存器方式点亮STM32开发板上的LED。
环境搭建
安装Proteus:
- 下载安装程序:前往Proteus官方网站下载最新版本的Proteus安装程序。
- 运行安装程序:打开下载的安装程序文件,然后运行它。
- 选择安装选项:在安装过程中,你可能需要选择一些安装选项,例如安装路径、组件等。根据你的需求进行选择。
- 等待安装完成:安装程序会将Proteus及其相关组件安装到你选择的目录中。安装过程可能需要一些时间,具体取决于你的计算机性能和安装选项。
- 完成安装:安装完成后,你将收到安装成功的消息。你可以选择启动Proteus,或者手动启动它。
- 激活许可证:输入在网上寻找Proteus的许可证信息以激活软件。按照提供的步骤进行操作。
安装Keil μVision5:
-
下载安装程序:从Keil官网下载μVision5安装程序。
-
运行安装程序:运行安装程序,按照提示操作。
-
选择组件:选择安装μVision IDE和目标硬件支持包。
-
选择安装路径:选择安装文件夹。
-
等待完成:等待安装完成。
-
启动μVision5:从开始菜单或安装目录启动μVision5。
创建新项目
-
在Keil μVision5中,点击“Project”菜单,然后选择“New μVision Project”。
-
在弹出的对话框中,输入项目名称,选择存储项目的目录,并选择目标设备(例如本篇使用为STM32F103C8)。
-
点击“OK”创建新项目。
编写程序
在MDK5中,可以使用C/C++编写STM32程序。以下是本次实验所用代码,演示如何通过寄存器方式点亮STM32F103C8开发板所连接的三个LED灯。
#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 Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
int main(){
RCC_APB2ENR |= (1<<3);
RCC_APB2ENR |= (1<<4);
RCC_APB2ENR |= (1<<2);
GPIOB_CRL &=0xfffffff0 ;
GPIOB_CRL |= 0x00000001;
GPIOC_CRH &= 0x0fffffff;
GPIOC_CRH |= 0x30000000;
GPIOA_CRL &= 0xfffffff0;
GPIOA_CRL |= 0x00000001;
GPIOB_ODR |= (1<<0);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<0);
while(1){
GPIOB_ODR =0x0<<0;
Delay_ms(1000);
GPIOB_ODR =0x1<<0 ;
Delay_ms(1000);
GPIOC_ODR =0x0<<15;
Delay_ms(1000);
GPIOC_ODR =0x1<<15;
Delay_ms(1000);
GPIOA_ODR =0x0<<0;
Delay_ms(1000);
GPIOA_ODR =0x1<<0;
Delay_ms(1000);
}
}
void SystemInit(){
}
组装开发板
通过Proteus的仿真如下
对于USB转TTL模块
和stm32f103c8t6
连接
-
GND
—GND
-
3v3
—3v3
-
TXD
—A10
-
RXD
—A9
-
以下为总电路
编译和下载
-
在Keil中,点击“Project”菜单,选择Option for Target ‘Target 1’,并勾选Output菜单中的Create HEX File选项,点击OK,保存并返回主界面
-
在Keil中,点击“Build”以编译项目。
-
连接开发板与PC主机
-
打开软件mcuisp,在STMISP菜单中,准备上传
HEX文件
到stm32f103c8t6
上填写HEX文件的地址,如图进行勾选后,点击读器件信息后,点击开始编程,开始进行烧录
-
最后成功实现流水灯
流水灯交替闪烁
思考
1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器—>对应相关管脚)的操作有哪些相同与差别?
2)为什么51单片机的LED点灯编程要比STM32的简单?
3)与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。
1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器—>对应相关管脚)的操作有哪些相同与差别?
相同点:
- 访问内存变量和外部设备寄存器都需要使用相应的地址或寄存器名。
- 对变量和寄存器的读写操作都需要考虑并发性和原子性,以防止数据冲突和意外的副作用。
- 在C程序中,使用相似的语法进行变量和寄存器的读写操作,通常使用赋值操作符(=)。
差异点:
- 内存变量通常是在RAM中分配的,可以通过指针进行间接访问,而外部设备的寄存器通常是硬件特定的,需要通过直接内存访问(Direct Memory Access,DMA)或特殊的寄存器操作指令来访问。
- 外部设备寄存器的访问可能需要特殊的寄存器映射表和位操作,以设置或清除特定位的值,而内存变量的访问通常只涉及读写整个变量。
- 内存变量通常受C编译器的管理,可以使用标准的C语法进行操作,而外部设备的寄存器通常需要特定的硬件抽象层(Hardware Abstraction Layer,HAL)或低级编程来进行配置和操作。
2)为什么51单片机的LED点灯编程要比STM32的简单?
这个问题的答案涉及到不同芯片架构和外设设计的差异:
-
芯片架构:51单片机通常使用基于经典的8位MCU架构,而STM32系列芯片采用了更现代的32位ARM Cortex-M架构。32位架构具有更强大的性能和功能,但也需要更多的配置和管理。因此,在某些方面,51单片机可能更简单,因为它们通常用于较简单的任务。
-
GPIO控制:在51单片机中,GPIO控制通常更加简单,因为它们通常只有少量的GPIO引脚,并且对这些引脚的控制是直接的,不需要复杂的初始化或配置。而STM32系列芯片具有更多的GPIO引脚,需要更复杂的初始化和配置,以满足不同的应用需求。
-
开发环境和库:针对STM32芯片的开发通常使用HAL库或CubeMX等工具,这些工具提供了更高级别的抽象和功能,但可能需要更多的学习和配置。而51单片机的开发通常使用较简单的开发环境和标准C语言库。
综上所述,51单片机的LED点灯编程可能会相对简单,因为它们通常用于较简单的应用,拥有较简单的硬件和开发环境。而STM32系列芯片更适合复杂的嵌入式应用,因此需要更多的配置和管理,但也提供了更大的灵活性和性能。选择芯片应基于具体的应用需求来决定。
3)与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。
在嵌入式C编程中,经常会看到 register
和 volatile
这两个变量修饰符,它们用于告诉编译器如何对变量进行优化和访问。下面我将解释它们的作用,并提供相应的C代码示例:
-
register
关键字:register
关键字用于提示编译器将一个变量存储在CPU的寄存器中,以提高访问速度。然而,编译器不一定会遵循这个提示,因为寄存器的数量是有限的,编译器会根据情况决定是否将变量放入寄存器中。- 使用
register
关键字的变量不能被取地址,因为它们可能存储在寄存器中,而寄存器没有地址。
示例代码:
register int x; // 声明一个寄存器变量 x = 10; // 可能存储在寄存器中 int* ptr = &x; // 错误,无法取 `x` 的地址
-
volatile
关键字:volatile
关键字用于告诉编译器不要对变量进行优化,因为变量的值可能会在编程语境之外被改变,例如,由硬件或其他线程/中断修改。- 这可以确保每次访问变量时都会从内存中读取其最新值,而不会使用缓存的值。
示例代码:
volatile int sensorValue; // 使用 volatile 修饰的变量 while (1) { // 假设传感器的值会在中断中更新 int reading = sensorValue; // 从内存中读取 sensorValue 的最新值 // 这里进行操作,不会使用缓存的值 }
总结:
register
提示编译器将变量存储在寄存器中,以提高访问速度,但不一定会生效。volatile
告诉编译器不要对变量进行优化,以确保每次访问都会从内存中读取最新的值,适用于可能被外部因素修改的变量,如硬件寄存器。
————————————————
版权声明:本文参考自CSDN博主 Baker_Streets 的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46628481/article/details/120800967