STM32/51单片机编程入门

目录:

一、1.安装proteus

        2.完成一个c51程序

二、1.安装mdk5和stm32

        2.完成一个点亮LED灯的程序编译和仿真测试

三、理论概念:1.嵌入式c程序的操作概念

                          2.51单片机编程优点

四、理论概念:解释关键字register和volatile

五、总结

一、安装proteus和完成一个流水灯的程序输出

1.安装proteus

2.创建工程

进入proteus 8 professional创建原理图。

3.绘制原理图

创建完工程后在里面添加元器件,绘制流水灯原理图:

d3e7cb3a9c824b89a5ddf0b51ff4ac21.jpg

 4.编写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);
		}
	}

编译:

fa877ca29658491988ec528eca9986c2.jpg

5.仿真测试:

dad905a7d55e4958b4818b7e239156f5.jpg

 这里截取视频中的两张图片

028bc6b0416244f0a2c3117bed944937.jpg

492941b3904143a3a20d08961fed3bc7.jpg

二、在mdk5上完成点亮LED的程序和仿真测试

1.安装keil 5和stm32包,完成参数设置。

2.打开keil 5新建工程编写代码并编译:

代码:

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

在keil 5里进行编译:

97fabb643b5649b28ced5a7bac65ce64.jpg

 3.调试仿真

完成参数设置后点击放大镜开始调试:

三、1.嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器--->对应相关管脚)的操作有哪些相同与差别?

数据指针的相同和差别:  

在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能力。

以指针直接操作内存多发生在如下几种情况:

(1) 某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于某特定地址;

(2) 两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单元(称为mail box)书写内容以在对方CPU产生中断;

(3) 读取在ROM或FLASH的特定单元所烧录的汉字和英文字模。

注意:CPU以字节为单位编址,而C语言指针以指向的数据类型长度作自增和自减。理解这一点对于以指针直接操作内存是相当重要的。

函数指针的相同和差别:

(1)C语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;

(2)调用函数实际上等同于“调转指令+参数传递处理+回归位置入栈”,本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU的PC寄存器;

(3)因为函数调用的本质是跳转到某一个地址单元的code去执行,所以可以“调用”一个根本就不存在的函数实体

注意:函数无它,唯指令集合耳;你可以调用一个没有函数体的函数,本质上只是换一个地址开始执行指令。

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

 STM32点灯难度系数要大一点,因为STM32外设资源更多,启动文件更复杂。

51单片机的任何器件只需要配置寄存器打开就可以进行编程,而STM32系列单片机则需要先打开对应的时钟,包括开启后打开外部时钟(晶振)才开始工作。
STM32的内部资源(寄存器和外设功能)较普通的51单片机都要多,基本上接近于计算机的CPU。
STM32基本不会选择汇编语言了,因为工程量巨大,寄存器太多了,位数也多,而51单片机则多使用汇编语言。

四、解释关键字register和volatile

1.register关键字作用:

  如果一个变量用register来修饰,则意味着该变量会作为一个寄存器变量,让该变量的访问速度达到最快。例如,一个程序逻辑中有一个很大的循环,循环中有几个变量要频繁进行操作,这些变量可以声明为register类型。
  register int a; 限制变量定义在寄存器上的修饰符 。
  可定义快速访问的变量,如置于寄存器内,会进一步加快计算速度。
  编译器会尽量的安排CPU的寄存器去寄存这个变量a,如果寄存器不足时,变量a还是会被放在存储器中。

注意事项:

  register只能修饰局部变量,不能修饰全局变量和函数;
  register修饰的变量不能通过取地址来获取寄存器变量;
  register修饰的变量一定是CPU能接受的数据类型。

  2.volatile关键字作用:

是告知编译器编译方法的关键字,不会优化编译。

即具体会体现在以下方面
(1)不会在两个操作之间把volatile变量缓存在寄存器当中,在多任务中,甚至stejmp环境下变量可能被其他程序改变,编译器无法知道,volatile就是告诉编译器这种情况的;
  (2)不做常量合并,常量传播等优化;
  (3)对volatile变量的读写不会被优化掉,如果你对一个变量赋值,但后面没用到,编译器常常可以忽略掉那个赋值操作,然而对Memory Mapped IO处理是不能这样优化的。

