构建库函数(STM32)

2023-05-16

一、定义外设的各基地址,参考存储器映射

// 由存储器的映射可知,片上外设基地址0x4000 0000
#define PERIPH_BASE ((unsigned int)0x4000000)

// APB2 总线的基地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)

// AHB 总线基地址 
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

// GPIO 外设基地址,由系统框图可知,GPIO挂靠在APB2总线上
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

// RCC 外设基地址 在AHB总线上
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)

F103系统框图

在这里插入图片描述

F103存储器映射

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、定义GPIO与RCC寄存器结构体

#define __IO volatile             //volatile 表示易变的变量,防止编译器的优化
typedef unsigned int uint32_t     //一个int型可看做四个字节,32位
typedef unsigned short uint16_t

// 定义GPIO寄存器结构体
typedef struct{
  __IO uint32_t CRL;   // 端口配置低寄存器, 地址偏移 0X00
  __IO uint32_t CRH;   // 端口配置高寄存器, 地址偏移 0X04
  __IO uint32_t IDR;   // 端口数据输入寄存器, 地址偏移 0X08
  __IO uint32_t ODR;   // 端口数据输出寄存器, 地址偏移 0X0C
  __IO uint32_t BSRR;  // 端口位设置/清除寄存器,地址偏移 0X10
  __IO uint32_t BRR;   // 端口位清除寄存器, 地址偏移 0X14
  __IO uint32_t LCKR;  // 端口配置锁定寄存器, 地址偏移 0X18
}GPIO_typeDef;

// 定义时钟CRR结构体
typedef struct{
    __IO uint32_t CR;          // 时钟控制寄存器, 地址偏移 0X00
    __IO uint32_t CFGR;        // 时钟配置寄存器, 地址偏移 0X04
    __IO uint32_t CIR;         // 时钟中断寄存器, 地址偏移 0X08
    __IO uint32_t APB2RSTR;    // APB2外设复位寄存器,地址偏移 0X0C
    __IO uint32_t APB1RSTR;    // APB1外设复位寄存器,地址偏移 0X10
    __IO uint32_t AHBENR;      // AHB外设时钟使能寄存器,地址偏移 0X14
    __IO uint32_t APR2ENR;     // APB2外设时钟使能寄存器,地址偏移 0X18
    __IO uint32_t ApR1ENR:     //APB1外设时钟使能寄存器, 地址偏移 0X1C
    __IO uint32_t BDCR;        //备份域控制寄存器, 地址偏移 0X20
    __IO uint32_t CSR;         //控制/状态寄存器, 地址偏移 0X24
}RCC_TypeDef;

在stm32中,程序的存储器,数据存储器,寄存器和输入输出端口被组织在一个4GB(4294967296)的线性空间中,即从地址0x0000 0000 到 地址0xFFFF FFFF。且被分为8个block。一个block的大小为512MB,注意,这里的存储单位是一个字节。而GPIO中一个寄存器的存储空间是4个字节,上面的七个寄存器的存储空间是连续的。一个uint32_t刚好也为4个字节。因此只需要给结构体传入GPIO的基地址即可。

// GPIOA_BASE只是一串数字,将它转换成GPIO_TypeDef类型指针

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define RCC ((RCC_TypeDef *) RCC_BASE)

在这里插入图片描述

三、利用寄存器使PB0输出低电平

int main(void)
{
  
    // 开启GPIOB的时钟
    RCC -> APB2ENR |= (1<<3);
    // 清空控制PB0的端口位
    GPIO -> CRL &= ~( 0x0F << (4*0));
    // 配置 PB0 为通用的推挽输出,速度为10M
    GPIOB -> CRL |= (1<<4*0);
    // PB0输出低电平
    GPIOB -> ODR |= (0<<0);
    while(1);
    
}
// 对某一位赋值先清零,再|=,某一位清零用&=再取反,这样可避免对其他位置造成影响

四、构造库函数

定义GPIO初始化结构体

typedef struct{  
     uint16_t GPIO_Pin;    // 选择要配置的 GPIO 引脚 
	 uint16_t GPIO_Speed;  // 选择 GPIO 引脚的速率 
     uint16_t GPIO_Mode;   // 选择 GPIO 引脚的工作模式 
 } GPIO_InitTypeDef;

枚举类型定义

typedef enum{  
    GPIO_Speed_10MHz = 1, // 10MHZ (01)b
    GPIO_Speed_2MHz, // 2MHZ (10)b
    GPIO_Speed_50MHz // 50MHZ (11)b
 } GPIOSpeed_TypeDef;
// 枚举会自动递增,即1,2,3(01,10,11)

