51单片机LED闪烁(程序+仿真)
一、keil程序编写
1、新建程序(如下图)
先点击project,在单击new uvision project。
2.命名文件
3.生成main文件
(右键source group再点击Add new item…)
点击c File (.c)
4.在main.c项目中输入以下代码
#include <reg52.h> //包含头文件
sbit led1 = P2^0;
void Delay(int ms) //延时函数根据变量 ms 可以确定延时多长时间
{
int i,j; //定义两个变量
for(i=0;i<ms;i++) //for循环语句
for(j=0;j<110;j++); //延时1ms
}
main() //主函数
{
while(1) //死循环
{
led1 = 0; //点亮P20引脚连接的灯
Delay(500); //调用延时函数,延长500毫秒
led1 = 1; //熄灭P20引脚连接的灯
Delay(500); //调用延时函数,延长500毫秒
}
}
5.生成 .hex 文件
魔术棒-> Output -> 勾选Create HEX File -> ok
二、proteus仿真
1.上传.hex文件到proteus
双击芯片>在program file 中选择刚刚keil生成的.hex文件
2.仿真运行
完成stm32的通过寄存器方式实现LED闪烁
主要使用工具:proteus、keil5、stm32最小开发板、mcuisp烧录软件
一、原理图的会制
1.打开proteus8.15 -> 新建项目 -> 改名 -> 一直next
鼠标右键 -> 放置 -> 元件 -> From Libraries
2.搜索Stm32 -> 选择stm32f103c8
提示,各元器件搜索:
主控芯片:stm32
电阻:res
LED灯:led
鼠标右键 -> 放置 -> 元件 -> From Libraries
二、keil软件配置
1.新建一个工程模板
这里采用正点原子提供的工程模板
复制工程模板,改名为1.点亮一个LED
2.打开新建的项目
在点亮一个LED > user文件夹 > .uvprojx文件
3.程序编写
1)寄存器说明
来自stm32详细参考手册
提取码:4qijGPIOB_ODR地址 = 0x40010C00(GPIOB初始地址) + 0x0C(偏移地址) = 0x40010C0C
2)代码
led.c代码
#include "led.h"
#include "stm32f10x.h"
void LED_Init(void)
{
//声明一个结构体,名字是GPIO_InitStructure
GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIOC的时钟,ENABLE代表使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//GPIOC
//设置引脚为推挽输出Out_PP
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
//定义引脚为哪一号引脚,GPIO_Pin_13就是13号引脚
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
//设置引脚的速度50MHz
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
//初始化GPIO,初始化哪个引脚就对应哪个
GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC,所以引脚对应PC13
GPIO_SetBits(GPIOC,GPIO_Pin_13); //PC13引脚拉高电平
}
led.h代码
#ifndef __LED_H //头文件的格式
#define __LED_H
void LED_Init(void); //函数的声明
#endif
main.c代码
#include "stm32f10x.h"
#include "led.h" //led的头文件
#include "delay.h"
int main(void)
{
delay_init();
LED_Init();
while(1){ //主循环
//由原理图,拉低电平-> LED灯亮
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
delay_ms(500);
//拉高电平-> LED灯灭
GPIO_SetBits(GPIOC,GPIO_Pin_13);
delay_ms(500);
}
}
设置生成hex文件(同c51生成hex操作)
魔术棒-> Output -> 勾选Create HEX File -> ok
然后编译之。
3)将.hex文件加载进proteus(同上)
仿真情况如下,无报错。
4)利用mcuisp将程序烧录到stm32开发板上
使用USB转TTL CH340串口模块来烧写程序
第一步:与stm32F103c8t6最小系统开发版连线
第二种模式简易理解就是我们串口下载的所需要的模式
stm32–usb to ttl
GND–GND
3.3V–3V3
PA9–RXD
PA10–TXD
第二步:使用mcuisp烧录
1.点击port选择CH340所在端口
2.调整bps为256000
3.读器件信息,建立连接
4.上传.hex文件
5.开始编程
运行情况与仿真一致。
总结
刚开始使用stm32编译功能还是有点难操作的,相较于51知识更复杂。
问答题:
一:1)嵌入式C程序代码对内存(RAM)中的各变量的修改操作,与对外部设备(寄存器—>对应相关管脚)的操作有哪些相同与差别?2)为什么51单片机的LED点灯编程要比STM32的简单?
二:. (理论概念-常见嵌入式岗位面试题) 与PC平台上的一般程序不同,嵌入式C程序经常会看见 register和volatile 关键字,请解释这两个变量修饰符的作用,并用C代码示例进行说明。文章来源:https://www.toymoban.com/news/detail-764054.html
1:在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能力。由于51单片机结构相对简单,所以通常多使用汇编语言和C语言编程。而STM32系列的开发工作,不会采用汇编语言,因为工程量巨大,寄存器太多了,位数也多。 51单片机的任何器件只需要配置寄存器打开就可以进行编程,而STM32系列单片机则需要先打开对应的时钟,包括开启后打开外部时钟(晶振)才开始工作。 STM32的内部资源(寄存器和外设功能)较普通的51单片机都要多,基本上接近于计算机的CPU了,所以在程序编写上能有更多的选择。
2:register:在函数内部定义变量时,默认是auto类型,即分配给变量内存。如果定义一个变量用register关键字,那么编译器尽可能把变量存放在CPU内部寄存器中,这样就不必通过内存寻址来访问变量,提高访问效率
volatile: 这个是嵌入式开发必须知道的。用volatile修饰变量或地址,相当于告诉编译器这个值会随时发生变化,每次使用都要去内存中重新读取它的值。如果不用volatile,编译器会有优化操作:在同一进程中当上一次对这个地址操作的值在该进程中没有被修改时候,它会自动把上次读的数据取出来,而不是重新从这个地址取内容。在嵌入式开发中对寄存器或I/O端口的操作都要用volatile。文章来源地址https://www.toymoban.com/news/detail-764054.html
{
......
}
for(register int i=1;i<=1000000;i++)
{
......
}
在第一个 for 循环中,变量 i 存储在内存中,cpu 每次要从内存中取出变量 i,这样 cpu 就要来回读取10000次,只是很低效的。
而在第二个 for 循环中,cpu每次都会直接去寄存器上读取变量i,而不用再去内存读取,因此,代码的效率也会大大提高。
volatile是防止编译器优化,如果是高频繁的变量编译器会自动将变量放到寄存器中,但是有的变量需要实时更新不能间断,放到寄存器中会隔一段时间再去获取变量,导致变量的值不在准确。
在这里插入代码片
int main()
{
volatile int i = 10;
int a = i;
printf("%d", i);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr[ebp - 4], 20h
}
int b = i;
printf("i=%d", b);
return 0;
到了这里,关于使用STM32和C51实现LED闪烁(仿真+程序)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!