单片机入门篇

  • 准备工作:安装keil-C51、protues
  • 编写程序:
    打开keil-c51新建项目

me3z0qvw.png
me3z37ig.png
搜索并选择AT89C51芯片
me3z4e7l.png
创建完项目目录后创建C文件:File--New--保存到项目目录下并取名first.c
me3zgds1.png
右键"Source Group"选择添加文件到Source Group,将前面创建好的C文件添加进去
me3zmf3n.png
me3zmsy6.png

  • 编写程序:
    双击刚刚添加的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在两个黄色箭头状图标中

me40hwf3.png
me40ia9b.png

单片机P1.1口依此连接220欧电阻、LED、接地

me40jmxs.png
双击单片机,Program File选项中选择前面在keil中编译好的HEX文件,点击OK
点击左下角的蓝色开始按钮LED就被点亮了!
me40nbeo.png
me40ooqw.png


STM32对比C51要复杂得多,编程方面,需要先查看芯片数据手册找到需要操作的寄存器地址进行操作,点亮LED为例:
先熟悉两个C语言中的东西:

(uint32_ *)0x18     #表示将地址0x18转换为指向32未无符号整数的指针;
*                     #解引用操作,表示要访问该指针指向的内存位置;
  • 配置时钟
    我这里将LED灯连接在GPIOA的PA1引脚,所以我们需要查看数据手册配置时钟寄存器,先看总线架构:
    metzi6vi.png

数据手册中可以看到配置时钟信息的就是这个叫Reset & Clock control(RCC)的东西,我们的LED灯是连接在GPIOA的PAO1引脚,与GPIOA连接的总线是APB2,所以我们应该配置的时钟寄存器应该就是以RCC和APB2来命名的,所以我们就来找到与之相关的目录,可以看到有一个RCC_APB2ENR和一个RCC_APB2RSTR的目录,RCC_APBA2ENR是时钟使能寄存器,顾名思义,就是可以给外设提供时钟信号;RCC_APBA2RSTR是复位寄存器,可以通过它来给外设重启。因此,我们需要配置的就是RCC_APB2ENR寄存器,对应数据手册7.3.7,

metzbd7g.png
我们再来看RCC_APB2ENR寄存器的地址结构以及描述:
meve4pf1.png
meve5g1f.png
图中可以看到控制IOPA时钟信号的是序号为2这个位,再来看文档描述中说,把bit 2置为1,IO port A时钟启动,因此我们只需要把bit 2置为1,所以现在我们只需要找到RCC_APB2ENR的地址然后使用指针将RCC_APB2ENR中的bit 2置为1即可,查数据手册:
meyac5gc.png
meyacneo.png
我们可以看到有一个Address: 0x18,我们直接操作即可;

有的寄存器的地址类似于Address offset: 0x14(RCC_AHBENR地址)这是一个偏移地址,顾名思义,偏移地址就是由某个基地址偏移出来的,所以我们还需要找到基地址,在Memory map中有一个地址表,所有寄存器地址都在这里:
mevezgbh.png
我们可以看到,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:
GPIO_CRL.png
参考数据手册,一个引脚对应4个bit位,我们给CNF1置为00(通用推挽输出);给MODE0置为11(输出模式0);这里GPIO_CRL的偏移地址是0x00,所以我们直接找到基地址进行操作就行:
mey90gtr.png
GPIO_A的基地址为0x40010800,我们用C语言给这四个位赋值为0011:

volatile uint32_t *p = (volatile uint32_t *)(0x40010800);
*p |= (3<<4);

最后,将PA1引脚输出高电平,参考数据手册中的Port output data register (GPIOX_ODR):
mey9cxur.png
C语言赋值:

volatile uint32_t *PA_1 = (volatile uint32_t *)(0x40010800+0x0C);
*PA_1 |= (1<<1);

无标签
评论区
头像