- 准备工作:安装keil-C51、protues
- 编写程序:
打开keil-c51新建项目
搜索并选择AT89C51芯片
创建完项目目录后创建C文件:File--New--保存到项目目录下并取名first.c
右键"Source Group"选择添加文件到Source Group,将前面创建好的C文件添加进去
- 编写程序:
双击刚刚添加的C文件开始编写代码:
#include <REGX51.H> //C51头文件
sbit LED = P1^1; //给P1.1引脚取名为LED
void main(){
while(1){ //程序无限循环
LED = 1; //P1.1引脚输出高电平
}
}
按F7编译程序--点击菜单栏Options for Target(魔法棒图标)--output--勾选Create HEX File
- Protue仿真:
连接硬件:
所需元器件:AT89C51、电阻、GND
将所需元器件添加到左侧器件栏:按P键搜索相应元器件并回车,GND在两个黄色箭头状图标中
单片机P1.1口依此连接220欧电阻、LED、接地
双击单片机,Program File选项中选择前面在keil中编译好的HEX文件,点击OK
点击左下角的蓝色开始按钮LED就被点亮了!
STM32对比C51要复杂得多,编程方面,需要先查看芯片数据手册找到需要操作的寄存器地址进行操作,点亮LED为例:
先熟悉两个C语言中的东西:
(uint32_ *)0x18 #表示将地址0x18转换为指向32未无符号整数的指针;
* #解引用操作,表示要访问该指针指向的内存位置;
数据手册中可以看到配置时钟信息的就是这个叫Reset & Clock control(RCC)的东西,我们的LED灯是连接在GPIOA的PAO1引脚,与GPIOA连接的总线是APB2,所以我们应该配置的时钟寄存器应该就是以RCC和APB2来命名的,所以我们就来找到与之相关的目录,可以看到有一个RCC_APB2ENR和一个RCC_APB2RSTR的目录,RCC_APBA2ENR是时钟使能寄存器,顾名思义,就是可以给外设提供时钟信号;RCC_APBA2RSTR是复位寄存器,可以通过它来给外设重启。因此,我们需要配置的就是RCC_APB2ENR寄存器,对应数据手册7.3.7,
我们再来看RCC_APB2ENR寄存器的地址结构以及描述:
图中可以看到控制IOPA时钟信号的是序号为2这个位,再来看文档描述中说,把bit 2置为1,IO port A时钟启动,因此我们只需要把bit 2置为1,所以现在我们只需要找到RCC_APB2ENR的地址然后使用指针将RCC_APB2ENR中的bit 2置为1即可,查数据手册:
我们可以看到有一个Address: 0x18,我们直接操作即可;
有的寄存器的地址类似于Address offset: 0x14(RCC_AHBENR地址)这是一个偏移地址,顾名思义,偏移地址就是由某个基地址偏移出来的,所以我们还需要找到基地址,在Memory map中有一个地址表,所有寄存器地址都在这里:
我们可以看到,Reset and clock control RCC的地址范围是0x4002 1000 - 0x4002 13FF,0x4002 1000就是我们需要的基地址,用这个基地址加上偏移地址就是我们所需的RCC_AHBENR的地址,即:(0x4002 1000+0x18)就是RCC_AHBENR的地址.
回到RCC_APB2ENR中,现在只需要给这个地址的bit 2这个位赋值为1,就成功启动IOPA的时钟了。bit 2在这个寄存器中是第三位,所以对应二进制就是100,转成十进制就是4,那么只需要给RCC_APB2ENR这个寄存器赋值为4就能成功启动IOPA的时钟:
* (volatile uint32_t *) 0x18 = 4;
上述代码虽然把寄存器的bit 2置为了1,但是把其他位全部置为了0,这显然严重干扰了其他时钟的运行,因此我们还需要将这个代码优化一下,只改变bit 2这个位的值而不改变其他位的值。这里就需要用到C语言中的移位和逻辑运算:假设要把1111这个二进制数第二位的1改为0,我们就需要用到与运算,因为1011&1101=1001,这样既保证了把第二位置为0又保证了其他位不变,那么该如何去构造出1101这个数呢?需要用到移位,移位时,左移多少位右边就会自动补多少个0,如:1<<2 = 100;那么,构造上述1101就可以将1左移1位再取反 ~(1<<1)=1101;将此逻辑运用进来,那么,上述代码就可以优化为如下:
volatile uint32_t *RCC = (volatile uint32_t *) 0x18;
*RCC |= (1<<2);
配置完时钟后需要配置GPIO寄存器的工作模式,所谓的工作模式就是指GPIO端口既可以用于输入也可以用作输出,因此,需要先设置它的工作模式,参考数据手册中的端口配置低寄存器(GPIO_CRL),寄存器0-7为低,8-15为高(GPIO_CRH),我们使用的是PA1引脚,对应GPIO_CRL:
参考数据手册,一个引脚对应4个bit位,我们给CNF1置为00(通用推挽输出);给MODE0置为11(输出模式0);这里GPIO_CRL的偏移地址是0x00,所以我们直接找到基地址进行操作就行:
GPIO_A的基地址为0x40010800,我们用C语言给这四个位赋值为0011:
volatile uint32_t *p = (volatile uint32_t *)(0x40010800);
*p |= (3<<4);
最后,将PA1引脚输出高电平,参考数据手册中的Port output data register (GPIOX_ODR):
C语言赋值:
volatile uint32_t *PA_1 = (volatile uint32_t *)(0x40010800+0x0C);
*PA_1 |= (1<<1);