野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 9263|回复: 1

[STM32入门篇]第九课 构建库函数雏形点亮LED 作业汇总

[复制链接]
发表于 2022-7-15 00:29:15 | 显示全部楼层 |阅读模式
本帖最后由 GCT 于 2022-7-15 16:33 编辑

入门第九课《自己写库—构建库函数雏形》课后作业汇总

    入门篇 第九课内所有题目:

    9.1

    野火论坛202207141602301762..png

    9.3

    野火论坛202207141604116253..png

    使用新的库函数的方案继续优化之前的led灯闪烁程序


    野火论坛202207141634152812..png

    目标:简化宏定义部分。如果对于每个外设都去写对于基地址物理地址的宏定义,要去写指针还要去翻参考手册,会特别麻烦。

    看过参考手册可以对外设总结出一个共性:所有的寄存器都占用32byte,而且地址连续排列。有没有一种方式能替换这些宏定义?

    C语言有一种结构叫做结构体,它包含的数据成员可以是不同类型的,最重要的是,它的物理地址是对齐的连续排列的。

    野火论坛202207141643524643..png

    结构体的存放特性恰好迎合了我们对于外设寄存器的需求:大小相同,连续排列

    可以将上面的宏定义修改成结构体的定义:

    首先先定义两个新的类型:uint32_t和uint16_t。uint16_t用来定义16bit类型,c语言的short类型恰好大小为2byte;

    uint32_t用来定义32bit类型,c语言的int类型恰好大小为4byte。取个别名应该是为了统一便于编程,u代表unsigned,就不需要写那么多遍unsigned了。

    野火论坛202207141648221457..png


    RCC时钟的寄存器如下:

    野火论坛202207141655262432..png     野火论坛202207141655467698..png


    结构体定义如下:

    野火论坛202207141653142902..png     野火论坛202207141704249729..png

    注意:不管是否要用到,必须写上所有的寄存器!一定要写全!因为地址是连续的,如果不写的话寄存器对应的地址就错了!

    定义完结构体以后需要告诉程序定义的结构体首地址应该在哪里。RCC_BASE基地址之前已经定义好了,但是程序不知道这个是RCC外设的基地址。

   

    使用c语言方法将这个地址(宏定义完还是立即数)强制转换为结构体RCC_TypeDef的指针,告诉它你是一个地址,应该指向结构体。

    用相同的方法定义GPIO寄存器:

    野火论坛202207141739075322..png


    野火论坛202207141739298565..png

    完整头文件stm32f10x.h代码如下:

  1. #ifndef __STM32F10X_H
  2. #define __STM32F10X_H

  3. //用来存放STM32寄存器映射的代码
  4. //外设 peripheral

  5. #define PERIPH_BASE        ((unsigned int)0x40000000)
  6. #define APB2PERIPH_BASE    (PERIPH_BASE + 0x10000)
  7. #define AHBPERIPH_BASE     (PERIPH_BASE + 0x20000)

  8. #define RCC_BASE           (AHBPERIPH_BASE+0x1000)
  9. #define GPIOB_BASE          (APB2PERIPH_BASE+0x0C00)

  10. typedef unsigned int   uint32_t;
  11. typedef unsigned short uint16_t;

  12. typedef struct
  13. {
  14.     uint32_t CR;
  15.     uint32_t CFGR;
  16.     uint32_t CIR;
  17.     uint32_t APB2RSTR;
  18.     uint32_t APB1RSTR;
  19.     uint32_t AHBENR;
  20.     uint32_t APB2ENR;
  21.     uint32_t APB1ENR;
  22.     uint32_t BDCR;
  23.     uint32_t CSR;
  24. }RCC_TypeDef;

  25. #define RCC ((RCC_TypeDef*)RCC_BASE)

  26. typedef struct
  27. {
  28.     uint32_t CRL;
  29.     uint32_t CRH;
  30.     uint32_t IDR;
  31.     uint32_t ODR;
  32.     uint32_t BSRR;
  33.     uint32_t BRR;
  34.     uint32_t LCKR;
  35. }GPIO_TypeDef;

  36. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)

  37. #endif /*__STM32F10X_H*/
