STM32-构建库函数雏形

构建库函数雏形

修改寄存器地址封装

代码清单 8-1 封装寄存器列表

//volatile 表示易变的变量,防止编译器优化
#define __IO volatile
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;

/* GPIO 寄存器列表 */
typedef struct {
    __IO uint32_t MODER; /*GPIO 模式寄存器 地址偏移: 0x00 */
    __IO uint32_t OTYPER; /*GPIO 输出类型寄存器 地址偏移: 0x04 */
    __IO uint32_t OSPEEDR; /*GPIO 输出速度寄存器 地址偏移: 0x08 */
    __IO uint32_t PUPDR; /*GPIO 上拉/下拉寄存器 地址偏移: 0x0C */
    __IO uint32_t IDR; /*GPIO 输入数据寄存器 地址偏移: 0x10 */
    __IO uint32_t ODR; /*GPIO 输出数据寄存器 地址偏移: 0x14 */
    __IO uint16_t BSRRL; /*GPIO 置位/复位寄存器低 16 位部分 地址偏移: 0x18 */
    __IO uint16_t BSRRH; /*GPIO 置位/复位寄存器 高 16 位部分地址偏移: 0x1A */
    __IO uint32_t LCKR; /*GPIO 配置锁定寄存器 地址偏移: 0x1C */
    __IO uint32_t AFR[2]; /*GPIO 复用功能配置寄存器 地址偏移: 0x20-0x24 */
} GPIO_TypeDef;

/*RCC 寄存器列表*/
typedef struct {
    __IO uint32_t CR; /*!< RCC 时钟控制寄存器,地址偏移: 0x00 */
    __IO uint32_t PLLCFGR; /*!< RCC PLL 配置寄存器,地址偏移: 0x04 */
    __IO uint32_t CFGR; /*!< RCC 时钟配置寄存器,地址偏移: 0x08 */
    __IO uint32_t CIR; /*!< RCC 时钟中断寄存器,地址偏移: 0x0C */
    __IO uint32_t AHB1RSTR; /*!< RCC AHB1 外设复位寄存器,地址偏移: 0x10 */
    __IO uint32_t AHB2RSTR; /*!< RCC AHB2 外设复位寄存器,地址偏移: 0x14 */
    __IO uint32_t AHB3RSTR; /*!< RCC AHB3 外设复位寄存器,地址偏移: 0x18 */
    __IO uint32_t RESERVED0; /*!< 保留, 地址偏移: 0x1C */
    __IO uint32_t APB1RSTR; /*!< RCC APB1 外设复位寄存器,地址偏移: 0x20 */
    __IO uint32_t APB2RSTR; /*!< RCC APB2 外设复位寄存器,地址偏移: 0x24*/
    __IO uint32_t RESERVED1[2]; /*!< 保留,地址偏移: 0x28-0x2C*/
    __IO uint32_t AHB1ENR; /*!< RCC AHB1 外设时钟寄存器,地址偏移: 0x30 */
    __IO uint32_t AHB2ENR; /*!< RCC AHB2 外设时钟寄存器,地址偏移: 0x34 */
    __IO uint32_t AHB3ENR; /*!< RCC AHB3 外设时钟寄存器,地址偏移: 0x38 */
/*RCC 后面还有很多寄存器,此处省略*/
} RCC_TypeDef;

每个结构体成员前增加了一个“__IO”前缀,代表了 C 语言中的关键字“volatile”,在 C 语言中该关键字用于表示变量是易变的,要求编译器不要优化。这些结构体内的成员,都代表着寄存器,而寄存器很多时候是由外设或 STM32 芯片状态修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求 CPU 去该变量的地址重新访问。若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。

定义访问外设的结构体指针

代码清单 8-2 指向外设首地址的结构体指针

/*定义 GPIOA-H 寄存器结构体指针*/
#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 GPIOH ((GPIO_TypeDef *) GPIOH_BASE)

/*定义 RCC 外设 寄存器结构体指针*/
#define RCC ((RCC_TypeDef *) RCC_BASE)

定义初始化结构化GPIO_InitTypeDef

代码清单 8-10 定义 GPIO 初始化结构体

typedef uint8_t unsigned char;
/**
* GPIO 初始化结构体类型定义
*/
typedef struct {
    uint32_t GPIO_Pin; /*!< 选择要配置的 GPIO 引脚可输入 GPIO_Pin_ 定义的宏 */
    uint8_t GPIO_Mode; /*!< 选择 GPIO 引脚的工作模式可输入二进制值:00 、01、10、11表示输入/输出/复用/模拟 */
    uint8_t GPIO_Speed; /*!< 选择 GPIO 引脚的速率可输入二进制值:00 、01、10、11表示2/25/50/100MHz */
    uint8_t GPIO_OType; /*!< 选择 GPIO 引脚输出类型可输入二进制值:0 、1表示推挽/开漏 */
    uint8_t GPIO_PuPd; /*!<选择 GPIO 引脚的上/下拉模式可输入二进制值:00 、01、10表示浮空/上拉/下拉*/
} GPIO_InitTypeDef;

