【野火启明_瑞萨RA6M5】梦的开始 ---- 点灯(寄存器)


从本文开始,我将以瑞萨RA系列 野火_启明6M5开发板(R7FA6M5BH3CFC)为例,与大家一起分享学习的乐趣

在这里插入图片描述

一、IOPORT简介

芯片的引脚可以被粗略地分为 IO 引脚和非 IO 引脚。 非 IO 引脚就是电源引脚、晶振引脚等的那些引脚,他们不具备 GPIO(通用输入输出)功能。 而 IO 引脚是那些具备 GPIO 功能的引脚,他们可以配置为各种模式、实现各种通用功能。

IO 引脚最基本的输出功能是输出高、低电平,实现开关控制(比如开关LED灯、继电器或三极管等); 最基本的输入功能是检测外部输入电平(比如通过引脚电平的高低区分按键是否被按下)。 IO 引脚还可以用来连接外部设备,与外部设备进行通讯,发送控制指令,采集传感器数据等等。

IOPORT 即 I/O Port,在代码里面为了方便而写成 “IOPORT”,表示输入输出端口。 IOPORT 是 RA MCU 的一个外设模块,它用来控制芯片的引脚,对每一个引脚进行详细的配置。 具体地说,IOPORT 可以对引脚进行以下几个方面的配置:

  • 配置引脚为普通 IO 功能,即输入或输出高电平或者低电平。

  • 控制引脚的输入上拉电阻。

  • 控制引脚的驱动能力。

  • 控制引脚是否检测上升沿/下降沿/双边沿。

  • 控制引脚是否作为中断输入引脚。

  • 配置引脚为模拟输入功能或者将引脚在内部连接到其他外设模块。

瑞萨 RA 系列芯片的 IO 端口从理论上被分成16个组(0~9, A, B, C, D, E, F),每组有16个引脚(0~15)。 然而,实际情况并非是完整的“16个组+每组16个引脚”的配置,而是因实际的芯片型号而异。 以野火启明6M5板子上的 R7FA6M5BH3CFC 芯片为例,它是 LQFP 176-pin 封装,一共有 176 个引脚, 其中的大部分引脚都是可以被 IOPORT 模块控制的 IO 引脚。 而这些 IO 引脚被分成了 0~9, A, B 共11组引脚(注:A, B 是 10, 11 的十六进制表示), 每组一般有 16 个引脚(引脚号为0~15),实际上有些组不到16个引脚。 对于引脚数比较少的封装(LQFP 144-pin 和 LQFP 100-pin 封装)来说,芯片的可用 IO 引脚数也会相对减少。

二、IOPORT的框图分析

RA6M5、RA4M2、RA2L1 这三者的 IOPORT 模块框图结构是基本一致的。

以 RA6M5 为例,下图是 RA6M5 的外设 IOPORT 的功能结构框图,标有字母 A 处表示的是芯片实际引出的 IO 引脚。
在这里插入图片描述
接下来我们对 IOPORT 外设的结构框图进行分析,将不难得出 IOPORT 有如下几种工作模式:

  • 通用输入输出(GPIO)模式
    输入模式(浮空/上拉)
    输出模式(推挽/开漏)

  • 模拟输入功能模式

  • 复用功能模式

1. IO端口方向

见图中标注 ① 处。

PDR (Port Direction Register) 是端口方向寄存器,它控制端口的 GPIO 方向。 当 IO 引脚需要控制输出高电平或者低电平时,可设置引脚的 GPIO 方向为 GPIO 输出; 而当需要读取 IO 引脚的电平时,可设置引脚的 GPIO 方向为 GPIO 输入。

2. IO输入上拉控制

见图中标注 ② 处。

PCR (Pull-up Control Register) 是上拉控制寄存器,它控制 IO 引脚的 GPIO 输入是否使能上拉。 当设置为允许上拉时,实际上会使得图中字母 B 处的弱上拉电阻连接到VCC电源正极,从而使得引脚处于弱上拉输入模式。 需要注意的是,当引脚的 GPIO 方向被配置为“输入”时,才可以设置使用弱上拉电阻。 从上图还可以看出,RA6M5 的 IO 端口是没有下拉电阻的。

3. IO驱动能力和开漏输出控制

见图中标注 ③ 处。

