【STM32F103c8t6】点灯教程:寄存器地址详解(适合初学者)

【STM32F103c8t6】点灯篇——寄存器地址版

  • 一. 探究目的
  • 1、了解stm32f103c8t6最小核心板的主要引脚接口;
  • 2、掌握Keil 开发stm32程序的环境搭建和设置;
  • 3、使用GPIO引脚,外接LED灯,编写程序让LED灯周期性亮灭。
  • 二. 探究过程
  • 2.1 stm32f103c8t6
  • 2.1.1 简介
  • 2.1.2 引脚原理图
  • 2.1.3 功能原理图
  • 2.1.3 实验原理图
  • 2.2 Keil 开发stm32程序的环境搭建和设置
  • 2.3 点灯
  • 2.3.1 面包板布局
  • 2.3.2 程序设计思路
  • 2.3.3 完整代码(寄存器地址版)
  • 三、原理探究
  • 3.1 概念引入
  • 3.1.1 寄存器
  • 3.1.2 GPIO
  • 1)推挽输出:
  • 2)开漏输出:
  • 3)复用推挽:
  • 3.2 代码实现过程
  • 3.2.1 寄存器地址配置
  • 3.2.2 GPIO配置寄存器
  • 3.2.3 GPIO数据寄存器
  • 3.2.4 打开时钟
  • 3.2.5 GPIO口配置
  • 3.2.6 初始化灯
  • 3.2.7 灯的闪烁
  • 四. 探究总结
  • 1、所有寄存器都需要时钟才能配置,寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值。
  • 2、51单片机不用配置IO时钟,只是因为默认使用同一个时钟,但是方便的同时也带来了高功耗的缺点。
  • 一. 探究目的

    1、了解stm32f103c8t6最小核心板的主要引脚接口;

    2、掌握Keil 开发stm32程序的环境搭建和设置;

    3、使用GPIO引脚,外接LED灯,编写程序让LED灯周期性亮灭。

    二. 探究过程

    2.1 stm32f103c8t6

    2.1.1 简介

    STM32F103xx中等容量高性能系列,集成了工作频率为72 MHz的高性能Arm® Cortex®-M3 32位RISC内核、高速嵌入式存储器(高达128 KB的Flash存储器和20 KB的SRAM存储器),以及大量连接至2条APB总线的增强型I/O与外设。所有器件均提供2个12位ADC、3个16位通用定时器、2个PWM定时器以及标准和高级通信接口:多达2个I2C和SPI、3个USART、1个USB和1个CAN。(官网简介)

    具体手册可以直接去ST官网下载数据手册
    链接: stm32f103c8
    中文版可到百度网盘下载:
    链接:中文翻教程及参考手册
    提取码:66LL

    2.1.2 引脚原理图

    2.1.3 功能原理图


    图源自 stm32f103c8t6最小系统引脚及功能原理图

    2.1.3 实验原理图

    以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。

    用Proteus 设计一个STM32最小系统板+LED流水灯实验原理图,详情如下:

    2.2 Keil 开发stm32程序的环境搭建和设置

    KEIL、stm32f103相关包以及串口驱动的下载,可以戳下面链接下载:
    链接:百度网盘
    提取码:66LL

    下载完之后按下图创建新工程:


    本篇使用寄存器点灯一定要加入红框内文件!!!

    2.3 点灯

    2.3.1 面包板布局

    2.3.2 程序设计思路

    STM32点灯的常规步骤是:
    1.打开GPIO口的时钟
    2.初始化GPIO口(选择推挽输出)
    3.初始灯灭
    3.设置低电平

    2.3.3 完整代码(寄存器地址版)

    #define GPIOA_BASE 0x40010800
    #define GPIOB_BASE 0x40010C00 
    #define GPIOC_BASE 0x40011000
    
    #define RCC_APB2ENR (*(unsigned int *)0x40021018)
    
    #define GPIOA_CRL (*(unsigned int *)0x40010800)
    #define GPIOB_CRH (*(unsigned int *)0x40010C04)	
    #define GPIOC_CRH (*(unsigned int *)0x40011004) 
    	
    #define GPIOA_ODR (*(unsigned int *)0x4001080C)
    #define GPIOB_ODR (*(unsigned int *)0x40010C0C)
    #define GPIOC_ODR (*(unsigned int *)0x4001100C)	
     
    void SystemInit(void);
    void Delay_ms(volatile  unsigned  int);
    void A_LED_LIGHT(void);
    void B_LED_LIGHT(void);
    void C_LED_LIGHT(void);
    
    void Delay_ms( volatile  unsigned  int  t)
    {
         unsigned  int  i;
         while(t--)
             for (i=0;i<800;i++);
    }
     
    void A_LED_LIGHT(){
    	GPIOA_ODR=0x0<<4;		//PA4低电平
    	Delay_ms(1000);
    	GPIOA_ODR=0x1<<4;		//PA4高电平
    }
    
    void B_LED_LIGHT(){
    	GPIOB_ODR=0x0<<9;		//PB9低电平
    	Delay_ms(1000);
    	GPIOB_ODR=0x1<<9;		//PB9高电平
    }
    
    void C_LED_LIGHT(){
    	GPIOC_ODR=0x0<<15;		//PC15低电平	
    	Delay_ms(1000);
    	GPIOC_ODR=0x1<<15;		//PC15高电平
    }
     
    int main(){
    	int j=100;
    	// 开启时钟
    
    	RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟
    	RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟
    	RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟	
    	
    	// 设置 GPIO 为推挽输出
    	GPIOA_CRL &= 0xfff0ffff; //设置位清零		
    	GPIOA_CRL|=0x00020000; //PA4推挽输出
    	
    	GPIOB_CRH&= 0xffffff0f;	//设置位 清零		
    	GPIOB_CRH|=0x00000020;  //PB9推挽输出
     
    	GPIOC_CRH &= 0x0fffffff; //设置位 清零		
    	GPIOC_CRH|=0x20000000;  //PC15推挽输出 
    	
    	// 3个LED初始化为不亮(即高点位)
    	GPIOA_ODR |= (1<<4);  
    	GPIOB_ODR |= (1<<9); 
    	GPIOC_ODR |= (1<<15); 
    	
    	while(j){
    		A_LED_LIGHT();
    
    		B_LED_LIGHT();
     
    		C_LED_LIGHT();
    	}
    	
    }
     
    void SystemInit(){
    	
    }
    

    三、原理探究

    3.1 概念引入

    3.1.1 寄存器

    寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。

    所有寄存器都需要时钟才能配置,寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值。

    51单片机不用配置IO时钟,只是因为默认使用同一个时钟,但是方便的同时也带来了高功耗的缺点。

    本次实验需要用到GPIO端口,同样的也会用到GPIO端口的寄存器和相应时钟。

    3.1.2 GPIO

    GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。

    这里介绍一下几种常用模式:

    1)推挽输出:

    最大特点是可以正的输出高电平和低电平,但是不能实现" 线与"。

    2)开漏输出:

    无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。但是很方便的调节输出的电平。

    3)复用推挽:

    和推挽输出并没有太大区别,区别仅仅在于触发源不一样, 单纯的推挽输出是在软件中手动地设置0和1实现的。而复用推挽输出指的是在某些模式下,启用了引脚的复用功能,这个时候需要设置该引脚为复用推挽输出,因为这个时候开发人员只需要配置相应的寄存器(参数),不需要关注具体的引脚电平。

    3.2 代码实现过程

    3.2.1 寄存器地址配置


    GPIOA寄存器地址从0x4001 0800开始
    GPIOB寄存器地址从0x4001 0C00开始
    GPIOC寄存器地址从0x4001 1100开始

    #define GPIOA_BASE 0x40010800
    #define GPIOB_BASE 0x40010C00 
    #define GPIOC_BASE 0x40011000
    


    GPIOA~C的时钟是能寄存器为RCC_APB2ENR,由于RCC寄存器地址为0x4002 1000,RCC_APB2ENR的偏移地址为0x18,因此实际RCC_APB2ENR地址为:
    0x4002 1000+0x18=0x4002 1018

    #define RCC_APB2ENR (*(unsigned int *)0x40021018)
    

    3.2.2 GPIO配置寄存器



    由偏移地址可得,
    PA4(4<0~7,低寄存器)的配置寄存器地址为0x4001 0800(0x4001 0800+0x00)
    PA9(9>0~7,高寄存器)的配置寄存器地址为0x4001 0C04 (0x40010C00 +0x04)
    PA15(15>0~7,高寄存器)的配置寄存器地址为0x4001 0804(0x40011000+0x04)

    #define GPIOA_CRL (*(unsigned int *)0x40010800)
    #define GPIOB_CRH (*(unsigned int *)0x40010C04)	
    #define GPIOC_CRH (*(unsigned int *)0x40011004) 
    

    3.2.3 GPIO数据寄存器

    由偏移地址可得:
    PA4的数据寄存器地址为0x4001 080C(0x4001 0800+0x0C)
    PA9的数据寄存器地址为0x4001 0C0C (0x40010C00 +0x0C)
    PA15的数据寄存器地址为0x4001 080C(0x40011000+0x0C)

    #define GPIOA_ODR (*(unsigned int *)0x4001080C)
    #define GPIOB_ODR (*(unsigned int *)0x40010C0C)
    #define GPIOC_ODR (*(unsigned int *)0x4001100C)	
    

    3.2.4 打开时钟

    1)打开GPIOA/GPIOB/GPIOC的时钟APB2。

    	RCC_APB2ENR |= (1<<2); // 开启 GPIOA 时钟
    	RCC_APB2ENR |= (1<<3); // 开启 GPIOB 时钟
    	RCC_APB2ENR |= (1<<4); // 开启 GPIOC 时钟	
    

    其中,RCC_APB2ENR是APB2(应用外设桥2)的时钟使能寄存器。
    1<<2是一个位运算表达式,表示将数字1(0001)左移2位(0100),结果是4(2^2=4)。
    在STM32中,每个外设的时钟使能位在相应的时钟使能寄存器中都有固定的位位置。由上图可得GPIOA是第2位,B是第3位,C是第4位。

    3.2.5 GPIO口配置

    2)初始化GPIO口,将GPIO口选择为推挽输出:

    	GPIOA_CRL &= 0xfff0ffff; //设置位 清零		
    	GPIOA_CRL |= 0x00020000; //PA4推挽输出
    	
    	GPIOB_CRH &= 0xffffff0f;	//设置位 清零		
    	GPIOB_CRH |= 0x00000020;  //PB9推挽输出
     
    	GPIOC_CRH &= 0x0fffffff; //设置位 清零		
    	GPIOC_CRH |= 0x20000000;  //PC15推挽输出 
    

    端口配置低寄存器(GPIOx_CRL),0xfff0ffff是一个十六进制数,其二进制形式为1111 1111 1111 0000 1111 1111 1111 1111,其中只有第16-19位是0,其余位都是1,因此第16-19位(对应于PA4的配置)清零。

    0x00010000的二进制形式为0000 0000 0010 0000 0000 0000 0000 0000,只有第17位是1,这意味着将PA4配置为推挽输出模式。

    同理,由于CRL只配置低8位端口,PB9和PC15都大于0~7,因此应配置高八位CRH。

    3.2.6 初始化灯

    	GPIOB_ODR |= (1<<9); 
    	GPIOC_ODR |= (1<<15); 
    	GPIOA_ODR |= (1<<4);  
    

    3.2.7 灯的闪烁

    void A_LED_LIGHT(){
    	GPIOA_ODR=0x0<<4;		//PA4低电平
    	Delay_ms(1000);
    	GPIOA_ODR=0x1<<4;		//PA4高电平
    }
    
    void B_LED_LIGHT(){
    	GPIOB_ODR=0x0<<9;		//PB9低电平
    	Delay_ms(1000);
    	GPIOB_ODR=0x1<<9;		//PB9高电平
    }
    
    void C_LED_LIGHT(){
    	GPIOC_ODR=0x0<<15;		//PC15低电平	
    	Delay_ms(1000);
    	GPIOC_ODR=0x1<<15;		//PC15高电平
    }
    

    四. 探究总结

    1、所有寄存器都需要时钟才能配置,寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值。

    2、51单片机不用配置IO时钟,只是因为默认使用同一个时钟,但是方便的同时也带来了高功耗的缺点。

    以上则是本次探究内容,如有错漏请多指教。

    作者:双料毒狼_s

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32F103c8t6】点灯教程:寄存器地址详解(适合初学者)

    发表回复