STM32/51单片机编程入门(点亮LED)

本文介绍了STM32和51单片机的LED编程入门,包括安装和使用Proteus进行电路仿真,以及MDK5中通过寄存器控制GPIO点亮LED。对比了两者在内存操作和编程复杂度上的差异,解析register和volatile关键字在嵌入式C中的作用。
摘要由CSDN通过智能技术生成

一、安装并熟悉Proteus电路仿真软件,完成一个C51程序设计和仿真

1.绘制原理图
用Proteus电路仿真软件,将电路仿真模拟出来。原理图如下:
在这里插入图片描述
2、编写51程序
我是用keil4来编写的,就不需要添加C51的pack了。
程序如下:

	#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);
		}
	}

生成.hex文件,然后回到Proteus软件的原理图中,将.hex文件添加进 AT89C51 芯片中,点击调试按钮,开始仿真,仿真结果如下:
在这里插入图片描述

二、安装mdk5软件和stm32包,熟悉mdk开发环境,完成一个stm32的简单的通过寄存器方式,用某一个GPIO端口点亮LED等程序。

安装好了keil 和 stm32 包,就来开始一个 stm32 的简单程序的编译。
代码如下:

//宏定义,用于存放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);//延时时间
     }
}

编译完成后,显示0错误,0警告:
在这里插入图片描述
然后生成hex文件

三、通过以上实践,结合阅读ARM、STM32技术手册,深入思考STM32F103系列芯片的地址映射和寄存器映射原理,GPIO端口的初始化设置的一般步骤。回答:1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器—>对应相关管脚)的操作有哪些相同与差别?2)为什么51单片机的LED点灯编程要比STM32的简单?

在嵌入式C程序中,对内存(RAM)中的变量进行修改操作与对外部设备(寄存器—>对应相关管脚)的操作有一些相同和一些差别。

相同之处:
1、都需要使用相关的寄存器或地址来访问和修改数据。
2、都需要按照特定的操作顺序和时序进行操作。
3、都需要考虑数据的精确性和正确性,避免出现错误的结果。

差别之处:
1、对内存中的变量进行修改操作时,可以直接使用变量名来进行读取和修改,而对外部设备的操作通常需要通过特定的寄存器或地址进行读写。
2、内存中的变量修改操作通常是在代码中进行的,而对外部设备的操作通常是通过特定的指令或函数调用来完成的。
3、对内存中的变量的修改操作主要受限于内存容量,而对外部设备的操作则受限于设备的特性和功能。

51单片机的LED点灯编程比STM32的简单主要有以下几个原因:
1、51单片机的操作相对简单,它直接操作寄存器和I/O口,只需要将口线设置为输出状态并输出高电平即可点亮LED。而STM32需要通过外设驱动LED,需要进行外设时钟配置和初始化设置,这增加了编程的复杂性。
2、51单片机的资源较少,I/O地址简单,而STM32的外设资源更多,启动文件更加复杂。在51单片机的编程中,点亮LED只需要几行简单的代码就可以实现,易于理解和上手。而STM32的代码相对复杂且麻烦,需要更多的配置和初始化,不容易读懂和编写。
3、51单片机的寄存器编程相对简单直观,初学者容易理解和掌握。而STM32的寄存器编程需要更深入的理解和掌握,对初学者来说有一定的难度。

四、与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。

register关键字用于告诉编译器将变量存储在寄存器中,以便于快速访问。寄存器是位于CPU内部的一种高速存储器,可以更快地读写数据。使用register关键字可以提高程序的执行效率。然而,由于嵌入式系统的资源有限,通常只有少量的寄存器可用,所以register关键字不一定能够被完全实现。

volatile关键字用于告诉编译器该变量可能在任何时刻都会被改变,因此编译器不会对该变量进行优化。在嵌入式系统中,由于硬件的特殊性,某些变量可能会被外部设备或者其他中断程序修改,因此需要使用volatile关键字来确保程序正确地读取和写入这些变量的值。

下面是一个示例代码,演示了register和volatile关键字的用法:

register int counter;  // 使用register关键字声明一个计数器变量

void delay(volatile int num) {
    while(num > 0) {
        num--;
    }
}

int main() {
    volatile int flag = 0;  // 使用volatile关键字声明一个标志变量

    counter = 10;  // 将计数器初始化为10

    while(counter > 0) {
        if(flag == 1) {
            delay(counter);
            counter--;
            flag = 0;
        }
    }

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值