DSCR (Port Drive Capability Register) 是端口驱动能力寄存器,它控制 IO 引脚的驱动能力。 驱动能力指的是IO驱动的电流强度和IO的最大翻转速率。

NCODR (N-Channel Open-Drain Control Register) 是开漏输出控制寄存器,它控制 IO 引脚是否使能开漏模式。 当引脚的 GPIO 方向被配置为“输出”时,可以配置 IO 引脚输出的模式是推挽输出还是使能开漏输出。

4. IO端口输出数据

见图中标注 ④ 处。

这部分看似比较复杂,但是实际上 EOSR、POSR、PORR、EORR 的箭头最终都指向 PODR, 这意味着操作 EOSR、POSR、PORR、EORR 这些寄存器,实际上将最终操作的是 PODR 寄存器。

  • 图中 PODR (Port Output Data Register) 是端口输出数据寄存器,它控制 GPIO 引脚输出的电平。 当引脚的 GPIO 方向被配置为“输出”时,可以配置引脚输出高电平或者低电平。

  • 图中 EOSR (Event Output Set Register) 是事件输出置位寄存器(该寄存器我们暂且忽略它)。

  • 图中 POSR (Pmn Output Set Register) 是端口输出数据寄存器,它控制 GPIO 引脚输出为高电平,但却不能控制输出低电平。

  • 图中 PORR (Pmn Output Reset Register) 是端口输出数据寄存器,它控制 GPIO 引脚输出为低电平,但却不能控制输出高电平。

  • 图中 EORR (Event Output Reset Register) 是事件输出复位寄存器(该寄存器我们暂且忽略它)。

5. IO端口输入数据

见图中标注 ⑤ 处。

PIDR (Port Input Data Register) 是端口输入数据寄存器,可以通过它读取 GPIO 引脚的电平状态。 当引脚的 GPIO 方向被配置为“输入”时,程序可以读出输入引脚的电平是高电平还是低电平。

6. 模拟输入模式

见图中标注 ⑥ 处。

ASEL (Analog Input Enable) 是模拟输入选择控制位,可以通过它来将引脚配置为模拟输入模式。 当使用 ADC 功能时,需将引脚配置为模拟输入模式。

7. 端口模式控制和外设复用选择

见图中标注 ⑦ 处。

PMR (Port Mode Control) 是端口模式控制位,可以通过它来将引脚配置为 GPIO 输入/输出模式,或者配置作为复用外设功能引脚。

PSEL (Peripheral Select) 是外设复用选择,可以通过它来选择将引脚连接到某一个外设功能上。

8. IO边沿检测与中断

见图中标注 ⑧ 处。

EOFR (Event on Falling/Event on Rising) 是事件触发选择,可以通过它来让引脚检测边沿信号,如果检测到指定信号将触发一个事件。

ISEL (IRQ Input Enable) 是IRQ输入使能控制位,可以配置是否产生中断。

三、IOPORT的寄存器描述

IOPORT 模块由于功能相对简单,寄存器相对来说比较少,下面我们就来看看它的这几个寄存器。

1. 端口引脚功能选择寄存器

我们在前面说过,瑞萨 RA 系列芯片的 IO 端口从理论上被分成16个组、每组有16个引脚(实际上可能会更少), 而下图中所描述的端口引脚功能选择寄存器(PmnPFS)便是用来配置 IO 引脚的,一个这样的寄存器对应配置一个 IO 引脚。
在这里插入图片描述
注:上图中的 “Pmn”,“m = 0 to 9, A, B”表示的是 IO 端口 m 的范围,而“n = 00 to 15”表示的是 IO 引脚 n 的范围。

PmnPFS 寄存器的位域说明如下:
在这里插入图片描述

2. 端口输出数据寄存器

在这里插入图片描述
上图所示为端口输出数据寄存器的相关描述。该寄存器的位域说明如下:
在这里插入图片描述

3. 端口输入数据寄存器

在这里插入图片描述
上图所示为端口输入数据寄存器的相关描述。该寄存器的位域说明如下:
在这里插入图片描述

4. 端口输出置位/复位寄存器

在这里插入图片描述
上图所示为端口输出置位/复位寄存器的相关描述。该寄存器的位域说明如下:
在这里插入图片描述

5. 写保护寄存器