3.c程序说明:

 (1)register

代码:
 

#include <stdio.h>
int main()
{
    register int i;
    int tmp=0;
    for(i=1;i<=100;i++)
        tmp+=i;
    printf("总和为 %d\n",tmp);
    return 0;
}

输出结果:

总和为 5050

(2)volatile

源代码:

int tmp, a1, a2;
tmp = (unsigned int *)0x4004;
a1 = *tmp;
a2 = *tmp;

在一些编译软件中,可能会有优化:

int tmp, a1, a2;
tmp = (unsigned int *)0x4004;
a1 = *tmp;
a2 = a1;

这时候就需要用volatile关键字进行说明:

volatile unsigned int *tmp;
int a1, a2;
tmp = (volatile unsigned int *)0x4004;
a1 = *tmp;
a2 = *tmp;

五、总结

       通过本次实验,学会了如何使用Proteus创建工程,原理图的创建以及使用,并且设计一个简单的51原理图,通过keil软件进行编程,然后生成.hex文件在原理图里运行,这样一个简单的原理图就能实现运行了。在用mdk5编译程序时,因为没有硬件的接入,所以只做了代码编译和仿真测试,在这过程中学会了安装mdk5软件,并在里面创建工程,编写代码运行等。

       通过查找资料以及程序学习,了解了GPIO的初始化需要通过时钟配置,输入输出模式设置,最大速率设置三个步骤来实现。在运行代码的过程中,我也发现不论代码怎么写,不论是寄存器,库函数,还是其他的操作系统,要在STM32F103这个单片机点亮LED灯,肯定需要把时钟和GPIO这几个相关的特殊地址,进行赋值或修改数值的操作。并且通过学习资料,还了解到了关键字register和volatile的含义,以及会用简单的c代码进行理解。

        在这整个实验中,虽然遇到了很多问题,比如像keil 4编译代码却无法运行时,找了又找才发现安装的插件少了,然后就重新安装软件才能正常运行。还有就是安装keil 5时,在官网下载的是没有破解的,只有空的一个软件,里面什么功能都不能使用,后来在csdn里找到一个博客发的文章里有破解版的keil 5,也就是在管理员运行里破解,后续跟着安装就能使用正常的软件了。经历了这些,我也会更加熟练的使用keil类软件了,收获了至多的成长。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
机械手臂是一种能够模拟人类手臂运动的机械装置,常用于工业自动化和科研领域。机械手臂的核心是控制系统,控制系统可以使用各种单片机进行编程实现。对于机械手臂的控制,可以选择使用arduino、STM32或51单片机等开源硬件平台。 使用arduino进行机械手臂控制编程是一种常见的选择。arduino是一款简单易用、开源的单片机开发板,具有丰富的周边工具和库函数支持。通过arduino与机械手臂的连接,可以通过编程实现手臂的协调运动、精确定位等功能。在arduino的开发环境中,我们可以使用C/C++编写程序,通过对串口和GPIO等接口的操作来控制各个关节的运动。 与arduino相比,STM32是一款功能更强大的单片机,具有更高的计算性能和更多的外设接口。使用STM32进行机械手臂的控制编程可以实现更加复杂和精确的运动控制。STM32的开发环境是基于Keil或STM32CubeIDE等工具,我们可以使用C语言或者用C++进行编程,通过对定时器、PWM信号、CAN总线等进行操作来控制机械手臂的运动。 51单片机是一款经典的单片机,使用广泛,但相对于arduino和STM32而言,功能和性能有所限制。使用51单片机进行机械手臂的编程可以实现基本的运动控制功能,但不如arduino和STM32那样灵活和强大。 总结来说,使用arduino、STM32或51单片机进行机械手臂的编程可以实现6个自由度的控制。在开源硬件平台中,arduino是入门级的选择,适合初学者进行控制编程STM32具有更好的性能和扩展性,适合需要更复杂控制的应用场景。51单片机则是功能相对受限的选择,适合一些简单的控制需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真的是aaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值