复制代码
      现在可以用结构体指针访问修改寄存器了,方法:<结构体指针名> -> <寄存器名>。之前宏定义的别名就可以不需要了。
      修改优化后的红蓝闪烁代码如下:
  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"

  3. #define Red 5
  4. #define Green 0
  5. #define Blue 1

  6. void delay_us(int delay_us);
  7. void delay_ms(int delay_ms);
  8. void initial(int clp);
  9. void ledBlink(int cl);

  10. int main(void)
  11. {
  12.     RCC->APB2ENR |=(1<<3);

  13.     while(1)
  14.     {
  15.         initial(Red);
  16.         ledBlink(Red);

  17.         initial(Blue);
  18.         ledBlink(Blue);
  19.     }
  20. }

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

  24. //微秒级的延时
  25. void delay_us(int delay_us)
  26. {
  27.     volatile unsigned int num;
  28.     volatile unsigned int t;

  29.     for (num = 0; num < delay_us; num++)
  30.     {
  31.         t = 11;
  32.         while (t != 0)
  33.         {
  34.             t--;
  35.         }
  36.     }
  37. }
  38. //毫秒级的延时
  39. void delay_ms(int delay_ms)
  40. {
  41.     volatile unsigned int num;
  42.     for (num = 0; num < delay_ms; num++)
  43.     {
  44.         delay_us(1000);
  45.     }
  46. }

  47. void initial(int clp)
  48. {
  49.     GPIOB->CRL &=~((0x0F)<<(4*clp));
  50.     GPIOB->CRL |=(1<<(4*clp));
  51. }

  52. void ledBlink(int cl)
  53. {
  54.     GPIOB->BSRR &=~(1<<cl);
  55.     delay_ms(100);
  56.     GPIOB->BRR &=~(1<<cl);
  57.     GPIOB->CRL &=~((0x0F)<<(4*cl));
  58. }
复制代码

      目标:优化寄存器操作部分,使代码可读性增强。如果寄存器修改使用|=1<<(4*cl)这样的方法,对于其他开发人员如果没有注释和参考手册,很难理解要干什么。
      而且,使用结构体以后,main.c并没有得到真正的优化,只是别名改了而已。

      创建新的文件:stm32f10x_gpio.c,用来存放封装寄存器操作的函数;其对应的头文件stm32f10x_gpio.h,用来存放对于外设硬件方面的宏定义
      (一般对于硬件有关的 直接进行宏定义)
      在写头文件的宏定义之前,有一点规范是需要注意的。
       野火论坛202207142201394230..png     野火论坛202207142202089847..png     野火论坛202207142235091874..png
      当头文件一多以后,难免会出现相互引用的情况,这样编译就会报错说重复定义。
      解决方法是每次写头文件的时候先写三句宏定义,再写别的定义:
      #ifndef __<头文件名字 全大写>_H

      #define __<头文件名字 全大写>_H
      xxx(此处写其他定义)

      #endif /*__<头文件名字 全大写>_H*/
      也算是一种江湖规矩


      有些定义和函数声明需要用到结构体定义,所以先要包含主头文件stm32f10x.h
       野火论坛202207142304364588..png

      我先对所有的GPIOA-GPIOG进行了定义,为的是方便进行时钟的设置。
       野火论坛202207142310345976..png

      然后定义了用于设置GPIO不同引脚模式的宏(后面课上讲了更好的方法——用枚举类型 灵活性好),这里所有口默认设置为推挽输出,10MHz
       野火论坛202207142321527079..png

      最后定义用于控制具体端口输入输出内容的宏:
       野火论坛202207142323096511..png


      下面先开始写函数部分,在stm32f10x_gpio.c
      先包含它的专属头文件:stm32f10x_gpio.h
      弄清需要那些功能:RCC时钟设置、清空CRLCRL设置模式、位“置1”、位“清除”。
      总体如下:
       野火论坛202207142341346526..png

      因为是模拟的库函数,有些名字是我自己起的,可能和库函数的不一致。

      函数定义需要写到头文件,便于main.c调用

      RCC时钟设置函数(RCC_Enable()):
      参数:RCC寄存器地址,GPIO端口选哪一个
      函数内容:相应RCC寄存器置1操作使能时钟,对应原来的RCC->APB2ENR |= (1<<3);

       野火论坛202207150010357596..png

      CRL清空函数(GPIO_ClearRL()):
      函数内容:清空对应GPIO引脚的初始值或给定值,为的是让后续的按位或 置1操作能够顺利进行不出错(防止出现例如之前的按位或之后变成开漏输出的情况)。
       野火论坛202207142355015244..png

      CRL配置函数(GPIO_ConfigRL()):
      函数内容:配置对应GPIO引脚为推挽输出、10MHz(设定成默认值,现在不能选择其他选项)
       野火论坛202207150003579917..png

      BSRR位设置函数(GPIO_SetBits())

      函数内容:配置对应GPIO引脚输出高电平(LED关)
       野火论坛202207150007149400..png

      BRR位设置函数(GPIO_ResetBits())
      函数内容:配置对应GPIO引脚输出低电平(LED开)
       野火论坛202207150007353484..png

      经过优化后的完整红蓝闪烁代码:
      stm32f10x_gpio.c:
  1. #include "stm32f10x_gpio.h"

  2. void RCC_Enable(RCC_TypeDef *RCCx,uint16_t IOPortx)
  3. {
  4.     RCCx->APB2ENR |=IOPortx;
  5. }
  6. void GPIO_ClearRL(GPIO_TypeDef *GPIOx,uint32_t GPIO_Pin)
  7. {
  8.     GPIOx->CRL &=~GPIO_Pin;
  9. }
  10. void GPIO_ConfigRL(GPIO_TypeDef *GPIOx,uint32_t GPIO_Pin)
  11. {
  12.     GPIOx->CRL |=GPIO_Pin;
  13. }

  14. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  15. {
  16.     GPIOx->BSRR |= GPIO_Pin;
  17. }

  18. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  19. {
  20.     GPIOx->BRR |= GPIO_Pin;
  21. }