当我们要配置RA单片机的 I/O 引脚时,我们主要需要配置的是 PmnPFS 寄存器, 但是所有的 PmnPFS 寄存器默认是被保护的,因此要想写入引脚 Pmn 对应的 PmnPFS 寄存器,需要先通过写保护寄存器解除写保护。
在这里插入图片描述
上图所示为写保护寄存器的相关描述。该寄存器的位域说明如下:在这里插入图片描述

四、点亮LED灯(寄存器)

1. 硬件设计
野火启明6M5开发板的 LED 电路图如图所示。图中 RA6M5 芯片的 P400、P403、P404 引脚分别通过一个 2.2 KΩ 的限流电阻连接到 LED1、LED2、LED3 这三个用户 LED 灯的阴极,LED 灯的阳极连接到 3.3V 电源。 而 LED4 是电源指示灯,只要开发板通电就会亮。
在这里插入图片描述
2. 软件设计
拷贝一份之前新建的 Keil 工程模板 “06_Template”, 然后将工程文件夹重命名为 “08_Register_LED”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

(1)寄存器定义头文件
当新建工程完成之后,工程里已经自动包含了这个定义寄存器的头文件,比如:R7FA6M5BH.h 头文件。 在这个头文件里面,已经包含了芯片所有的寄存器定义,包括 IOPORT 外设的寄存器。

以启明6M5开发板的 RA6M5 工程为例, 我们在这里列出 IOPORT 部分寄存器定义(它们存在于寄存器定义头文件 R7FA6M5BH.h 中)。

R7FA6M5BH.h 文件中的 IOPORT 部分寄存器定义

/**
  * @brief I/O Ports (R_PORT0)
  */

typedef struct {
    
                                    /*!< (@ 0x40080000) R_PORT0 Structure                                          */

   union {
    
    
      union {
    
    
         __IOM uint32_t PCNTR1;                    /*!< (@ 0x00000000) Port Control Register 1                                    */

         struct {
    
    
            __IOM uint32_t PDR      : 16;           /*!< [15..0] Pmn Direction                                                     */
            __IOM uint32_t PODR     : 16;           /*!< [31..16] Pmn Output Data                                                  */
         } PCNTR1_b;
      } ;

      struct {
    
    
         union {
    
    
            __IOM uint16_t PODR;                    /*!< (@ 0x00000000) Output data register                                       */

            /* ... 代码过长省略 ... */
         } ;

         union {
    
    
            __IOM uint16_t PDR;                     /*!< (@ 0x00000002) Data direction register                                    */

            /* ... 代码过长省略 ... */
         } ;
      };
   };

   union {
    
    
      union {
    
    
         __IM  uint32_t PCNTR2;                    /*!< (@ 0x00000004) Port Control Register 2                                    */

         struct {
    
    
            __IM  uint32_t PIDR     : 16;           /*!< [15..0] Pmn Input Data                                                    */
            __IM  uint32_t EIDR     : 16;           /*!< [31..16] Pmn Event Input Data                                             */
         } PCNTR2_b;
      } ;

      struct {
    
    
         union {
    
    
            __IM  uint16_t EIDR;                    /*!< (@ 0x00000004) Event input data register                                  */

            /* ... 代码过长省略 ... */
         } ;

         union {
    
    
            __IM  uint16_t PIDR;                    /*!< (@ 0x00000006) Input data register                                        */

            /* ... 代码过长省略 ... */
         } ;
      };
   };

   union {
    
    
      union {
    
    
         __OM  uint32_t PCNTR3;                    /*!< (@ 0x00000008) Port Control Register 3                                    */

         struct {
    
    
            __OM  uint32_t POSR     : 16;           /*!< [15..0] Pmn Output Set                                                    */
            __OM  uint32_t PORR     : 16;           /*!< [31..16] Pmn Output Reset                                                 */
         } PCNTR3_b;
      } ;

      struct {
    
    
         union {
    
    
            __OM  uint16_t PORR;                    /*!< (@ 0x00000008) Output set register                                        */

            /* ... 代码过长省略 ... */
         } ;

         union {
    
    
            __OM  uint16_t POSR;                    /*!< (@ 0x0000000A) Output reset register                                      */

            /* ... 代码过长省略 ... */
         } ;
      };
   };

   union {
    
    
      union {
    
    
         __IOM uint32_t PCNTR4;                    /*!< (@ 0x0000000C) Port Control Register 4                                    */

         struct {
    
    
            __IOM uint32_t EOSR     : 16;           /*!< [15..0] Pmn Event Output Set                                              */
            __IOM uint32_t EORR     : 16;           /*!< [31..16] Pmn Event Output Reset                                           */
         } PCNTR4_b;
      } ;

      struct {
    
    
         union {
    
    
            __IOM uint16_t EORR;                    /*!< (@ 0x0000000C) Event output set register                                  */

            /* ... 代码过长省略 ... */
         } ;

         union {
    
    
            __IOM uint16_t EOSR;                    /*!< (@ 0x0000000E) Event output reset register                                */

            /* ... 代码过长省略 ... */
         } ;
      };
   };
} R_PORT0_Type;                                 /*!< Size = 16 (0x10)                                                          */