定义引脚模式的枚举类型

代码清单 8-11 GPIO 配置参数的枚举定义

/**
* GPIO 端口配置模式的枚举定义
*/
typedef enum {
    GPIO_Mode_IN = 0x00, /*!< 输入模式 */
    GPIO_Mode_OUT = 0x01, /*!< 输出模式 */
    GPIO_Mode_AF = 0x02, /*!< 复用模式 */
    GPIO_Mode_AN = 0x03 /*!< 模拟模式 */
} GPIOMode_TypeDef;

/**
* GPIO 输出类型枚举定义
*/
typedef enum {
    GPIO_OType_PP = 0x00, /*!< 推挽模式 */
    GPIO_OType_OD = 0x01 /*!< 开漏模式 */
} GPIOOType_TypeDef;

/**
* GPIO 输出速率枚举定义
*/
typedef enum {
    GPIO_Speed_2MHz = 0x00, /*!< 2MHz */
    GPIO_Speed_25MHz = 0x01, /*!< 25MHz */
    GPIO_Speed_50MHz = 0x02, /*!< 50MHz */
    GPIO_Speed_100MHz = 0x03 /*!<100MHz */
} GPIOSpeed_TypeDef;

/**
*GPIO 上/下拉配置枚举定义
*/
typedef enum {
    GPIO_PuPd_NOPULL = 0x00,/*浮空*/
    GPIO_PuPd_UP = 0x01, /*上拉*/
    GPIO_PuPd_DOWN = 0x02 /*下拉*/
} GPIOPuPd_TypeDef;

代码清单 8-12 使用枚举类型定义的 GPIO_InitTypeDef 结构体成员

/**
* GPIO 初始化结构体类型定义
*/

typedef struct {
    uint32_t GPIO_Pin; /*!<选择要配置的GPIO引脚可输入GPIO_Pin_定义的宏 */
    GPIOMode_TypeDef GPIO_Mode; /*!<选择GPIO引脚的工作模式可输入 GPIOMode_TypeDef 定义的枚举值*/
    GPIOSpeed_TypeDef GPIO_Speed; /*!<选择GPIO引脚的速率可输入 GPIOSpeed_TypeDef 定义的枚举值 */
    GPIOOType_TypeDef GPIO_OType; /*!< 选择GPIO引脚输出类型可输入 GPIOOType_TypeDef 定义的枚举值*/
    GPIOPuPd_TypeDef GPIO_PuPd; /*!<选择GPIO引脚的上/下拉模式可输入 GPIOPuPd_TypeDef 定义的枚举值*/
} GPIO_InitTypeDef;

代码清单 8-13 给 GPIO_InitTypeDef 初始化结构体赋值范例

GPIO_InitTypeDef InitStruct;

/* LED 端口初始化 */
/*选择要控制的 GPIO 引脚*/
InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置引脚模式为输出模式*/
InitStruct.GPIO_Mode = GPIO_Mode_OUT;
/*设置引脚的输出类型为推挽输出*/
InitStruct.GPIO_OType = GPIO_OType_PP;
/*设置引脚为上拉模式*/
InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
/*设置引脚速率为 2MHz */
InitStruct.GPIO_Speed = GPIO_Speed_2MHz;

定义初始化函数

代码清单 8-14 GPIO 初始化函数

