历史上的今天
返回首页

历史上的今天

今天是: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寄存器

推荐阅读

史海拾趣

北京人民电器厂公司的发展小趣事

为了加强技术研发和创新能力,北京人民电器建立了北京市级技术研究中心,并吸引了教授级高工、博士后、博士、硕士等多层次的专业技术人才。这些人才为公司的新产品研发、技术创新提供了强大的智力支持,使得北京人民电器在激烈的市场竞争中始终保持领先地位。

CDIL[Continental Device India Pvt. Ltd.]公司的发展小趣事

随着市场竞争的加剧和技术的不断进步,CDIL在15年前作出了重要决策——退出晶圆制造,转而专注于IC封装和电子制造服务。这一转型不仅使CDIL能够更好地适应市场需求,也为其带来了更广阔的发展空间。如今,CDIL的IC封装技术已达到国际先进水平,为全球众多知名电子企业提供优质服务。

HEICO Corporation公司的发展小趣事

在追求业务增长的同时,HEICO Corporation也积极履行社会责任,注重环保与可持续发展。公司致力于减少生产过程中的能源消耗和废弃物排放,采用环保材料和工艺进行生产。此外,HEICO还积极参与环保公益活动,推动电子行业的绿色发展。这种环保理念不仅提升了公司的社会形象,也为公司在电子行业中树立了良好的口碑。通过持续推动环保与可持续发展战略的实施,HEICO在电子行业中实现了经济效益与社会效益的双赢。

AAT [Advanced Analog Technology, Inc.]公司的发展小趣事

自1957年成立以来,HEICO Corporation不断扩展其电子技术领域的业务。公司电子技术部门专注于设计、制造电子、数据和微波以及光电产品,涵盖了从红外模拟和测试设备到激光测距接收器、电源转换产品等多个方面。这种多元化的产品组合不仅满足了航空、国防等行业的特定需求,也逐渐在医疗、电信等电子行业中占据了一席之地。通过持续的技术创新和研发投入,HEICO在电子市场上建立了稳固的地位。

HDP_Power公司的发展小趣事

随着业务的不断发展,HEICO Corporation开始在全球范围内布局其电子业务。公司建立了完善的供应链管理体系,与全球供应商建立了长期稳定的合作关系。这种全球化布局不仅有助于HEICO获取更优质的原材料和零部件,还降低了生产成本和风险。同时,HEICO还注重本地化生产和服务,以满足不同地区客户的需求。通过优化供应链管理和提升本地化服务能力,HEICO在电子行业中保持了强劲的增长势头。

鑫雁公司的发展小趣事

聚洵半导体始终坚持以市场为导向、以创新为驱动的发展理念。公司不断加大新产品研发投入,致力于在低功耗运放、高速运放、仪表放大器等领域取得更多技术突破。同时,聚洵还积极关注行业动态和市场趋势,针对医疗电子、工业控制、汽车电子等新兴领域推出了一系列创新产品。展望未来,聚洵将继续保持专注和创新精神,致力于成为国内领先的信号链模拟芯片设计公司之一,为电子行业的发展贡献更多力量。

问答坊 | AI 解惑

晶闸管在电力稳压器中的应用

摘要:在所研制的新型无触点电力稳压器中,采用晶闸管作为开关器件。文中介绍该系统基本工作原理,详细分析晶闸管在使用过程中存在的一些问题及解决方法。目前流行的电力稳压器大多采用伺服电机带动炭刷移动调整电压。它具有整机效率高、输出波形好 ...…

查看全部问答>

自学成才-电子工程师-找工作 General Electronics Engineer

我从小就有很强的好奇心和动手能力,想知道每一件东西是如何工作的,对自然科学非常着迷。我还记得,小学时因为在课堂上用钉子和电线做电磁铁被批评;初中因为画电路图做收音机被取笑;高中被禁止做一种能开所有教室门锁的万能钥匙。我父亲是一名中 ...…

查看全部问答>

tcpmp 的问题.

tcpmp 好不好用,下了一个, 在EVC下怎么编译不了?请高手介绍一下经验.…

查看全部问答>

闹钟的问题

用IAppointment设定一个闹钟,时间到了没有\"日程提醒\" 再去设定另一个闹钟,上一个已过时间的闹钟就马上蹦出\"日程提醒\"来,这是怎么回事? “日程提醒”界面好好象是poom画的,我想显示自己的闹钟界面该怎么做? 我的poom组件还需要其它设置 ...…

查看全部问答>

wince下用GDI加载bmp图片的方法(能提供下可用的代码吗?)跪求

wince下用GDI加载bmp图片的方法(能提供下可用的代码吗?)跪求…

查看全部问答>

arm9 开发板

想自己学一下嵌入式方面的东西,在网上看了一下arm9 的板子,但不知道哪一块比较合适 2410或是2440的,请大家推荐一块,价格合理,性能比较好,适合初学者的。支持linux和wince。…

查看全部问答>

wince 连接sql server解决方案

我现在使用c++在pda下需要访问sql server,使用RDA.现在有个问题就是需要实时的和sql server通讯,使用RDA速度总是有2秒左右的延时。 不知道是RDA固有的问题,还是sql server设置的问题? 在PDA上真的没有好的解决方案?不能直接连接sql server?不 ...…

查看全部问答>

飞思卡尔单片机MC9RS08KA8的C程序的例子

高手求助一下,哪位有MC9RS08KA8的C程序例子呢?急用!小女子新学,好歹不知道从何下手!谢谢各位的帮忙!…

查看全部问答>

讨论下mma7455的一些参数含义

最近在看mma7444的资料,看到里面有俩个中断脚,init1和init2,还看到里面讲到脉冲测试,水平测试。不知道脉冲测试和水平测试指的是什么东西?还有为啥有俩个中断引脚?这俩个引脚如何使用?还有大家怎么处理x,y,z轴的校准,以及如何处理得到的数据 ...…

查看全部问答>

很全面的传感器应用电路

文档一共讲述14章的内容,基本涉及了现有常用传感器的电路,分享给大家····…

查看全部问答>