打印
[其他ST产品]

stm32-寄存器点灯

[复制链接]
238|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
对于stm32通常有三种编程方式对其进行控制,分别是寄存器、库函数、HAL库。其中库函数用的最多,但是对于新手,使用寄存器的方式能更好地学习单片机内部的结构以及运行过程。

本文就是基于寄存器方式实现简单的点灯操作。

一、常用寄存器
对于每个寄存器都有一段分配的地址,我们只需要找到需要使用到地址,对其进行赋值等操作,就可以实现我们想要的结果。

根据STM32F103中文教程及参考手册,我们就可以得到每个寄存器对应的起始地址,对起始地址进行加减可以得到我们想要控制的端口。

1、RCC寄存器
2、GPIO寄存器
(1)端口配置低寄存器GPIOX_CRL
每个GPIOX都要16个输入、输出端口,即PX0-PX15,而GPIO_CRL则控制的是PX0-PX7,GPIO_CRH控制的是PX8-PX15。

使用特权

评论回复
沙发
个百zz分点个|  楼主 | 2023-11-29 12:31 | 只看该作者
每四位控制一个端口,因此,用十六进制数刚好能够描述每一位的状况。这里所说的状况就是输入输出模式。


每四位中的高两位控制输出模式,低两位控制输出速度,因此由四位控制整个端口输出。

使用特权

评论回复
板凳
个百zz分点个|  楼主 | 2023-11-29 12:31 | 只看该作者
(2)端口配置高寄存器GPIOX_CRH

使用特权

评论回复
地板
个百zz分点个|  楼主 | 2023-11-29 12:43 | 只看该作者
(3)端口输入数据寄存器GPIOX_IDR

IDR和ODR均能对数据进行存储。但是IDR是对输入数据进行存储,ODR是对输出数据进行存储。

高十六位始终为零,只用低十六位存储十六个输入输出引脚的数据。

使用特权

评论回复
5
个百zz分点个|  楼主 | 2023-11-29 12:43 | 只看该作者
(4)端口输出数据寄存器GPIOX_ODR、

使用特权

评论回复
6
个百zz分点个|  楼主 | 2023-11-29 12:44 | 只看该作者
3.输出模式


输出模式由红色方框中的器件进行控制。

(1)推挽输出
推挽输出时,P-MOS有效、N-MOS无效。高低电平均能输出。

(2)开漏输出
开楼输出时,P-MOS无效、N-MOS有效。只能输出低电平,不能输出高电平。

使用特权

评论回复
7
个百zz分点个|  楼主 | 2023-11-29 12:44 | 只看该作者
二、点亮LED
绝对地址=外设基地址+偏移地址

这里的外设基地址也就是外设的初始地址。

本文设计了三个led,接入引脚分别为PA0、PB0、PC15,控制led灯的亮灭,其实就是控制对应I/O口的高低电平。

通常有以下三个步骤:

1、配置时钟寄存器RCC

使用特权

评论回复
8
个百zz分点个|  楼主 | 2023-11-29 12:45 | 只看该作者
根据该系统结构图,如果我们要对GPIOX进行控制输出,我们就需要让APB2工作,因此,在配置时钟时,我们选用的时APB2外设时钟使能寄存器



使用特权

评论回复
9
个百zz分点个|  楼主 | 2023-11-29 12:45 | 只看该作者
由此,计算出该寄存器的绝对地址=0x40021000+0x18=0x4002018

使用特权

评论回复
10
个百zz分点个|  楼主 | 2023-11-29 12:45 | 只看该作者
根据上图,我们需要将选用的GPIOA、GPIOB、GPIOC 的时钟均开启,开启之后才能进行工作,因此就需要将位2、位3、位4置1,代码如下:

//配置复位和时钟控制寄存器(RCC)-使能寄存器APB2
        *(unsigned int*)0x40021018|=(0X7<<2);//7的二进制位111

使用特权

评论回复
11
个百zz分点个|  楼主 | 2023-11-29 12:58 | 只看该作者
2、配置输出模式-推挽输出
点亮led灯只需要输出高低电平就能实现led的亮与灭,需要对输出数据进行存储,因此应该将CRL或者CRH设置为推挽模式



本文采用的是输出最大速度为50MHz的推挽输出模式,因此二进制表示为0011,转化为十六进制为3。

使用特权

评论回复
12
个百zz分点个|  楼主 | 2023-11-29 13:02 | 只看该作者
又因为本文接入引脚分别为PA0、PB0、PC15,所以分别将对应的GPIOX的位0、位0、位15设置为3即可。

代码为:

//配置为推挽输出
//初始地址GPIOA:0x40010800、GPIOB:0x40010C00、GPIOC:0x40011000
        *(unsigned int*)0x40010800|=0x00000003;//PA0,低位,偏移地址0x00
        *(unsigned int*)0x40010C00|=0x00000003;//PB0,低位,偏移地址0x00
        *(unsigned int*)0x40011004|=0x30000000;//PC15,高位,偏移0x04

使用特权

评论回复
13
个百zz分点个|  楼主 | 2023-11-29 13:10 | 只看该作者
3、配置端口输出数据寄存器
低电平点亮、高电平熄灭。

代码为:
//全亮
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C&=~(0x0<<15);
                        delay();
//AB亮,C灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();
//A亮,BC灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C=0x1<<0;
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();
//ABC全灭
                        *(unsigned int*)0x4001080C=0x1;
                        *(unsigned int*)0x40010C0C=0x1;
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();

使用特权

评论回复
14
个百zz分点个|  楼主 | 2023-11-29 13:10 | 只看该作者
4.最终代码
 
#include "stm32f10x.h"                  // Device header

//延时
void delay()
{
        int i,j;
        for (i=0;i<100000;i++)
        for (j=0;j<100;j++);
}
void led()
{
       
}
int main(void)
{
        //初始地址GPIOA:0x40010800、GPIOB:0x40010C00、GPIOC:0x40011000
        // &= ~用来清0(给某一位单独置0)
        // |= 用来给某一位单独置1
        //配置复位和时钟控制寄存器(RCC)-使能寄存器APB2
        *(unsigned int*)0x40021018|=(0X7<<2);
        //配置为推挽输出
        *(unsigned int*)0x40010800|=0x00000003;//PA0,低位,偏移地址0x00
        *(unsigned int*)0x40010C00|=0x00000003;//PB0,低位,偏移地址0x00
        *(unsigned int*)0x40011004|=0x30000000;//PC15,高位,偏移0x04
        //配置端口输出数据寄存器ODR,偏移地址0x0Ch
        while(1)
        {
               
                //全亮
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C&=~(0x0<<15);
                        delay();
          //AB亮,C灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();
                //A亮,BC灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C=0x1<<0;
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();
                //ABC全灭
                        *(unsigned int*)0x4001080C=0x1;
                        *(unsigned int*)0x40010C0C=0x1;
                        *(unsigned int*)0x4001100C=0x1<<15;
                delay();
        }
}

使用特权

评论回复
15
个百zz分点个|  楼主 | 2023-11-29 13:11 | 只看该作者
5、仿真结果

使用特权

评论回复
16
个百zz分点个|  楼主 | 2023-11-29 13:11 | 只看该作者
6、烧录结果

使用特权

评论回复
17
个百zz分点个|  楼主 | 2023-11-29 13:12 | 只看该作者
三、补充
由于STM32最小系统核心板子出厂时已经焊接好了1个led灯(标注了PC13处),一般可通过此灯的点亮让编程者验证自己烧录的代码是否正常运行了。下面是将这个灯也用在流水灯中,进行点灯。在代码中只需要将位移15的地方改为13、以及推挽输出的位置。

使用特权

评论回复
18
个百zz分点个|  楼主 | 2023-11-29 13:12 | 只看该作者
最终代码如下:
#include "stm32f10x.h"                  // Device header

//延时
void delay()
{
        int i,j;
        for (i=0;i<100000;i++)
        for (j=0;j<100;j++);
}
void led()
{
       
}
int main(void)
{
        //初始地址GPIOA:0x40010800、GPIOB:0x40010C00、GPIOC:0x40011000
        // &= ~用来清0(给某一位单独置0)
        // |= 用来给某一位单独置1
        //配置复位和时钟控制寄存器(RCC)-使能寄存器APB2
        *(unsigned int*)0x40021018|=(0X7<<2);
        //配置为推挽输出
        *(unsigned int*)0x40010800|=0x00000003;//PA0,低位,偏移地址0x00
        *(unsigned int*)0x40010C00|=0x00000003;//PB0,低位,偏移地址0x00
        *(unsigned int*)0x40011004|=0x00300000;//PC13,高位,偏移0x04
        //配置端口输出数据寄存器ODR,偏移地址0x0Ch
        while(1)
        {
               
                //全亮
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C&=~(0x0<<13);
                        delay();
          //AB亮,C灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C&=~(0x0);
                        *(unsigned int*)0x4001100C=0x1<<13;
                delay();
                //A亮,BC灭
                        *(unsigned int*)0x4001080C&=~(0x0);
                        *(unsigned int*)0x40010C0C=0x1<<0;
                        *(unsigned int*)0x4001100C=0x1<<13;
                delay();
                //ABC全灭
                        *(unsigned int*)0x4001080C=0x1;
                        *(unsigned int*)0x40010C0C=0x1;
                        *(unsigned int*)0x4001100C=0x1<<13;
                delay();
        }
}

使用特权

评论回复
19
个百zz分点个|  楼主 | 2023-11-29 13:12 | 只看该作者
烧录实现如下:

使用特权

评论回复
20
Stahan| | 2023-11-29 16:09 | 只看该作者
寄存器操作起来好麻烦啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

41

主题

592

帖子

0

粉丝