历史上的今天
返回首页

历史上的今天

今天是:2024年10月21日(星期一)

正在发生

2019年10月21日 | stm32入门——跑马灯(基于stm32f103zet6)

2019-10-21 来源:eefocus

最近开始学stm32,着实感觉到了stm32和51之间的区别,但也有联系,总我感觉32与51之间最大的区别就是在使用某个外设之前,要对该外设进行时钟的使能(以达到降低功耗的目的),和相关配置。


刚学完跑马灯,下面对跑马灯用到的对IO口的配置相关知识分别对应官方库函数和寄存器进行总结。


如有错误或不足,请在下方留言。


文章内容基于正点原子战舰。


IO口的状态

       IO口有八大模式:─  输入浮空(  GPIO_Mode_IN_FLOATING = 0x04,)

                         ─  输入上拉(  GPIO_Mode_IPU = 0x48,)

                         ─  输入下拉(  GPIO_Mode_IPD = 0x28,)

                         ─  模拟输入(GPIO_Mode_AIN = 0x0,)

                         ─  开漏输出(  GPIO_Mode_Out_OD = 0x14,)

                         ─  推挽式输出(  GPIO_Mode_Out_PP = 0x10,)

                         ─  推挽式复用功能(GPIO_Mode_AF_PP = 0x18)

                         ─  开漏复用功能(  GPIO_Mode_AF_OD = 0x1C,) 


      IO口有三种速   -2MHZ( GPIO_Speed_2MHz=1,)


                       -10MHz( GPIO_Speed_10MHz = 1,)


                       -50MHz(  GPIO_Speed_50MHz=3,)   //当看到这些配置相应的值是否会感到疑惑呢,稍后讲解。 


跑马灯的原理图

显然led的硬件连接很简单分别连接了IO口PE5和PB5,另一端串联一个电阻共同接地。


实验的代码分析

我们知道任何外设的驱动都要使能相应的时钟,首先看stm32系统的时钟框图

经查阅资料可知,GPIO的时钟在APB2的外设时钟使能寄存器上,相关函数的定义在stm32f10x_rcc.h中 void   RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)其源代码为:


void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

{

  /* Check the parameters */ //检查值的有效性

  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));

  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)

  {

    RCC->APB2ENR |= RCC_APB2Periph; //配置APB2ENBR寄存器

  }

  else

  {

    RCC->APB2ENR &= ~RCC_APB2Periph; //配置APB2ENR寄存器

  }

}

 

//与该函数相关的一些宏定义  检查RCC_APB2Periph参数的有效性

 

#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)

#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)

#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)

#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)

#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)

#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)

#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)

#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)

#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)

#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)

#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)

#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)

#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)

#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)

#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)

#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)

#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)

#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)

#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)

#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)

#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

 

#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))

  

//与该函数相关的枚举变量定义  检查NewState参数的有效性

 

typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;

#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))

  我们来看这个使能GPIO时钟函数的源代码,函数没有返回值,接受两个参数 unint32_t(unsigned int)类型的RCC_APB2Periph和FunctionalState(枚举变量)类型的NewState 。


  函数首先检查传入值的有效性,我们可以看到和RCC_APB2Periph相关的宏定义中,规定了相关参数的取值范围,相关的值实际上是APB2  外设时钟使能寄存器(RCC_APB2ENR)相关位的配置,在这里我们也可以看出库函数实际上就是操作寄存器,对操作寄存器进行了一系列的封装。我们这里从硬件来看需要启动GPIOB和GPIOE的时钟使能,则RCC_APB2Periph分别为RCC_APB2Periph_GPIOB,RCC_APB2Periph_GPIOE。再看参数NewState  有相关定义可知{DISABLE = 0, ENABLE = !DISABLE}则当NewState为ENABLE时,开启使能,GPIO相关使能完毕。(实际上库函数就是对寄存器RCC_APB2ENR的相关操作,理解该函数便可写出相关的寄存器版本)


与51单片机不同的是每次使用IO口还要对IO口进行初始化,配置IO的模式(MODE),速度(SPEED)及针脚(PIN),


GPIO初始化函数   void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct),其源代码:


/**

  * @brief  Initializes the GPIOx peripheral according to the specified

  *         parameters in the GPIO_InitStruct.

          //  根据指定初始化GPIOx外设GPIO_InitStruct中的参数。

  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.

                 //    其中x可以是(A..G)来选择GPIO外设

  * @param  GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that

  *         contains the configuration information for the specified GPIO peripheral.

           // GPIO_InitStruct:指向GPIO_InitTypeDef结构的指针包含指定GPIO外设的配置信息。

  * @retval None

  */

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)//GPIO初始化函数

{

  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;

  uint32_t tmpreg = 0x00, pinmask = 0x00; 

    //设置相关变量 currentmode存储CRL CRH配置信息 tmpreg 存储当前及最终CRL CRH配置信息

  /* Check the parameters */

  assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); //检查GPIO的有效性

  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); //检查GPIO_Mode的有效性

  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  //检查GPIO_Pin的有效性

  

/*---------------------------- GPIO Mode Configuration GPIO模式配置 -----------------------*/

  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); 

    //取GPIO_Mode中的低四位,这里的做法和GPIO_Mode的值有关,可自行参考结构体中的值进验证

  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)

    //该判断的意思是如果模式GPIO_Mode的第五位不是零就执行该语句,由结构体中的模式的值可得如果    

    //第五位为1,则该模式为输出模式

  { 

    /* Check the parameters *///检查速度的GPIO_Speed的有效性

    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));

    /* Output mode */

    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;

    //如果为输出模式则用模式GPIO_Mode的低四位|GPIO_Speed,便可得到输出模式寄存器中相关配置的 

    //值,可自行验证

  }