/**
  * @brief I/O Ports-PFS (R_PFS)
  */

typedef struct {
    
                                    /*!< (@ 0x40080800) R_PFS Structure                                            */
   __IOM R_PFS_PORT_Type PORT[15];               /*!< (@ 0x00000000) Port [0..14]                                               */
} R_PFS_Type;                                   /*!< Size = 960 (0x3c0)                                                        */



/**
  * @brief I/O Ports-MISC (R_PMISC)
  */

typedef struct {
    
                                    /*!< (@ 0x40080D00) R_PMISC Structure                                          */

   union {
    
    
      __IOM uint8_t PFENET;                       /*!< (@ 0x00000000) Ethernet Control Register                                  */

      /* ... 代码过长省略 ... */
   } ;
   __IM  uint8_t   RESERVED[2];

   union {
    
    
      __IOM uint8_t PWPR;                         /*!< (@ 0x00000003) Write-Protect Register                                     */

      /* ... 代码过长省略 ... */
   } ;
   __IM  uint8_t   RESERVED1;

   union {
    
    
      __IOM uint8_t PWPRS;                        /*!< (@ 0x00000005) Write-Protect Register for Secure                          */

      /* ... 代码过长省略 ... */
   } ;
   __IM  uint16_t  RESERVED2[5];
   __IOM R_PMISC_PMSAR_Type PMSAR[12];           /*!< (@ 0x00000010) Port Security Attribution Register                         */
} R_PMISC_Type;                                 /*!< Size = 40 (0x28)                                                          */




/** @addtogroup Device_Peripheral_peripheralAddr
  * @{          外设首地址
  */

#define R_PORT0_BASE                0x40080000UL
#define R_PORT1_BASE                0x40080020UL
#define R_PORT2_BASE                0x40080040UL
#define R_PORT3_BASE                0x40080060UL
#define R_PORT4_BASE                0x40080080UL
#define R_PORT5_BASE                0x400800A0UL
#define R_PORT6_BASE                0x400800C0UL
#define R_PORT7_BASE                0x400800E0UL
#define R_PORT8_BASE                0x40080100UL
#define R_PORT9_BASE                0x40080120UL
#define R_PORT10_BASE               0x40080140UL
#define R_PORT11_BASE               0x40080160UL
#define R_PORT12_BASE               0x40080180UL
#define R_PORT13_BASE               0x400801A0UL
#define R_PORT14_BASE               0x400801C0UL
#define R_PFS_BASE                  0x40080800UL
#define R_PMISC_BASE                0x40080D00UL



/** @addtogroup Device_Peripheral_declaration
  * @{          外设寄存器声明(定义结构体指针,指向 IOPORT 寄存器首地址)
  */