复制代码

      stm32f10x_gpio.h:
  1. #ifndef __STM32F10X_GPIO_H
  2. #define __STM32F10X_GPIO_H

  3. #include "stm32f10x.h"

  4. #define IOPortA ((uint16_t)0x0004) //GPIOA
  5. #define IOPortB ((uint16_t)0x0008) //GPIOB
  6. #define IOPortC ((uint16_t)0x0010) //GPIOC
  7. #define IOPortD ((uint16_t)0x0020) //GPIOD
  8. #define IOPortE ((uint16_t)0x0040) //GPIOE
  9. #define IOPortF ((uint16_t)0x0080) //GPIOF
  10. #define IOPortG ((uint16_t)0x0100) //GPIOG

  11. #define GPIOCFG_Pin0   ((uint32_t)0x00000001) //设置Pin0
  12. #define GPIOCFG_Pin1   ((uint32_t)0x00000010) //设置Pin1
  13. #define GPIOCFG_Pin2   ((uint32_t)0x00000100) //设置Pin2
  14. #define GPIOCFG_Pin3   ((uint32_t)0x00001000) //设置Pin3
  15. #define GPIOCFG_Pin4   ((uint32_t)0x00010000) //设置Pin4
  16. #define GPIOCFG_Pin5   ((uint32_t)0x00100000) //设置Pin5
  17. #define GPIOCFG_Pin6   ((uint32_t)0x01000000) //设置Pin6
  18. #define GPIOCFG_Pin7   ((uint32_t)0x10000000) //设置Pin7

  19. #define GPIO_Pin0   ((uint16_t)0x0001) //选择Pin0
  20. #define GPIO_Pin1   ((uint16_t)0x0002) //选择Pin1
  21. #define GPIO_Pin2   ((uint16_t)0x0004) //选择Pin2
  22. #define GPIO_Pin3   ((uint16_t)0x0008) //选择Pin3
  23. #define GPIO_Pin4   ((uint16_t)0x0010) //选择Pin4
  24. #define GPIO_Pin5   ((uint16_t)0x0020) //选择Pin5
  25. #define GPIO_Pin6   ((uint16_t)0x0040) //选择Pin6
  26. #define GPIO_Pin7   ((uint16_t)0x0080) //选择Pin7

  27. #define GPIO_Pin8   ((uint16_t)0x0100) //选择Pin8
  28. #define GPIO_Pin9   ((uint16_t)0x0200) //选择Pin9
  29. #define GPIO_Pin10  ((uint16_t)0x0400) //选择Pin10
  30. #define GPIO_Pin11  ((uint16_t)0x0800) //选择Pin11
  31. #define GPIO_Pin12  ((uint16_t)0x1000) //选择Pin12
  32. #define GPIO_Pin13  ((uint16_t)0x2000) //选择Pin13
  33. #define GPIO_Pin14  ((uint16_t)0x4000) //选择Pin14
  34. #define GPIO_Pin15  ((uint16_t)0x8000) //选择Pin15
  35. #define GPIO_PinAll ((uint16_t)0xFFFF) //选择全部Pin

  36. void RCC_Enable(RCC_TypeDef *RCCx,uint16_t IOPortx);
  37. void GPIO_ClearRL(GPIO_TypeDef *GPIOx,uint32_t GPIO_Pin);
  38. void GPIO_ConfigRL(GPIO_TypeDef *GPIOx,uint32_t GPIO_Pin);
  39. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  40. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

  41. #endif /*__STM32F10X_GPIO_H*/

