STM32/51单片机编程入门(一)使用C51实现“HELLO”数码管1.安装protues
具体方法:如何安装和使用Proteus软件(超详细)-知乎()
2.安装Keil
Keil5和Keil MDK的区别:Keil5可以用于C51编程,MDK可以用于stm32编程。 两者可以整合。 具体方法参见:让keil5同时支持STM32和C51_keil5可以用于stm32_开心别走博客-CSDN博客
3.利用C51程序设计与仿真实现数码管输出“HELLO”
3.1数码管
数码管分为共阴极级和共阳极级。 共阳极低电平有效,共阴极高电平有效。
Protues中的区分方式:底部的单脚为阴极,顶部的单脚为阳极。
3.2 仿真图:
数码管是动态显示的。 由于人体视觉暂留现象,人体无法察觉到Delay(10)图像的变化,因此看起来是静态的。 如果使用Delay(500),可以清晰地看到数码管的变化。
3.3 代码:
8级代码选择:0x01、0x02、0x04、0x08、0x10、0x20、0x40、0x80
“HELLO”段选择码:0x76,0x79,0x38,0x38,0x3F
#include
unsigned char str[]={0x76,0x79,0x38,0x38,0x3F};
unsigned char wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void delay(unsigned int n)
{
unsigned int j,i;
for(i=0;i<n;i++)
{
for(j=0;j<120;j++);
}
}
void main()
{
unsigned int i;
while(1){
for(i=0;i<5;i++)
{
P3=~wei[i];
P2=str[i];
delay(10);
}
}
}
3.4 模拟改进
引入74LS138解码器可以减少IO口的使用
#include
unsigned char str[]={0x76,0x79,0x38,0x38,0x3F};
unsigned char wei[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
void delay(unsigned int n)
{
unsigned int j,i;
for(i=0;i<n;i++)
{
for(j=0;j<120;j++);
}
}
void main()
{
unsigned int i;
while(1){
for(i=0;i<5;i++)
{
P3=wei[i];
P2=str[i];
delay(10);
}
}
}
(二)通过注册方式点亮LED灯 1、新建
单击菜单栏Project中的New uVision Project,在所需存储地址新建一个名为“Light LED”的文件夹。
进入文件夹,将工程命名为project并保存,然后选择我们的单片机——STM32F103RB(对应的型号写在我们STM32最小板的芯片上),然后点击确定。
检查 CMSIS 下的 COFE 和设备下的启动
点击菜单栏的文件——新建,另存为mian.c(注意:这里一定要在名称后面加上“.c”)
在右侧工具栏中找到 Source Goupe 1,右键单击,然后单击 Add Existing Files to Group 'Source Goupe 1'
在出现的窗口中,选择我们刚刚创建的.c文件并添加
如下图,添加成功
输入以下代码:
//用来存放STM寄存器映射
#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<APB2ENR|=1<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);//延时
}
}
项目没有错误:
2.LED闪烁
2.1 下载ST-LINK数据包
信息包中点击dpinst_amd64.exe进行下载
2.2 硬件连接
点击Keil工具箱的魔棒,点击Debug中的ST-Link Debuggeer
单击旁边的设置并检查 ST-LINK/V2 和 SW
然后点击确定即可编译。 编译成功后,点击LOAD即可看到LED闪烁。
LED等闪烁:
(三)STM32系列芯片原理 1、STM32系列芯片地址映射和寄存器映射原理
地址映射原理:为了保证CPU在执行指令时能够正确访问存储单元,需要在运行时将用户程序中的逻辑地址转换为机器直接寻址的物理地址。 这个过程称为地址映射。
内存映射:下表列出了STM32F10xxx中内置外设的起始地址,如下表所示:
内存和总线架构:
Cortex™-M3 内存映像包括两个位带区域。这两个位字段区域对内存区域中的每个字进行别名。
映射位段存储区中的一个位,在别名存储区中写入一个字,具有对位段区中的目标位进行读-修改-写操作的功能。
效果一样。
在STM32F10xxx中,外设寄存器和SRAM被映射到位段区域,这允许执行单个位段
写和读操作。
下面的映射公式显示了别名区域中的每个字如何对应位带区域中的相应位:
bit_word_addr = bit_band_base + (byte_offset×32) + (bit_number×4)
在:
bit_word_addr 是别名内存区域中字的地址,映射到某个目标位。
bit_band_base 是别名区域的起始地址。
byte_offset 是位域中包含目标位的字节的序号
bit_number 是目标位的位置 (0-31)
2、嵌入式C程序代码对内存(RAM)中各个变量的修改操作与外部设备的操作(寄存器->对应相关引脚)有何异同?
相同的:
(1)存储位置:内存和外部设备中的变量需要特定的存储位置。 变量在内存中有自己的地址,而外部设备通常通过特定的寄存器连接到处理器或控制器,每个寄存器都有一个地址。
(2)数据传输:代码中,变量的修改或者外部设备的操作都需要数据传输。 无论是将值存储到变量中还是将数据发送到外部设备的寄存器,都需要数据传输。
不同的:
(1)访问权限:对于内存中的变量,C程序可以直接读取和修改其值,除非变量被声明为常量或指针限定符限制访问权限。 对于外部设备来说,对其寄存器的访问权限可能会受到硬件保护机制的限制,需要特定的接口和指令才能访问。
(2)时间延迟:对内存中的变量的修改是立即的,并且由于内存通常直接与处理器相连,因此读写延迟很小。 对外部设备的操作通常会涉及到与设备的通信,这可能需要等待设备响应,因此有时会出现较大的延时。
(3) 编程接口:对于内存中的变量,可以通过直接访问变量的地址来读取和修改其值。 对于外部设备来说,通常需要特定的接口和寄存器操作来通信和控制设备。 这些操作可能需要使用特定的寄存器配置、位操作或特殊指令来实现正确的设备控制。
综上所述,虽然在代码层面上对内存和外部设备中的变量的操作可能有一些相似之处,但底层的实现和使用接口却存在明显的差异。 在嵌入式系统中进行编程操作时,正确理解和处理这些差异非常重要。
3、为什么51单片机的LED点亮比STM32的LED点亮容易?
(1)编译语言:
由于STM32的外设和时钟比51高很多,而且51单片机结构相对简单,所以通常使用汇编语言和C语言进行编程。 不过STM32系列的开发工作不会使用汇编语言,因为工程量巨大,寄存器太多太多位数
(2)编程方法:
51单片机在编程前只需要打开配置寄存器,而STM32系列单片机需要先打开相应的时钟,包括打开外部时钟才开始工作。
(3)资源不同:
STM32比普通51单片机有更多的内部资源(寄存器和外设功能),因此在编程时有更多的选择。
(4)C程序中变量修饰符的作用
1、与PC平台上的一般程序不同,嵌入式C程序中经常会看到register和volatile关键字。 请解释这两个变量修饰符的功能,并用C代码示例进行说明。
在嵌入式C程序中,经常会看到register和volatile这两个变量修饰符,它们用来修改变量的行为和存储。
(1)注册关键字:
这是一个例子:
#include ;
int main() {
register int count = 0;
while (count < 10) {
printf("Count: %dn", count);
count++;
}
return 0;
}
在上面的例子中,count变量被声明为寄存器类型,这样编译器会将其存储在寄存器中,以提高循环中的访问效率。
(2)易失性关键字:
这是一个例子:
#include
int main() {
volatile int sensorValue = 0;
while (1) {
// 从传感器读取新值
sensorValue = readSensor();
// 将传感器值打印到控制台
printf("Sensor Value: %dn", sensorValue);
}
return 0;
}
在上面的示例中,sensorValue变量被声明为易失性,这确保在每个循环中从传感器读取最新值并将其打印到控制台。 由于传感器值的变化不受程序控制,编译器将避免优化该变量,以确保每次循环都能获取最新的传感器值。
需要注意的是,在使用register和volatile关键字时,应根据具体的嵌入式系统和编译器考察它们的支持和行为,并在适当的时候使用它们,以保证代码的正确性和性能优化。