typedef enum{
    GPIO_Mode_AIN = 0x0,// 模拟输入 (0000 0000)b
 	GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入 (0000 0100)b
 	GPIO_Mode_IPD = 0x28,   // 下拉输入 (0010 1000)b
	GPIO_Mode_IPU = 0x48,  // 上拉输入 (0100 1000)b

	GPIO_Mode_Out_OD = 0x14, // 开漏输出 (0001 0100)b
	GPIO_Mode_Out_PP = 0x10, // 推挽输出 (0001 0000)b
	GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出 (0001 1100)b
	GPIO_Mode_AF_PP = 0x18 // 复用推挽输出 (0001 1000)b
} GPIOMode_TypeDef;
// 定义枚举类型是为了来限定输入参数

定义引脚号

/*GPIO 引脚号定义 */
#define GPIO_Pin_0 (uint16_t)0x0001)    // 0000 0000 0000 0001
#define GPIO_Pin_1 ((uint16_t)0x0002)   // 0000 0000 0000 0010
#define GPIO_Pin_2 ((uint16_t)0x0004)   // 0000 0000 0000 0100
#define GPIO_Pin_3 ((uint16_t)0x0008)   // 0000 0000 0000 1000
#define GPIO_Pin_4 ((uint16_t)0x0010)   // 0000 0000 0001 0000
#define GPIO_Pin_5 ((uint16_t)0x0020)   // 0000 0000 0010 0000
#define GPIO_Pin_6 ((uint16_t)0x0040)   // 0000 0000 0100 0000
#define GPIO_Pin_7 ((uint16_t)0x0080)   // 0000 0000 1000 0000
#define GPIO_Pin_8 ((uint16_t)0x0100)   // 0000 0001 0000 0000
#define GPIO_Pin_9 ((uint16_t)0x0200)   // 0000 0010 0000 0000
#define GPIO_Pin_10 ((uint16_t)0x0400)  // 0000 0100 0000 0000
#define GPIO_Pin_11 ((uint16_t)0x0800)  // 0000 1000 0000 0000
#define GPIO_Pin_12 ((uint16_t)0x1000)  // 0001 0000 0000 0000
#define GPIO_Pin_13 ((uint16_t)0x2000)  // 0010 0000 0000 0000
#define GPIO_Pin_14 ((uint16_t)0x4000)  // 0100 0000 0000 0000
#define GPIO_Pin_15 ((uint16_t)0x8000)  // 1000 0000 0000 0000
#define GPIO_Pin_All ((uint16_t)0xFFFF) // 1111 1111 1111 1111

GPIO初始化函数

/**
  * @brief  Initializes the GPIOx peripheral according to the specified
  *         parameters in the GPIO_InitStruct.
  * @根据GPIO_InitStruct中的特定参数初始化GPIO外设
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @参数中的x可以用来选择GPIO外设
  * @param  GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
  *         contains the configuration information for the specified GPIO peripheral.
  *指向GPIO_InitTypeDef结构体的指针,包含了指定的GPIO外设的配置信息
  * @retval None(没有返回值)
  */

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;

  
/*---------------------------- GPIO Mode Configuration -----------------------*/
    //输入参数GPIO_Mode的低四位暂存在currentmode中
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
    //bit4 是1表示输出,bit4是0表示输入
    //判断bit4是1还是0,即首先判断的输入还是输出模式
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
   
    /* Output mode */
     //输出模式设置输出的速度
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
    //配置低八位
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    // 备份CRL寄存器的值
    tmpreg = GPIOx->CRL;
     // 循环,找到具体对应的是哪一个管脚pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
        // 得到具体管脚pin位置
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
        //如果currentpin=pos ,则找到相应的管脚
      if (currentpin == pos)
      {
          // pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        // 把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变  
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
          // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
          // 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
            // 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 对引脚置 0
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
            // 判断是否为上拉输入模式
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
              // 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 对引脚1
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
      // 把前面处理后的暂存值写入到 CRL 寄存器之中
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
    // 配置端口高 8 位,即 Pin8~Pin15
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
     // 先备份 CRH 寄存器的值 
    tmpreg = GPIOx->CRH;
      // 循环,从 Pin8 开始配对,找出具体的 Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
        // pos 与输入参数 GPIO_PIN 作位与运算
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
        //若 currentpin=pos, 则找到使用的引脚
      if (currentpin == pos)
      {
          //pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
          //把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
          // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
          // 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
         // 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 可对引脚置 0
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
     // 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 可对引脚置1
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
     // 把前面处理后的暂存值写入到 CRH 寄存器之中
    GPIOx->CRH = tmpreg;
  }
}
GPIO引脚工作模式真值表

在这里插入图片描述

  1. 当bit4为1时,工作模式为输出,为0时,工作模式为输入。而输入不需要设置速率
  2. 配置上下拉的工作模式是通过写BSRR或者BRR寄存器来实现的

端口输出函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{ 
 	GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
    GPIOx->BRR = GPIO_Pin;
}

用库函数实现端口输出