复制代码

      stm32f10x.h:
  1. #ifndef __STM32F10X_H
  2. #define __STM32F10X_H
  3. //用来存放STM32寄存器映射的代码

  4. //外设 peripheral

  5. #define PERIPH_BASE        ((unsigned int)0x40000000)
  6. //#define APB1PERIPH_BASE    PERIPH_BASE
  7. #define APB2PERIPH_BASE    (PERIPH_BASE + 0x10000)
  8. #define AHBPERIPH_BASE     (PERIPH_BASE + 0x20000)

  9. #define RCC_BASE           (AHBPERIPH_BASE+0x1000)
  10. #define GPIOB_BASE          (APB2PERIPH_BASE+0x0C00)

  11. #define RCC_APB2ENR        *(unsigned int*)(RCC_BASE + 0x18)
  12.    
  13. #define GPIOB_CRL           *(unsigned int*)(GPIOB_BASE + 0x00)
  14. #define GPIOB_CRH           *(unsigned int*)(GPIOB_BASE + 0x04)
  15. #define GPIOB_IDR           *(unsigned int*)(GPIOB_BASE + 0x08)
  16. #define GPIOB_ODR           *(unsigned int*)(GPIOB_BASE + 0x0C)
  17. #define GPIOB_BSRR          *(unsigned int*)(GPIOB_BASE + 0x10)
  18. #define GPIOB_BRR           *(unsigned int*)(GPIOB_BASE + 0x14)
  19. #define GPIOB_LCKR          *(unsigned int*)(GPIOB_BASE + 0x18)

  20. typedef unsigned int   uint32_t;
  21. typedef unsigned short uint16_t;

  22. typedef struct
  23. {
  24.     uint32_t CR;
  25.     uint32_t CFGR;
  26.     uint32_t CIR;
  27.     uint32_t APB2RSTR;
  28.     uint32_t APB1RSTR;
  29.     uint32_t AHBENR;
  30.     uint32_t APB2ENR;
  31.     uint32_t APB1ENR;
  32.     uint32_t BDCR;
  33.     uint32_t CSR;
  34. }RCC_TypeDef;

  35. #define RCC ((RCC_TypeDef*)RCC_BASE)

  36. typedef struct
  37. {
  38.     uint32_t CRL;
  39.     uint32_t CRH;
  40.     uint32_t IDR;
  41.     uint32_t ODR;
  42.     uint32_t BSRR;
  43.     uint32_t BRR;
  44.     uint32_t LCKR;
  45. }GPIO_TypeDef;

  46. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)

  47. #endif /*__STM32F10X_H*/