/**
*函数功能:初始化引脚模式
*参数说明: GPIOx,该参数为 GPIO_TypeDef 类型的指针,指向 GPIO 端口的地址
* GPIO_InitTypeDef:GPIO_InitTypeDef 结构体指针,指向初始化变量
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
    uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;

    /*-- GPIO Mode Configuration --*/
    for (pinpos = 0x00; pinpos < 16; pinpos++) {
        /*以下运算是为了通过GPIO_InitStruct->GPIO_Pin算出引脚号0-15*/
        /*经过运算后pos的pinpos位为1,其余为0,与GPIO_Pin_x宏对应。pinpos 变量每次循环加1*/
        pos = ((uint32_t)0x01) << pinpos;        

        /* pos与GPIO_InitStruct->GPIO_Pin做&运算,若运算结果currentpin == pos,则表示GPIO_InitStruct->GPIO_Pin的pinpos位也为1,从而可知pinpos 就是GPIO_InitStruct->GPIO_Pin对应的引脚号:0-15*/
        currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;

        /*currentpin == pos 时执行初始化*/
        if (currentpin == pos) {
            /*GPIOx 端口, MODER寄存器的GPIO_InitStruct->GPIO_Pin对应的引脚,MODER位清空*/
            GPIOx->MODER &= ~(3 << (2 *pinpos));

            /*GPIOx 端口, MODER寄存器的GPIO_Pin引脚,MODER位设置"输入/输出/复用输出/模拟"模式*/
            GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (2 *pinpos));

            /*GPIOx端口,PUPDR寄存器的GPIO_Pin引脚,PUPDR位清空*/
            GPIOx->PUPDR &= ~(3 << ((2 *pinpos)));

            /*GPIOx端口,PUPDR寄存器的GPIO_Pin引脚,PUPDR位设置"上/下拉"模式*/
            GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (2 *pinpos));

            /*若模式为"输出/复用输出"模式,则设置速度与输出类型*/
            if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) ||(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF)) {

                /*GPIOx端口,OSPEEDR寄存器的GPIO_Pin引脚,OSPEEDR位清空*/
                GPIOx->OSPEEDR &= ~(3 << (2 *pinpos));

                /*GPIOx端口, OSPEEDR寄存器的GPIO_Pin引脚,OSPEEDR位设置输出速度*/

                GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed)<<(2 *pinpos));

                /*GPIOx端口, OTYPER寄存器的GPIO_Pin引脚,OTYPER位清空*/
                GPIOx->OTYPER &= ~(1 << (pinpos)) ;

                /*GPIOx端口,OTYPER位寄存器的GPIO_Pin引脚,OTYPER位设置"推挽/开漏"输出类型*/
                GPIOx->OTYPER |= (uint16_t)(( GPIO_InitStruct->GPIO_OType)<< (pinpos));
            }
        }
    }
}

使用函数点亮LED灯

代码清单 8-15 使用函数点亮 LED 灯

/*
使用寄存器的方法点亮 LED 灯
*/
#include "stm32f4xx_gpio.h"

void Delay( uint32_t nCount);

/**
* 主函数,使用封装好的函数来控制 LED 灯
*/
int main(void)
{

    GPIO_InitTypeDef InitStruct;

    /*开启GPIOH 时钟,使用外设时都要先开启它的时钟*/
    RCC->AHB1ENR |= (1<<7);

    /* LED 端口初始化 */

    /*初始化 PH10 引脚*/
    /*选择要控制的 GPIO 引脚*/
    InitStruct.GPIO_Pin = GPIO_Pin_10;
    /*设置引脚模式为输出模式*/
    InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    /*设置引脚的输出类型为推挽输出*/
    InitStruct.GPIO_OType = GPIO_OType_PP;
    /*设置引脚为上拉模式*/
    InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    /*设置引脚速率为 2MHz */
    InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    /*调用库函数,使用上面配置的 GPIO_InitStructure 初始化 GPIO*/
    GPIO_Init(GPIOH, &InitStruct);

    /*使引脚输出低电平,点亮 LED1*/
    GPIO_ResetBits(GPIOH,GPIO_Pin_10);

    /*延时一段时间*/
    Delay(0xFFFFFF);

    /*使引脚输出高电平,关闭 LED1*/
    GPIO_SetBits(GPIOH,GPIO_Pin_10);

    /*初始化 PH11 引脚*/
    InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_Init(GPIOH,&InitStruct);

    /*使引脚输出低电平,点亮 LED2*/
    GPIO_ResetBits(GPIOH,GPIO_Pin_11);

    while (1);
}

//简单的延时函数,让 cpu 执行无意义指令,消耗时间
//具体延时时间难以计算,以后我们可使用定时器精确延时
void Delay( uint32_t nCount)
{
    for (; nCount != 0; nCount--);
}

// 函数为空,目的是为了骗过编译器不报错
void SystemInit(void)
{
}

原文地址:https://www.cnblogs.com/luoxiao23/p/11209308.html

时间: 2024-11-02 10:14:05

STM32-构建库函数雏形的相关文章

第8章 自己写库-构建库函数雏形—零死角玩转STM32-F429系列

第8章 ????自己写库-构建库函数雏形 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ? 本章参考资料:<STM32F4xx 中文参考手册>.<STM32F429规格书> 虽然我们上面用寄存器点亮了 LED,乍看一下好像代码也很简单,但是我们别侥幸以后就可以一直用寄存器开发.在用寄存器点亮 LED 的时候,我们会发现 STM32 的寄存器都是 32 位的,每次