#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
int main(void) { 
    // 定义一个 GPIO_InitTypeDef 类型的结构体
	GPIO_InitTypeDef GPIO_InitStructure;
 // 开启 GPIO 端口时钟
    RCC_APB2ENR |= (1<<3);

 // 选择要控制的 GPIO 引脚
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

 // 设置引脚模式为通用推挽输出
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

 // 设置引脚速率为 50MHz
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 // 调用库函数,初始化 GPIO 引脚
 GPIO_Init(GPIOB, &GPIO_InitStructure);
    
 // 使引脚输出低电平, 点亮 LED1
 GPIO_ResetBits(GPIOB,GPIO_Pin_0);
    while (1)
    {
        GPIO_ResetBits(GPIOB,GPIO_Pin_0);

        // 延时一段时间
     	Delay(0xFFFF);

       // 使引脚输出高电平,关闭 LED1
       GPIO_SetBits(GPIOB,GPIO_Pin_0);

       // 延时一段时间 
        Delay(0xFFFF);
    }
}
void Delay(int i)
{
    i--;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

构建库函数(STM32) 的相关文章

  • 如何在 Cortex-M3 (STM32) 上从 RAM 执行函数?

    我正在尝试从 Cortex M3 处理器 STM32 上的 RAM 执行函数 该函数会擦除并重写内部闪存 所以我肯定需要在 RAM 中 但我该怎么做呢 我尝试过的是 使用 memcpy 将函数复制到 RAM 中的字节数组 检查它是否正确对齐
  • 初始化 ST-Link 设备时出错 - 无法连接到设备

    我目前正在使用 ST Link 调试器对我的 STM32F3 Discovery 板进行编程 我使用的IDE是Atollic TrueStudio 5 5 2 现在我面临一个非常奇怪的问题 那就是我不断收到消息 初始化 ST Link 设备
  • 如何让printf在STM32F103上工作?

    我是 STM32F103 世界的新手 我有一个STM32F103的演示代码 我正在使用arm none eabi来编译它 我尝试了在谷歌上可以找到的内容 但到目前为止没有任何效果 我已经花了三天时间来解决这个问题 任何人都可以给我一个运行良
  • 优化 ARM Cortex M3 代码

    我有一个 C 函数 它尝试将帧缓冲区复制到 FSMC RAM 这些函数将游戏循环的帧速率降低至 10FPS 我想知道如何分析反汇编的函数 我应该计算每个指令周期吗 我想知道CPU把时间花在哪里 在哪个部分 我确信该算法也是一个问题 因为它的
  • 139-基于stm32单片机老人居家监护报警系统Proteus仿真+源程序

    资料编号 139 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 MQ4传感器 电位器模拟 MQ2传感器 电位器模拟 蜂鸣器 电机 制作一个基于stm32单片机老人居家监护报警系统Proteus仿真 2 通过MQ2传
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32F103概要

    The STM32F103x4 STM32F103x6 STM32F103xC STM32F103xD and STM32F103xE are a drop in replacement for STM32F103x8 B medium d
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • [屏驱相关]【SWM166-SPI-Y1.28C1测评】+ 有点惊艳的开箱

    耳闻华芯微特许久了 看到论坛得评测活动赶紧上了末班车 毕竟对有屏幕得板子也是很喜欢得 京东快递小哥客客气气 微笑着把快递给了我 好评 直接拆了包 在此之前没看过视频号 所以这个圆盘盘得模具还是有点惊喜的 正面照如下 开机有灯光秀 还有动画
  • VS Code 有没有办法导入 Makefile 项目?

    正如标题所说 我可以从现有的 Makefile 自动填充 c cpp properties json 吗 Edit 对于其他尝试导入 makefile 的人 我找到了一组脚本 它们完全可以实现我想要实现的目标 即通过 VS Code 管理
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 最终启动顺序错误 - STM32L476 的 Eclipse System Workbench 调试

    我正在尝试调试和运行 STM32L476 的简单汇编代码 我已经设置了 Eclipse Oxygen 在 Eclipse 中安装了最新版本的 System Workbench 插件并安装了 ST Link 驱动程序 IDE 成功构建了程序
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • STM32内部时钟

    我对 STM32F7 设备 意法半导体的 Cortex M7 微控制器 上的时钟系统感到困惑 参考手册没有充分阐明这些时钟之间的差异 SYSCLK HCLK FCLK 参考手册中阅读章节 gt RCC 为 Cortex 系统定时器 SysT
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • stm32l0: 执行MI命令失败。使用 vFlashErase 数据包擦除闪存时出错

    我正在使用 Nucleo STM32L031 和 AC6 STM32 工作台 eclipse 我编写应用程序并进入调试模式 一切正常 直到我在应用程序中添加另一个功能 我注意到当我删除 评论 新函数 软件可以再次进入调试模式 但是当我添加

随机推荐