#define R_PORT0                     ((R_PORT0_Type*)           R_PORT0_BASE)
#define R_PORT1                     ((R_PORT0_Type*)           R_PORT1_BASE)
#define R_PORT2                     ((R_PORT0_Type*)           R_PORT2_BASE)
#define R_PORT3                     ((R_PORT0_Type*)           R_PORT3_BASE)
#define R_PORT4                     ((R_PORT0_Type*)           R_PORT4_BASE)
#define R_PORT5                     ((R_PORT0_Type*)           R_PORT5_BASE)
#define R_PORT6                     ((R_PORT0_Type*)           R_PORT6_BASE)
#define R_PORT7                     ((R_PORT0_Type*)           R_PORT7_BASE)
#define R_PORT8                     ((R_PORT0_Type*)           R_PORT8_BASE)
#define R_PORT9                     ((R_PORT0_Type*)           R_PORT9_BASE)
#define R_PORT10                    ((R_PORT0_Type*)           R_PORT10_BASE)
#define R_PORT11                    ((R_PORT0_Type*)           R_PORT11_BASE)
#define R_PORT12                    ((R_PORT0_Type*)           R_PORT12_BASE)
#define R_PORT13                    ((R_PORT0_Type*)           R_PORT13_BASE)
#define R_PORT14                    ((R_PORT0_Type*)           R_PORT14_BASE)
#define R_PFS                       ((R_PFS_Type*)             R_PFS_BASE)
#define R_PMISC                     ((R_PMISC_Type*)           R_PMISC_BASE)

3. hal_entry入口函数
一般来说,接下来我们应该在 main 函数里编写我们的程序, 但是使用 FSP 库却不一样,在没有使用 RTOS 的情况下,它规定以名为 hal_entry 的函数作为用户应用程序的入口, 因此我们应该在 hal_entry 入口函数下编写我们的代码。

实际上,当使用 RTOS 时,程序是从 main 函数开始进行线程调度; 当没有使用 RTOS 时,C语言程序的入口函数 main 函数调用了 hal_entry 函数。 我们新建的工程是没有选用 RTOS 的,因此,用户程序是从 hal_entry 函数开始执行。 我们打开 “\src\hal_entry.c” 文件,在 hal_entry 函数里面编写我们的代码。

以启明6M5开发板为例,RA6M5 工程的 hal_entry 函数代码如下所示。

hal_entry.c文件

 void hal_entry(void)
 {
    
    
     /* TODO: add your own code here */

     /* 取消写保护 */
     R_PMISC->PWPR = 0;                               ///< Clear BOWI bit - writing to PFSWE bit enabled
     R_PMISC->PWPR = 1U << BSP_IO_PWPR_PFSWE_OFFSET;  ///< Set PFSWE bit - writing to PFS register enabled

     /* LED1:配置引脚 P400 对应的PFS寄存器 */
     R_PFS->PORT[BSP_IO_PORT_04_PIN_00>>8].PIN[BSP_IO_PORT_04_PIN_00 & 0xFF].PmnPFS =
        IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_LOW;
     /* LED2:配置引脚 P403 对应的PFS寄存器 */
     R_PFS->PORT[BSP_IO_PORT_04_PIN_03>>8].PIN[BSP_IO_PORT_04_PIN_03 & 0xFF].PmnPFS =
        IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_LOW;
     /* LED3:配置引脚 P404 对应的PFS寄存器 */
     R_PFS->PORT[BSP_IO_PORT_04_PIN_04>>8].PIN[BSP_IO_PORT_04_PIN_04 & 0xFF].PmnPFS =
        IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_LOW;

     /** 此时3个LED灯的引脚默认输出的是低电平
      *  所以3个LED灯都会默认亮起来
      *  我们在 while 循环里让 LED1 闪烁:每秒钟翻转一次状态
      */

     while(1)
     {
    
    
        /* 翻转LED灯:LED1 */
        //R_PORT4->PODR |= 1<<(BSP_IO_PORT_04_PIN_00 & 0xFF);
        //R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
        //R_PORT4->PODR &= (uint16_t)~(1 << (BSP_IO_PORT_04_PIN_00 & 0xFF));
        //R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);

        /* 或者也可以这样用位异或操作来翻转LED1 */
        R_PORT4->PODR ^= 1<<(BSP_IO_PORT_04_PIN_00 & 0xFF);
        R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
     }

     //这后面的代码无需理会
 #if BSP_TZ_SECURE_BUILD
     /* Enter non-secure code */
     R_BSP_NonSecureEnter();
 #endif
 }

4. 下载验证
编写好上述代码,然后将程序编译并下载到开发板之后,按下复位按键来复位开发板, 可以观察到开发板上面除了电源指示灯之外的3个 LED 灯当中有两个灯常亮,还有一个灯在缓慢闪烁。 闪烁着的 LED 灯为 LED1,它每秒钟(1000毫秒)便改变一次亮灭的状态。

猜你喜欢

转载自blog.csdn.net/Dustinthewine/article/details/130689982