复制代码

      main.c
  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"

  3. #define Red GPIO_Pin5
  4. #define Green GPIO_Pin0
  5. #define Blue GPIO_Pin1

  6. #define RedCfg GPIOCFG_Pin5
  7. #define GreenCfg GPIOCFG_Pin0
  8. #define BlueCfg GPIOCFG_Pin1

  9. void delay_us(int delay_us);
  10. void delay_ms(int delay_ms);
  11. void initial(int clp);
  12. void ledBlink(int cl);

  13. int main(void)
  14. {
  15.     RCC_Enable(RCC,IOPortB);
  16.     while(1)
  17.     {
  18.         initial(RedCfg);
  19.         ledBlink(Red);
  20.         initial(BlueCfg);
  21.         ledBlink(Blue);
  22.     }
  23. }

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

  27. //微秒级的延时
  28. void delay_us(int delay_us)
  29. {
  30.     volatile unsigned int num;
  31.     volatile unsigned int t;

  32.     for (num = 0; num < delay_us; num++)
  33.     {
  34.         t = 11;
  35.         while (t != 0)
  36.         {
  37.             t--;
  38.         }
  39.     }
  40. }
  41. //毫秒级的延时
  42. void delay_ms(int delay_ms)
  43. {
  44.     volatile unsigned int num;
  45.     for (num = 0; num < delay_ms; num++)
  46.     {
  47.         delay_us(1000);
  48.     }
  49. }

  50. void initial(int clp)
  51. {
  52.     GPIO_ClearRL(GPIOB,(clp*(0xf)));
  53.     GPIO_ConfigRL(GPIOB,clp);
  54. }

  55. void ledBlink(int cl)
  56. {
  57.     GPIO_ResetBits(GPIOB,cl);
  58.     delay_ms(100);
  59.     GPIO_SetBits(GPIOB,cl);
  60.     GPIO_ClearRL(GPIOB,(cl*(0x4)));
  61. }
复制代码
      

      这样修改以后还是可读性增强了不少,但是还是有些不足比如模式不能选择。
      GPIO配置中,输入输出模式有八种分别是:模拟输入、浮空输入、下拉输入、上拉输入、开漏输出、推挽输出、复用开漏输出、复用推挽输出。
      输出速度速度有三种:10mhz、2mhz、50mhz。
      可以通过定义enum枚举类型来限定选项。


      输入输出模式枚举类型:
       野火论坛202207151554555002..png

      输出速度枚举类型:
       野火论坛202207151555359587..png

      再定义一个初始化结构体类型,存放初始化GPIO引脚要用到的三个参数:引脚号、输出速度、输入输出模式。
       野火论坛202207151556418746..png


      对于初始化,官方库有个更详尽复杂的函数能替代这两个函数的功能(官方函数课上已讲解):

       野火论坛202207151612116960..png -> 野火论坛202207151613199104..png



      引入官方初始化函数并申明。
      如何给初始化结构体赋值呢?
      先定义新的GPIO_InitTypeDef初始化结构体类型,然后用“.”访问结构体中的元素。输入输出模式、速度用枚举变量里取的别名。
      赋值完成以后将结构体的地址作为参数传入官方初始化函数。

       野火论坛202207151616146168..png


      将灯闪烁功能整合成一个函数后:
       野火论坛202207151620338104..png


      为了更好的程序可移植性多增加了一些宏定义:
       野火论坛202207151621499431..png


      整合以后的代码:
stm32f10x_gpio.c:
  1. #include "stm32f10x_gpio.h"

  2. void RCC_Enable(RCC_TypeDef *RCCx,uint16_t IOPortx)
  3. {
  4.     RCCx->APB2ENR |=IOPortx;
  5. }
  6. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  7. {
  8.     GPIOx->BSRR |= GPIO_Pin;
  9. }

  10. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  11. {
  12.     GPIOx->BRR |= GPIO_Pin;
  13. }


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

  18.     /*---------------------- GPIO 模式配置 --------------------------*/
  19.     // 把输入参数GPIO_Mode的低四位暂存在currentmode
  20.     currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);

  21.     // bit4是1表示输出,bit4是0则是输入
  22.     // 判断bit4是1还是0,即首选判断是输入还是输出模式
  23.     if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  24.     {
  25.         // 输出模式则要设置输出速度
  26.         currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  27.     }
  28.     /*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  29.     // 配置端口低8位,即Pin0~Pin7
  30.     if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  31.     {
  32.         // 先备份CRL寄存器的值
  33.         tmpreg = GPIOx->CRL;

  34.         // 循环,从Pin0开始配对,找出具体的Pin
  35.         for (pinpos = 0x00; pinpos < 0x08; pinpos++)
  36.         {
  37.             // pos的值为1左移pinpos位
  38.             pos = ((uint32_t)0x01) << pinpos;

  39.             // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
  40.             currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;

  41.             //若currentpin=pos,则找到使用的引脚
  42.             if (currentpin == pos)
  43.             {
  44.                 // pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
  45.                 pos = pinpos << 2;
  46.                 //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
  47.                 pinmask = ((uint32_t)0x0F) << pos;
  48.                 tmpreg &= ~pinmask;

  49.                 // 向寄存器写入将要配置的引脚的模式
  50.                 tmpreg |= (currentmode << pos);

  51.                 // 判断是否为下拉输入模式
  52.                 if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  53.                 {
  54.                     // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
  55.                     GPIOx->BRR = (((uint32_t)0x01) << pinpos);
  56.                 }
  57.                 else
  58.                 {
  59.                     // 判断是否为上拉输入模式
  60.                     if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  61.                     {
  62.                         // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
  63.                         GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
  64.                     }
  65.                 }
  66.             }
  67.         }
  68.         // 把前面处理后的暂存值写入到CRL寄存器之中
  69.         GPIOx->CRL = tmpreg;
  70.     }
  71.     /*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  72.     // 配置端口高8位,即Pin8~Pin15
  73.     if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  74.     {
  75.         // // 先备份CRH寄存器的值
  76.         tmpreg = GPIOx->CRH;

  77.         // 循环,从Pin8开始配对,找出具体的Pin
  78.         for (pinpos = 0x00; pinpos < 0x08; pinpos++)
  79.         {
  80.             pos = (((uint32_t)0x01) << (pinpos + 0x08));

  81.             // pos与输入参数GPIO_PIN作位与运算
  82.             currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);

  83.             //若currentpin=pos,则找到使用的引脚
  84.             if (currentpin == pos)
  85.             {
  86.                 //pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
  87.                 pos = pinpos << 2;

  88.                 //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
  89.                 pinmask = ((uint32_t)0x0F) << pos;
  90.                 tmpreg &= ~pinmask;

  91.                 // 向寄存器写入将要配置的引脚的模式
  92.                 tmpreg |= (currentmode << pos);

  93.                 // 判断是否为下拉输入模式
  94.                 if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  95.                 {
  96.                     // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
  97.                     GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
  98.                 }
  99.                 // 判断是否为上拉输入模式
  100.                 if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  101.                 {
  102.                     // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
  103.                     GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
  104.                 }
  105.             }
  106.         }
  107.         // 把前面处理后的暂存值写入到CRH寄存器之中
  108.         GPIOx->CRH = tmpreg;
  109.     }
  110. }
复制代码


stm32f10x_gpio.h:
  1. #ifndef __STM32F10X_GPIO_H
  2. #define __STM32F10X_GPIO_H

  3. #include "stm32f10x.h"

  4. #define IOPortA ((uint16_t)0x0004) //GPIOA
  5. #define IOPortB ((uint16_t)0x0008) //GPIOB
  6. #define IOPortC ((uint16_t)0x0010) //GPIOC
  7. #define IOPortD ((uint16_t)0x0020) //GPIOD
  8. #define IOPortE ((uint16_t)0x0040) //GPIOE
  9. #define IOPortF ((uint16_t)0x0080) //GPIOF
  10. #define IOPortG ((uint16_t)0x0100) //GPIOG

  11. #define GPIO_Pin0   ((uint16_t)0x0001) //选择Pin0
  12. #define GPIO_Pin1   ((uint16_t)0x0002) //选择Pin1
  13. #define GPIO_Pin2   ((uint16_t)0x0004) //选择Pin2
  14. #define GPIO_Pin3   ((uint16_t)0x0008) //选择Pin3
  15. #define GPIO_Pin4   ((uint16_t)0x0010) //选择Pin4
  16. #define GPIO_Pin5   ((uint16_t)0x0020) //选择Pin5
  17. #define GPIO_Pin6   ((uint16_t)0x0040) //选择Pin6
  18. #define GPIO_Pin7   ((uint16_t)0x0080) //选择Pin7

  19. #define GPIO_Pin8   ((uint16_t)0x0100) //选择Pin8
  20. #define GPIO_Pin9   ((uint16_t)0x0200) //选择Pin9
  21. #define GPIO_Pin10  ((uint16_t)0x0400) //选择Pin10
  22. #define GPIO_Pin11  ((uint16_t)0x0800) //选择Pin11
  23. #define GPIO_Pin12  ((uint16_t)0x1000) //选择Pin12
  24. #define GPIO_Pin13  ((uint16_t)0x2000) //选择Pin13
  25. #define GPIO_Pin14  ((uint16_t)0x4000) //选择Pin14
  26. #define GPIO_Pin15  ((uint16_t)0x8000) //选择Pin15
  27. #define GPIO_PinAll ((uint16_t)0xFFFF) //选择全部Pin

  28. typedef enum
  29. {
  30.     GPIO_Speed_10MHz = 1,//10mhz 01
  31.     GPIO_Speed_2MHz,     //2mhz 10
  32.     GPIO_Speed_50MHz     //50mhz 11
  33. } GPIO_SpeedTypeDef;

  34. typedef enum
  35. {
  36.     GPIO_Mode_AIN = 0x00,            //模拟输入 0000 0000
  37.     GPIO_Mode_In_FLOATING = 0x04,    //浮空输入 0000 0100
  38.     GPIO_Mode_IPD = 0x28,            //下拉输入 0010 1000
  39.     GPIO_Mode_IPU = 0x48,            //上拉输入 0100 1000

  40.     GPIO_Mode_Out_OD = 0x14,         //开漏输出     0001 0100
  41.     GPIO_Mode_Out_PP = 0x10,         //推挽输出     0001 0000
  42.     GPIO_Mode_AF_OD = 0x1C,          //复用开漏输出 0001 1100
  43.     GPIO_Mode_AF_PP = 0x18           //复用推挽输出 0001 1000
  44. } GPIO_ModeTypeDef;

  45. typedef struct
  46. {
  47.     uint16_t GPIO_Pin;
  48.     GPIO_SpeedTypeDef GPIO_Speed;
  49.     GPIO_ModeTypeDef GPIO_Mode;
  50. } GPIO_InitTypeDef;

  51. void RCC_Enable(RCC_TypeDef *RCCx,uint16_t IOPortx);
  52. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  53. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

  54. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

  55. #endif /*__STM32F10X_GPIO_H*/
