如何用C语言操作寄存器——瑞萨RA系列FSP库开发实战指南(10)

2025-04-22

3.4

如何用C语言操作寄存器

3.4.1

C语言对寄存器的封装

前面的所有关于存储器映射的内容,最终都是为大家更好地理解如何用C语言控制读写外设寄存器做准备,因此此处是本章的重点内容。

3.4.1.1

外设模块基地址定义

在编程上为了方便理解和记忆,我们要把外设模块基地址以相应的宏定义起来,外设基地址都以它们的名字作为宏名的组成部分。以下是IO端口外设基地址的宏定义。

列表1:代码清单3‑1 IOPORT外设基地址宏定义

左右滑动查看完整内容

/* 外设基地址 */
#defineR_PORT0_BASE 0x40080000
#defineR_PORT1_BASE 0x40080020
#defineR_PORT2_BASE 0x40080040
#defineR_PORT3_BASE 0x40080060
#defineR_PORT4_BASE 0x40080080
#defineR_PORT5_BASE 0x400800A0
#defineR_PORT6_BASE 0x400800C0
#defineR_PORT7_BASE 0x400800E0
#defineR_PORT8_BASE 0x40080100
#defineR_PORT9_BASE 0x40080120
#defineR_PORT10_BASE 0x40080140
#defineR_PORT11_BASE 0x40080160
#defineR_PFS_BASE 0x40080800
#defineR_PMISC_BASE 0x40080D00

3.4.1.2

寄存器结构体定义

由于寄存器的数量是非常之多的,如果每个寄存器都用像*((uint32_t*)(0x40080000+0x0020*1))这样的方式去访问的话,会显得很繁琐、很麻烦。为了更方便地访问寄存器,我们会借助C语言结构体的特性去定义寄存器和寄存器位域,这是通用的做法。

列表2:代码清单3‑2使用结构体封装外设寄存器

左右滑动查看完整内容

// 注:关于输入输出端口的声明
/* C 语言: IO definitions (access restrictions to peripheral registers) */
//#define __I volatile const /*!< Defines 'read only'␣
,
→permissions */
//#define __O volatile /*!< Defines 'write only'␣
,
→permissions */
//#define __IO volatile /*!< Defines 'read / write'␣
,
→permissions */


/* 下面的宏定义用于结构体成员 */
/* following defines should be used for structure members */
//#define __IM volatile const /*! Defines 'read only'␣
,
→structure member permissions */
//#define __OM volatile /*! Defines 'write only'␣
,
→structure member permissions */
//#define __IOM volatile /*! Defines 'read / write'␣
,
→structure member permissions */


//typedef unsigned char uint8_t;
//typedef unsigned short int uint16_t; /* 无符号 16 位整型变量 */
//typedef unsigned int uint32_t; /* 无符号 32 位整型变量 */


/**
* @brief I/O Ports (R_PORT0)
*/
typedefstruct /*!< (@ 0x40040000) R_PORT0␣
,
→Structure */
{
union
{
union
{
__IOM uint32_t PCNTR1; /*!< (@ 0x00000000) Port Control␣
,
→Register 1 */


struct
{
__IOM uint32_t PDR : 16; /*!< [15..0] Pmn Direction(引脚
Pmn 方向)*/
__IOM uint32_t PODR : 16; /*!< [31..16] Pmn Output Data(引脚
Pmn 输出数据)*/
} PCNTR1_b;
};
/* ... 代码过长省略 ... */
};


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


struct
{
__IM uint32_t PIDR : 16; /*!< [15..0] Pmn Input Data(引脚
Pmn 输入数据)*/
__IM uint32_t EIDR : 16; /*!< [31..16] Pmn Event Input Data
(引脚 Pmn 事件输入数据)*/
} PCNTR2_b;
};


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


union
{


union
{
__OM uint32_t PCNTR3; /*!< (@ 0x00000008) Port Control␣
,
→Register 3 */
struct
{
__OM uint32_t POSR : 16; /*!< [15..0] Pmn Output Set(引脚
Pmn 输出置位)*/
__OM uint32_t PORR : 16; /*!< [31..16] Pmn Output Reset(引脚
Pmn 输出复位)*/
} PCNTR3_b;
};


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


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


struct
{
__IOM uint32_t EOSR : 16; /*!< [15..0] Pmn Event Output Set
(引脚 Pmn 事件输出置位)*/
__IOM uint32_t EORR : 16; /*!< [31..16] Pmn Event Output␣
→Reset(引脚 Pmn 事件输出复位)*/
} PCNTR4_b;
};


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

3.4.1.3

外设模块寄存器定义

我们在上一步已经定义好了R_PORT0_Type类型的结构体,它包含了IOPORT的寄存器定义。接下来使用宏定义来表示结构体指针,指针指向IOPORT外设的每个端口的寄存器首地址。

列表3:代码清单3‑3寄存器定义

#defineR_PORT0 ((R_PORT0_Type *) R_PORT0_BASE)
#defineR_PORT1 ((R_PORT0_Type *) R_PORT1_BASE)
#defineR_PORT2 ((R_PORT0_Type *) R_PORT2_BASE)
#defineR_PORT3 ((R_PORT0_Type *) R_PORT3_BASE)
#defineR_PORT4 ((R_PORT0_Type *) R_PORT4_BASE)
#defineR_PORT5 ((R_PORT0_Type *) R_PORT5_BASE)
#defineR_PORT6 ((R_PORT0_Type *) R_PORT6_BASE)
#defineR_PORT7 ((R_PORT0_Type *) R_PORT7_BASE)
#defineR_PORT8 ((R_PORT0_Type *) R_PORT8_BASE)
#defineR_PORT9 ((R_PORT0_Type *) R_PORT9_BASE)
#defineR_PORT10 ((R_PORT0_Type *) R_PORT10_BASE)