自己写库—构建库函数雏形

1.什么是 STM32 函数库 以上所说的软件库是指“STM32 标准函数库”,它是由 ST公司针对 STM32提供的函数接口,即 API (Application Program Interface),开发者可调用这些函数接口来配置 STM32的寄存器,使开发人员得以脱离最底层的寄存器操作: 实际上,库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口. 库开发方式与直接配置寄存器方式的区别见图 8-1!!!!!!! 2.为什么采用库来开发及学

C#上位机开发(三)—— 构建SerialAssistant雏形

上一篇简单介绍了C#的一些基本知识,并成功的Hello,World,那么从这篇开始,我们来自己动手写一个串口助手: 1.构思功能 串口助手在单片机开发中经常被用来调试,最基本的功能就是接收功能和发送功能,其次,串口在打开前需要进行一些设置:串口列表选择.波特率.数据位.校验位.停止位,这样就有了一个基本的雏形:然后我们在此功能上添加:ASCII/HEX显示,发送,发送新行功能,重复自动发送功能,显示接收数据时间这几项扩展功能: 2.设计布局 根据以上功能,将整个界面分为两块:设置界面(不可缩放)

STM32时钟库函数RCC_DeInit介绍

void RCC_DeInit(void) { RCC->CR |= (uint32_t)0x00000001; //开启内部8MHz时钟 #ifndef STM32F10X_CL //STM32F10X_CL指的是STM32互联系列微处理器 RCC->CFGR &= (uint32_t)0xF8FF0000; //其它类型处理器的CFGR寄存器中27-31位是保留位,24-26为MCO位 #else //而互联型处理器的CFGR寄存器中,28-31位是保留位,24-27位属MCO R

第11章 GPIO输出-使用固件库点亮LED—零死角玩转STM32-F429系列

第11章 ????GPIO输出-使用固件库点亮LED 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ? 本章参考资料:<STM32F4xx参考手册>.库帮助文档<stm32f4xx_dsp_stdperiph_lib_um.chm>. 利用库建立好的工程模板,就可以方便地使用STM32标准库编写应用程序了,可以说从这一章我们才开始迈入STM32开发的大门. LE

第11章 GPIO输出—使用固件库点亮LED

第11章     GPIO输出-使用固件库点亮LED 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:<STM32F4xx参考手册>.库帮助文档<stm32f4xx_dsp_stdperiph_lib_um.chm>. 利用库建立好的工程模板,就可以方便地使用STM32标准库编写应用程序了,可以说从这一章我们才开始迈入STM32开发的大门. LED灯

【玩转单片机系列002】 如何使用STM32提供的DSP库进行FFT

前些日子,因为需要在STM32F103系列处理器上,对采集的音频信号进行FFT,所以花了一些时间来研究如何高效并精确的在STM32F103系列处理器上实现FFT.在网上找了很多这方面的资料做实验并进行比较,最终选择了使用STM32提供的DSP库这种方法. 本文将以一个实例来介绍如何使用STM32提供的DSP库函数进行FFT. 1.FFT运算效率 使用STM32官方提供的DSP库进行FFT,虽然在使用上有些不灵活(因为它是基4的FFT,所以FFT的点数必须是4^n),但其执行效率确实非常高效,看图

stm32编译前为什么要配置keil中C/C++中的define 和include Paths?

这是Keil与编译器的一个相互通信的过程,准确的来说,是编译器读取Keil的配置 ARM系列的有一些公司的库编译器,是与Keil的一些配置通信的. 比如你说的那个 Define,include path 一般来说,我们用Keil做51或者STR710等等一些单片机的程序时候,不需要配置刚才的两个选项,为什么? 因为C51和ARM7的编译器不去读取上述的配置. 而Cortex-M3编译器,则读取上述的配置,并转换成自己的内编译器配置 比如:你在Define里面写:THIS_MY_DEF 那么,编译

STM32F10xx CAN BUS相关库文件&quot;stm32f10x_can.c&quot;内的库函数解析

一.背景: 还是继续CAN通信,要节省开发时间,使用库函数可大大降低开发周期,并且还能确保寄存器的配置几 乎是万无一失,所以,在此就STM32F10xx的CAN操作库函数的使用做个简析. STM32有库函数这件事,对软件开发人员来说是极其利好的,对库函数有褒有贬,说不好的,无非就是 库函数会占用一些额外Ram,并且不利于新手对于这款单片机更深层次的理解等等.我倒觉得,不应当有这 些顾虑,首先,库函数那都是由一些非常牛,并且对该型MCU极其了解的厂方工作人员编写,不去说万无一 失,但也是绝对按照标