复制代码



stm32f10x.h:
  1. #ifndef __STM32F10X_H
  2. #define __STM32F10X_H

  3. //用来存放STM32寄存器映射的代码
  4. //外设 peripheral

  5. #define PERIPH_BASE        ((unsigned int)0x40000000)
  6. #define APB2PERIPH_BASE    (PERIPH_BASE + 0x10000)
  7. #define AHBPERIPH_BASE     (PERIPH_BASE + 0x20000)

  8. #define RCC_BASE           (AHBPERIPH_BASE+0x1000)
  9. #define GPIOB_BASE          (APB2PERIPH_BASE+0x0C00)

  10. #define RCC_APB2ENR        *(unsigned int*)(RCC_BASE + 0x18)

  11. #define GPIOB_CRL           *(unsigned int*)(GPIOB_BASE + 0x00)
  12. #define GPIOB_CRH           *(unsigned int*)(GPIOB_BASE + 0x04)
  13. #define GPIOB_IDR           *(unsigned int*)(GPIOB_BASE + 0x08)
  14. #define GPIOB_ODR           *(unsigned int*)(GPIOB_BASE + 0x0C)
  15. #define GPIOB_BSRR          *(unsigned int*)(GPIOB_BASE + 0x10)
  16. #define GPIOB_BRR           *(unsigned int*)(GPIOB_BASE + 0x14)
  17. #define GPIOB_LCKR          *(unsigned int*)(GPIOB_BASE + 0x18)

  18. typedef unsigned int   uint32_t;
  19. typedef unsigned short uint16_t;

  20. typedef struct
  21. {
  22.     uint32_t CR;
  23.     uint32_t CFGR;
  24.     uint32_t CIR;
  25.     uint32_t APB2RSTR;
  26.     uint32_t APB1RSTR;
  27.     uint32_t AHBENR;
  28.     uint32_t APB2ENR;
  29.     uint32_t APB1ENR;
  30.     uint32_t BDCR;
  31.     uint32_t CSR;
  32. } RCC_TypeDef;

  33. #define RCC ((RCC_TypeDef*)RCC_BASE)

  34. typedef struct
  35. {
  36.     uint32_t CRL;
  37.     uint32_t CRH;
  38.     uint32_t IDR;
  39.     uint32_t ODR;
  40.     uint32_t BSRR;
  41.     uint32_t BRR;
  42.     uint32_t LCKR;
  43. } GPIO_TypeDef;

  44. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)

  45. #endif /*__STM32F10X_H*/