这样便大功告成了,我们就可以使用这些宏来访问各个IO端口的每一个寄存器了。

3.4.2

修改寄存器操作的本质:读-改-写

有了以上的对IOPORT这个外设模块的寄存器的定义,我们便完成了“C语言对寄存器的封装”这个步骤,接下来我们便可以使用C语言对寄存器进行各种操作了。

对寄存器进行操作可以是忽略寄存器原本的值,而直接覆盖写入新的值;但是更为一般的操作是根据原本的寄存器值进行修改,即:先读出寄存器原本的值,然后修改该值,最后重新写入到寄存器里面,让新的值生效。

接下来将介绍修改寄存器的几种通用方法。

3.4.2.1

清零寄存器上的某N个位

使用C语言的按位与“&”运算符可以将位进行清零。

列表4:代码清单3‑4位清零:按位与&

//清零某个位
R_PORT0->PODR &= ~(1u<<0); //清零 PODR 寄存器的第 0 位
R_PORT0->PODR &= ~(1u<<6); //清零 PODR 寄存器的第 6 位


//清零多个位
R_PORT0->PODR &= ~(3u<<0); //清零 PODR 寄存器的第 0,1 位
R_PORT0->PODR &= ~(3u<<6); //清零 PODR 寄存器的第 6,7 位

3.4.2.2

对寄存器上的某N个位进行置位

使用C语言的按位或“|”运算符可以将位进行置一。

列表5:代码清单3‑5位置位:按位或|

//置位某个位
R_PORT0->PODR |= 1u<<0; //PODR 寄存器的第 0 位置 1
R_PORT0->PODR |= 1u<<6; //PODR 寄存器的第 6 位置 1


//置位多个位
R_PORT0->PODR |= 3u<<0; //PODR 寄存器的第 0,1 位置 1
R_PORT0->PODR |= 3u<<6; //PODR 寄存器的第 6,7 位置 1

3.4.2.3

对寄存器上的某N个位进行取反

使用C语言的按位异或“^”运算符可以将位进行取反。

列表6:代码清单3‑6位取反:按位异或^

//取反某个位
R_PORT0->PODR ^= 1u<<0; //取反 PODR 寄存器的第 0 位
R_PORT0->PODR ^= 1u<<6; //取反 PODR 寄存器的第 6 位


//取反多个位
R_PORT0->PODR ^= 3u<<0; //取反 PODR 寄存器的第 0,1 位
R_PORT0->PODR ^= 3u<<6; //取反 PODR 寄存器的第 6,7 位

文章推荐

相关推荐

  • 小安派BW21 UNO从机

    作为一款本地AI图象识别开发板,BW21-CBV-Kit它能够独自运行目标识别模型。2.4GHz+5GHz的双频Wi-Fi,提供高性能的无线传输能 ...

    2025-05-21
  • IGBT模块吸收回路分析模型

    IGBT模块吸收回路分析模型 一、IGBT模块吸收电路的模型 尽管开关器件内部工作机理不同,但对于吸收电路的分析而 ...

    2025-05-21
  • BLDC电机的基本结构和控制方式

    来源:攻城狮原创之设计分享 直流无刷电机(Brushless DC Motor,BLDC)是一种基于电子换向技术的高效电机,具有长寿命 ...

    2025-05-21
  • 联通智家通通:聚四方守护之力,筑万家AI通途

    “神兽镇宅”,是深植于中国人心中的居家智慧。朱雀、玄武等神兽,站立在紫禁城之巅,也悬于普通百姓门上,寄托着镇守四方风雨 ...

    2025-05-21
  • FinFET与GAA结构的差异及其影响

    文章来源:老虎说芯 原文作者:老虎说芯 本文介绍了当半导体技术从FinFET转向GAA(Gate-All-Around)时工艺面临 ...

    2025-05-21
  • 一文详解球栅阵列封装技术

    文章来源:学习那些事 原文作者:前路漫漫 本文介绍了球栅阵列封装的结构、分类、应用和发展趋势。 概述 ...

    2025-05-21
  • 智能车电磁组——基本控制篇

    智能车电磁组——基本控制篇 前言 电磁车的控制比较简单,可以分为信号采集,舵机控制和电机控制三部分, ...

    2025-05-21
  • 低成本电源排序器解决方案

    绝大多数负载点DC-DC转换器可以将上一个转换器的电源就绪输出连接至下一个转换器的使能输入,实现上电排序。这种方法只适合 ...

    2025-05-21
  • 注入增强型IGBT学习笔记

    来源:星际浮尘 注入增强型IGBT 1、结构特点与典型工艺 1.1结构提出与发展 为了协调 ...

    2025-05-21
  • 解析RZ/N2L CANFD模块的缓冲区机制(3)

    在工业自动化、智能交通、机器人等领域,CANFD(CAN with Flexible Data-Rate)技术正逐步取代传统CAN,以适应更高的数据速 ...

    2025-05-21