/*---------------------------- GPIO CRL Configuration GPIO CRL配置------------------------*/

  /* Configure the eight low port pins */ //设置低八位 CRL寄存器

  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)

        //由GPIO_Pin的范围可知,该语句的意思是判断0-7脚是否有定义

  {

    tmpreg = GPIOx->CRL;  //获取当前CRL配置

    for (pinpos = 0x00; pinpos < 0x08; pinpos++)  //循环检查引脚 ,判断引脚位置

    {

      pos = ((uint32_t)0x01) << pinpos;  //循环一次pos便左移一次

      /* Get the port pins position */ //判断引脚位置 

      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; 

        // 通过pos&GPIO_Pin(传入参数的引脚)对比判断

      if (currentpin == pos) //如果相等则当前循环值刚好是引脚位置

      {

        pos = pinpos << 2;  //pos左移二位(扩大四倍),目的是配置CRL寄存器

        /*我们知道IO口配置寄存器CRL和CRH都是32位寄存器,把pos*4即可得到对应引脚在相关配置寄 

        存器中对应的位置*/

        /* 清除相应的低控制寄存器位*/

        /* Clear the corresponding low control register bits */

        pinmask = ((uint32_t)0x0F) << pos;  //pinmask设置CRL对应的位上的pinmask为1

        tmpreg &= ~pinmask; //将CRL对应位清零

        /* Write the mode configuration in the corresponding bits */

        /*将模式配置写入相应的位*/

        tmpreg |= (currentmode << pos); //对应位写入

        /* Reset the corresponding ODR bit */

        /*重置相应的ODR位*/

        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) //如果是下拉输入

        {

          GPIOx->BRR = (((uint32_t)0x01) << pinpos);  //相应位置零  BRR寄存器间接控制了 

                                                       //ODR寄存器

        }

        else    //否则

        {

          /* Set the corresponding ODR bit */

          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)  如果是上拉输入

          {

            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);  //相应位置一,BSRR寄存器间接控 

                                                         //制了ODR寄存器

推荐阅读

史海拾趣

Directed Energy Inc公司的发展小趣事

在电子行业中,产品质量是企业生存和发展的关键。Directed Energy Inc深知这一点,因此始终将品质保障作为公司的核心竞争力之一。公司建立了严格的质量管理体系,从原材料采购到生产制造、再到产品检验和售后服务,每一个环节都严格把控,确保产品的高品质。这种对品质的执着追求赢得了客户的广泛信赖和好评,为公司赢得了更多的市场份额。

Cramer公司的发展小趣事

随着5G技术的普及,Cramer公司紧跟时代潮流,开始研发5G通信设备。他们与多家通信巨头合作,共同推进5G技术的发展。经过不懈的努力,Cramer公司成功研发出了一款高性能的5G路由器。这款产品的推出,使得Cramer公司在5G市场上取得了先机,并为其带来了丰厚的回报。

Ememory Technology Inc公司的发展小趣事

为了满足市场对高性能、低功耗芯片的需求,eMemory在2016年宣布向150nm高电压制程工艺扩展其可编程Neobit技术。这一技术的成功开发,使得eMemory能够在LCD驱动IC、逻辑IC和功率控制IC等高速增长的市场中占据一席之地。Neobit技术的可擦编程只读存储器特性,为芯片设计提供了更大的灵活性和可定制性。

GHI Electronics公司的发展小趣事

GHI Electronics深知技术创新是企业持续发展的关键。因此,公司不断加大研发投入,致力于开发具有自主知识产权的产品。其中,ARM微控制器、显示模块、空气质量传感器、模块化系统以及微处理器等主打产品系列,凭借其卓越的性能和稳定的品质,在消费电子、便携设备、通讯/网络等领域获得了广泛应用。这些技术创新不仅巩固了GHI Electronics在市场上的领先地位,也为其未来的发展奠定了坚实的基础。

Dialog公司的发展小趣事

近年来,Dialog公司通过一系列收购活动实现了快速扩张。例如,Dialog公司收购了Adesto公司,进一步拓展了工业物联网市场。这次收购为Dialog公司带来了一系列新的智能楼宇自动化工业解决方案,为其现有的制造自动化产品提供了非常好的补充。此外,收购Adesto还为Dialog公司带来了近5,000家客户公司,这些公司中大部分对Dialog公司来说是新客户。

Herga公司的发展小趣事

进入21世纪后,Herga公司意识到全球化市场的重要性,开始实施积极的国际化战略。公司首先在欧洲市场建立了稳固的根据地,随后通过并购和合资等方式,逐步进入北美、亚洲等新兴市场。在这个过程中,Herga公司不仅提供了符合当地市场需求的产品和服务,还积极融入当地文化,建立了良好的品牌形象。这些努力使得Herga公司的全球市场份额逐年上升,成为国际电子行业中不可忽视的力量。

问答坊 | AI 解惑

基于无线传感器的微控制器SPI总线设计

概述:基于IEEE802.15.4无线微控制器,为在2.4G频带上应用IEEE802.15.4标准及zigbee网络的开发应用提供了完整的解决方案; 其使用是JN5121芯片,文中主要介绍了JN5121 SPI总线特点、配置方式及相关函数等等;…

查看全部问答>

在Linux操作系统中,如何通过蓝牙连接到个人局域网和拨号上网的?

如题,对蓝牙的上网功能了解很少,希望高手指点! 谢谢…

查看全部问答>

开发板上怎么挂载标准键盘啊?

请教各位 开发板上怎么挂载标准PC键盘(usb的)啊? …

查看全部问答>

求助电机驱动芯片

我要帮忙做一个项目,用430F1611做主控,驱动的电机直流24V,额定电流13.4A,请问各位谁有好的建议用那款驱动芯片?小弟我才疏学浅,只知道用器件搭H桥了。(这个项目对与控制时效性要求不高)…

查看全部问答>

人需要的是什么???

          我们现在讲一个人成功与否最注重的一点是否他有地位,有金钱,很现实的一个问题。今天看了一档节目《我们约会吧》,挺佩服里面的一个男嘉宾,他的话其实代表了很大一批男性朋友的想法。八 ...…

查看全部问答>

【设计工具】basys2培训代码

里面的例程具体一个例程的内容…

查看全部问答>

从官网买的M4终于发货了。。。

从官网买的M4终于发货了。。。…

查看全部问答>

有关POS机样片的利用问题

相信群里的朋友和我一样拿到POS机样片也有一段时间了,开始以为是一套样板什么的,后来发现竟然是一堆样片,这些样片要用起来挺难的。光BGA能焊接的都没几个。群里那位朋友如果有能力的是否可以带领大伙一起设计板子,把POS机的样片用起来,这个都 ...…

查看全部问答>