复制代码

      红灯闪烁:
main.c:
  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"

  3. #define Red GPIO_Pin5
  4. #define Green GPIO_Pin0
  5. #define Blue GPIO_Pin1

  6. #define LED_GPIO_PORT     GPIOB

  7. void delay_us(int delay_us);
  8. void delay_ms(int delay_ms);
  9. void LedBlink(GPIO_TypeDef* PORT,int PIN);

  10. int main(void)
  11. {
  12.     RCC_Enable(RCC,IOPortB);
  13.     while(1)
  14.     {
  15.         LedBlink(LED_GPIO_PORT,Red);
  16.     }
  17. }

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

  21. //微秒级的延时
  22. void delay_us(int delay_us)
  23. {
  24.     volatile unsigned int num;
  25.     volatile unsigned int t;

  26.     for (num = 0; num < delay_us; num++)
  27.     {
  28.         t = 11;
  29.         while (t != 0)
  30.         {
  31.             t--;
  32.         }
  33.     }
  34. }
  35. //毫秒级的延时
  36. void delay_ms(int delay_ms)
  37. {
  38.     volatile unsigned int num;
  39.     for (num = 0; num < delay_ms; num++)
  40.     {
  41.         delay_us(1000);
  42.     }
  43. }

  44. void LedBlink(GPIO_TypeDef* PORT,int PIN)
  45. {
  46.     GPIO_InitTypeDef GPIO_InitStructure;
  47.     GPIO_InitStructure.GPIO_Pin = PIN;
  48.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  49.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  50.     GPIO_Init(PORT, &GPIO_InitStructure);

  51.     GPIO_ResetBits(PORT,PIN);
  52.     delay_ms(100);
  53.     GPIO_SetBits(LED_GPIO_PORT,PIN);
  54.     delay_ms(100);
  55. }
复制代码

      红蓝交替:
main.c:
  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"

  3. #define Red GPIO_Pin5
  4. #define Green GPIO_Pin0
  5. #define Blue GPIO_Pin1

  6. #define LED_GPIO_PORT     GPIOB

  7. void delay_us(int delay_us);
  8. void delay_ms(int delay_ms);
  9. void LedBlink(GPIO_TypeDef* PORT,int PIN);

  10. int main(void)
  11. {
  12.     RCC_Enable(RCC,IOPortB);
  13.     while(1)
  14.     {
  15.         LedBlink(LED_GPIO_PORT,Red);
  16.         LedBlink(LED_GPIO_PORT,Blue);
  17.     }
  18. }

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

  22. //微秒级的延时
  23. void delay_us(int delay_us)
  24. {
  25.     volatile unsigned int num;
  26.     volatile unsigned int t;

  27.     for (num = 0; num < delay_us; num++)
  28.     {
  29.         t = 11;
  30.         while (t != 0)
  31.         {
  32.             t--;
  33.         }
  34.     }
  35. }
  36. //毫秒级的延时
  37. void delay_ms(int delay_ms)
  38. {
  39.     volatile unsigned int num;
  40.     for (num = 0; num < delay_ms; num++)
  41.     {
  42.         delay_us(1000);
  43.     }
  44. }

  45. void LedBlink(GPIO_TypeDef* PORT,int PIN)
  46. {
  47.     GPIO_InitTypeDef GPIO_InitStructure;
  48.     GPIO_InitStructure.GPIO_Pin = PIN;
  49.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  50.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  51.     GPIO_Init(PORT, &GPIO_InitStructure);

  52.     GPIO_ResetBits(PORT,PIN);
  53.     delay_ms(100);
  54.     GPIO_SetBits(LED_GPIO_PORT,PIN);
  55.     //delay_ms(100);
  56. }
复制代码













回复

使用道具 举报

发表于 2022-7-25 11:54:49 | 显示全部楼层
可以可以,很详细
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-12-24 06:58 , Processed in 0.041641 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表