STM32/51单片机


一、使用C51程序设计点亮LED

1.1 仿真软件Proteus电路建立

在工具栏中搜索AT89C51,选择对应芯片;
在这里插入图片描述
选择合适的器件,诸如LED、电阻等,进行电路连接;

在这里插入图片描述

电路连接完成。

1.2 C51程序的编写与仿真

打开软件Keil,编写LED点亮程序;
选择对应芯片AT89C51:
在这里插入图片描述
创建新项目与新文件,在新文件中编写程序:
代码如下:

//51单片机编程常用的头文件
	#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);
		}
	}

点击保存,后缀为.c文件:
在这里插入图片描述
生成Hex文件,并导入Proteus芯片中:
在这里插入图片描述
点击调试按钮,进行仿真:

在这里插入图片描述
仿真成功。

二、使用STM32点亮LED

2.1 在软件Keil中安装Keil.STM32F1xx包

在这里插入图片描述

2.2 在Keil中选择芯片STM32F103C8

在这里插入图片描述

2.3 创建新项目与新文件

编写程序,代码如下:

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

点击保存,后缀为.c文件;进行编译;
在这里插入图片描述
首先生成Hex文件:
在这里插入图片描述
进行仿真调试:

在这里插入图片描述

三、思考

3.1 嵌入式C程序代码对内存(RAM)中各变量与对外部设备(寄存器—>对应相关管脚)的操作的相同与差别

嵌入式C程序代码可以通过对内存中的变量进行操作来控制外部设备。这可以通过直接操作内存地址或使用特定的寄存器来实现。以下是一个示例代码,展示了如何使用嵌入式C程序代码对外部设备进行操作:

#include <stdint.h>

// 定义外部设备寄存器的地址
volatile uint32_t* const GPIO_BASE = (uint32_t*)0x40000000;
volatile uint32_t* const GPIO_DIR = GPIO_BASE + 0x00;
volatile uint32_t* const GPIO_DATA = GPIO_BASE + 0x04;

// 设置指定管脚为输出
void set_pin_as_output(uint8_t pin) {
    *GPIO_DIR |= (1 << pin);
}

// 设置指定管脚为高电平
void set_pin_high(uint8_t pin) {
    *GPIO_DATA |= (1 << pin);
}

// 设置指定管脚为低电平
void set_pin_low(uint8_t pin) {
    *GPIO_DATA &= ~(1 << pin);
}

int main() {
    // 设置管脚1为输出
    set_pin_as_output(1);

    // 将管脚1设置为高电平
    set_pin_high(1);

    // 程序延时
    for (int i = 0; i < 1000000; i++);

    // 将管脚1设置为低电平
    set_pin_low(1);

    return 0;
}

在这个示例代码中,我们使用了指针来访问特定的内存地址,这些地址对应于外部设备的寄存器。通过设置相应的位或清除相应的位,我们可以控制外部设备的状态。

3.2 为什么51单片机的LED点灯编程要比STM32的简单?

51单片机的LED点灯编程相对于STM32来说更简单,主要有以下几个原因:
1.
51单片机的架构相对较简单:51单片机采用的是8位架构,指令集相对较小,功能相对简单。相比之下,STM32是基于ARM Cortex-M系列的32位微控制器,具有更复杂的架构和更丰富的功能。
2.
51单片机的编程模型相对简单:51单片机使用的是汇编语言或者C语言进行编程,编程模型相对简单直观。而STM32则需要使用更复杂的编程语言和开发环境,例如C语言、C++语言或者使用STM32CubeMX等开发工具。
3.
51单片机的开发工具相对简单:51单片机的开发工具相对简单,常用的有Keil C51等,使用起来相对容易上手。而STM32的开发工具相对复杂,例如Keil MDK、IAR Embedded Workbench等,需要更多的学习和配置。

3.3 与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用

在嵌入式C程序中,register和volatile关键字具有特殊的用途。
register关键字:在嵌入式系统中,通常会有一些寄存器用于存储临时变量或者频繁使用的变量。使用register关键字可以提示编译器将变量存储在寄存器中,以提高程序的执行效率。然而,现代编译器通常会自动进行寄存器分配,因此register关键字的使用已经不再常见。
下面是一个使用register关键字的示例:

register int x;  // 声明一个整型变量x,希望将其存储在寄存器中

volatile关键字:在嵌入式系统中,由于硬件和外部环境的干扰,某些变量的值可能会发生变化,但编译器无法感知这种变化。使用volatile关键字可以告诉编译器该变量是易变的,需要每次都从内存中读取,而不是使用寄存器中的缓存值。
下面是一个使用volatile关键字的示例:

volatile int sensorValue;  // 声明一个整型变量sensorValue,告诉编译器该变量是易变的

四、总结

在此次实验中,由于是第一次进行STM32相关的程序编译,因此遇到了许多困难。软件方面,添加文件等等较为繁琐,编译调试时也会出现其他问题,无法调试等等。总之,我也学到了许多知识,包括项目创建,下载调试代码,以及芯片中各个管脚等等的连接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值