stm32入门——跑马灯(基于stm32f103zet6)

发布者:huanli最新更新时间:2019-10-21 来源: eefocus关键字:stm32  跑马灯  stm32f103zet6 手机看文章 扫描二维码
随时随地手机看文章

最近开始学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寄存器

[1] [2]
关键字:stm32  跑马灯  stm32f103zet6 引用地址:stm32入门——跑马灯(基于stm32f103zet6)

上一篇:32位嵌入式系统的字节对齐
下一篇:STM32流水灯的几种实现方法

推荐阅读最新更新时间:2024-11-13 09:19

STM32学习记录——printf函数重定位
功能: 重定位printf函数,使printf作为串口打印输出函数。代替usart_send_string()函数 步骤: usart.c中包含USART初始化函数 1、USART初始化(使能时钟、使能GPIO、GPIO和USART初始化) 2、打开USART 3、在usart.c中加入如下代码 #ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker- Libraries- Small printf set to 'Yes') calls __io_putchar() */ #define PUT
[单片机]
STM32学习之串口的使用
串口的使用 1、为什么要用串口? 自上一篇写的时间是1月20号,今6月7号了,半年没更新了。 这半年发生了什么?过完年就去找公司实习,在那里自我感觉进步很大。其实在公司大多都是自学,师傅基本不会给你说什么。但这并不能说明你的师傅对你不好,带我的那个师傅只比我高一届,但他的水平比我高的好多届。他也是自学,也没人告诉他该怎么做,因为老板也不太懂。所以自学能力很重要,当然有人带你的话,这样会更好。 不说这些了,串口在调试的时候作用非常大。也学我们在学51的时候,只是将程序下载到开发板,看看是否能运行起来,通过数码管将结果显示出来,从而就知道程序设计的正确性。以前我也是这样做的,没什么不好。 在公司实习的时候,他们调试都是使用
[单片机]
<font color='red'>STM32</font>学习之串口的使用
STM32开发板学习日记-[5]TIM的PMW模式
脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。 在TIMx_CCMRx寄存器中的OCxM位写入 110 (PWM模式1)或 111 (PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存器OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位使能自动重装载的预装载寄存器(在向上计数或中心对称模式中)。 因为仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR寄存器中的UG位来初始化所有的寄存器。 OCx的极性可以通过软件在TIMx_C
[单片机]
stm32 spi 疑惑解疑 1
发送时 可以通过检测SPI_SR中的TXE位,当数据寄存器里有数据时,TXE位是0,当数据全部从数据寄存器的发送缓冲区传输到移位寄存器时TXE位被置1,这时候可以再往数据寄存器里写入数据。可以通过 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) 来检测。 SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE 是库函数可以检测SPI的一些状态位。 接收时 可以通过检测SPI_SR中的RXNE位,当数据寄存器里有数据时,RXNE位是0,当数据全部从数据寄存器的接收缓冲区传输到移位寄存器时RXNE位被置1,这时候可以从数
[单片机]
STM32串口通信校验问题
这里以串口作为传输媒介,介绍下怎样来发送接收一个完整的数据包。过程涉及到封包与解包。设计一个良好的包传输机制很有利于数据传输的稳定性以及正确性。串口只是一种传输媒介,这种包机制同时也可以用于SPI,I2C的总线下的数据传输。在单片机通信系统(多机通信以及PC与单片机通信)中,是很常见的问题。 一、根据帧头帧尾或者帧长检测一个数据帧 1、帧头+数据+校验+帧尾 这是一个典型的方案,但是对帧头与帧尾在设计的时候都要注意,也就是说帧头、帧尾不能在所传输的数据域中出现,一旦出现可能就被误判。如果用中断来接收的话,程序基本可以这么实现: unsigned char recstatu;//表示是否处于一个正在接收数据包的状态 unsign
[单片机]
<font color='red'>STM32</font>串口通信校验问题
一个关于STM32 FLASH编程应用相关的话题
“下面代码是stm32F1库函数中对flash写入一个字的函数部分,其中在给用u32表示的地址address赋值时,通过(_IO uint16_t*)将Address强制转换成了一个16位数的地址。很不理解为什么要转成16位的。实验改成32位后发现写入flash又不成功。很不理解,这是为什么?” FLASH_Status FLASH_ProgramWord(uint32_tAddress, uint32_t Data) { FLASH_Status status = FLASH_COMPLETE; __IOuint32_t tmp = 0; assert_param(IS_FLASH_ADDRESS(
[单片机]
STM32之DAC配置
STM32F103VCT6自带两个12位DAC,DAC的转换速度一直没有查到,网上有人说是1MHZ的频率,那就是1us了。ADC的转换时间在56MHZ工作频率下为1us,在72MHZ工作频率下为1.17us。如果AD和DA有对称关系的话,那么很可能跟ADC的时间相同。(仅作分析用!) DAC于我,有两个用途:输出波形和输出固定电压。先来说说前者的配置。 第一个参数:触发方式,DAC_InitStructure.DAC_Trigger。可选的外部触发源一共有八个。六个是定时器触发:TIM2,TIM4,TIM5,TIM6,TIM7和TIM8。剩下两个分别是:EXTI线路9和软件触发。如果采用定时器触发的话,就还要再编写
[单片机]
STM32的内部温度传感器
1、STM32的内部温度传感器 STM32内部温度传感器与ADC的通道16相连,与ADC配 合使用实现温度测量。测量范围–40~125℃,精度 ± 1.5℃ 操作流程: 1)、设置ADC相关参数 // ADC1 configuration ----------------------------- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_Init
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved