一起游 手游攻略 手游评测 详解极海G32R501 MCU的两核外设分配

详解极海G32R501 MCU的两核外设分配

时间: 来源:互联网 浏览:0

《极海芯得》系列的内容是用户使用集海系列产品的体验总结。均转载自21ic论坛集海半导体专区。全文未经过任何形式的修改,未经原作者授权禁止转载。

你是否遇到过这样尴尬的场景:当你想让CPU0和CPU1“好好相处”时,却发现他们居然在争夺外设?当我还在想“我的GPIO在哪里?为什么它去了另一个核心?”我正在翻阅用户手册,当我看到到处都是寄存器名称时,我的头很痛。

如果您也有这样的经历,那么恭喜您,这意味着您已经开始深入了解双核MCU G32R501了!今天我们来谈谈G32R501如何让两个核心共享和分配外设。顺便我们也会提供一些代码示例,让你在双核路上不再迷茫。

1、双核背景:G32R501长啥样?

在这款名为G32R501Dx的MCU中,最显着的特点当然是双核配置。在芯片的硬件框图中,CPU0和CPU1就像一对“欢喜冤家”,拥有各自的Cache和RAM,整齐地驻扎在芯片内部。

e18bc702-ed25-11f0-92de-92fbcf53809c.png

那么问题来了:既然是两个核心,那么外设该由谁来使用呢?假设所有GPIO都分配给了CPU1,那么CPU0如果只是盯着看怎么办?这需要寄存器进行访问控制。

2.外设访问控制:PERIPH_AC

如果你查找《G32R501用户手册V1.5.pdf》,你可以在12.10章中看到一个名为“PERIPH_AC”的访问控制寄存器。它的作用是指定主要外设(如ADCA、ADCB等)的访问权限。可以简单理解为:“CPU0可以读写这个外设吗?”、“CPU1有权限吗?”、“DMA可以参与吗?”等等。

2.1 寄存器结构

以ADCB为例,其访问控制寄存器(ADCB_AC)在手册中的描述如下:

• CPU0_ACC、CPU1_ACC 和DMA1_ACC 分别控制CPU0、CPU1 和DMA1 的读写权限。

• 如果组合值为00,则完全禁止读写;

• 10 为“只读+清除访问,但禁止写入”;

• 11 表示完全读写,类似于“VIP 通道”。

说明书比我说的严谨多了,大家可以自己参考一下。大致的寄存器表如下所示:

e1e52ee6-ed25-11f0-92de-92fbcf53809c.png

2.2 库函数调用

如果你觉得翻寄存器表很累,SDK为你贴心地封装了功能。在driverlib库中,可以看到类似下面的函数原型

(SysCtl_setPeripheralAccessControl):

静态内联空

SysCtl_setPeripheralAccessControl(SysCtl_AccessPeripheral 外设,

SysCtl_AccessMaster主控,

SysCtl_AccessPermission 权限)

{

//

//设置指定外设的主控权限。每个大师都有

//两位专用于其权限设置。

//

WRPRT_DISABLE;

HWREG(PERIPHAC_BASE + (uint16_t)外设* 2)=

(HWREG(PERIPHAC_BASE + (uint16_t)外设* 2)

~(0x3U (uint16_t)master)) |

((uint16_t)权限(uint16_t)master);

WRPRT_ENABLE;

}

如果你想快速禁止CPU1读写ADCB,你只需要调用一个函数即可完成。这种封装对于新手来说极其友好,再也不用担心记住寄存器偏移量了!

3. GPIO访问控制:谁拥有主核心?

GPIO简直就是MCU的脸面! “打开”MCU (Hello World) 的第一步是单击GPIO。这时候如果你拿到G32R501,你会发现它可以做“主核”把——绑定到GPIO上,告诉芯片这个GPIO是由CPU0管理还是由CPU1管理。

3.1 GPxCSELx 寄存器

在用户手册的21.9.16和21.9.36章节中,可以找到GPxCSELx等寄存器的说明,如“GPACSEL1”,其中每两位控制一个GPIO引脚的主核心。 00代表CPU0,01代表CPU1。

e245fd52-ed25-11f0-92de-92fbcf53809c.png

3.2 驱动库函数:GPIO_setMasterCore

驱动库还贴心的帮你封装了“烧脑寄存器”:

无效

GPIO_setMasterCore(uint32_t 引脚, GPIO_CoreSelect 内核)

{

易失性uint32_t *gpioBaseAddr;

uint32_t cSelIndex;

uint32_t shiftAmt;

//

//Check the arguments.

//

ASSERT(GPIO_isPinValid(引脚));

gpioBaseAddr=(uint32_t *)GPIOCTRL_BASE +

((引脚/32U) * GPIO_CTRL_REGS_STEP);

shiftAmt=(uint32_t)GPIO_GPACSEL1_GPIO1_S * (引脚% 8U);

cSelIndex=GPIO_GPxCSEL_INDEX + ((引脚% 32U)/8U);

//

//将核心参数写入寄存器。

//

WRPRT_DISABLE;

gpioBaseAddr[cSelIndex]=~((uint32_t)GPIO_GPACSEL1_GPIO0_M shiftAmt);

gpioBaseAddr[cSelIndex] |=(uint32_t)core shiftAmt;

WRPRT_ENABLE;

}

一句话:传入你要设置的引脚号,以及指定的CPU是CPU0还是CPU1,函数就会帮你改寄存器,可以省很多力气。

4. 外部中断分配:EXTI_xMASKx

除了GPIO之外,另外80%被抢最多的是中断。你肯定不希望A核测量温度时触发外部中断来中断B核的运动控制过程吧?因此,需要用寄存器来控制“我要让谁来响应这个中断”,优雅地将中断“发送”到某个核心。

4.1 EXTI_IMASKx、EXTI_EMASKx

在14.6.5章节中,官方为我们准备了EXTI_IMASK0、EXTI_IMASK1、EMASK0、EMASK1等寄存器。它们是中断和事件屏蔽寄存器,用于控制相应的外部中断或外部事件是否允许CPU0或CPU1响应。当屏蔽位为0时,不会接收到任何内容。将其设置为1 将启用它。

e2a0aed2-ed25-11f0-92de-92fbcf53809c.png

对应的driverlib函数如下:

static inline void

EXTI_enableInterrupt(EXTI_CoreSelect 内核, EXTI_LineNumber 行号)

{

WRPRT_DISABLE;

if(核心==EXTI_CORE_CPU0)

{

HWREG(EXTI_BASE + EXTI_O_IMASK0) |=(1U (uint32_t)lineNumber);

}

否则

{

HWREG(EXTI_BASE + EXTI_O_IMASK1) |=(1U (uint32_t)lineNumber);

}

WRPRT_ENABLE;

}

静态内联空

EXTI_disableInterrupt(EXTI_CoreSelect 内核, EXTI_LineNumber 行号)

{

WRPRT_DISABLE;

if(核心==EXTI_CORE_CPU0)

{

HWREG(EXTI_BASE + EXTI_O_IMASK0)=~(1U (uint32_t)lineNumber);

}

否则

{

HWREG(EXTI_BASE + EXTI_O_IMASK1)=~(1U (uint32_t)lineNumber);

}

WRPRT_ENABLE;

}

static inline void

EXTI_enableEvent(EXTI_CoreSelect 核心, EXTI_LineNumber 行号)

{

WRPRT_DISABLE;

if(核心==EXTI_CORE_CPU0)

{

HWREG(EXTI_BASE + EXTI_O_EMASK0) |=(1U (uint32_t)lineNumber);

}

否则

{

HWREG(EXTI_BASE + EXTI_O_EMASK1) |=(1U (uint32_t)lineNumber);

}

WRPRT_ENABLE;

}

静态内联空

EXTI_disableEvent(EXTI_CoreSelect 核心, EXTI_LineNumber 行号)

{

WRPRT_DISABLE;

if(核心==EXTI_CORE_CPU0)

{

HWREG(EXTI_BASE + EXTI_O_EMASK0)=~(1U (uint32_t)lineNumber);

}

否则

{

HWREG(EXTI_BASE + EXTI_O_EMASK1)=~(1U (uint32_t)lineNumber);

}

WRPRT_ENABLE;

}

只要指定了核心(CPU0或CPU1)和外部中断线,就可以轻松地让中断“运行”到所需的核心。

5、CPU1响应外部中断示例

既然说到这里,我们就用SDK例程来练练“真刀真枪”吧。 SDK中有两个经典的例子:

1.CPU1基本例程(ipc_ex2_mailbox_pollingcpu1)

2. 外部中断例程(interrupt_ex1_external)

假设我们想在CPU1上使用外部中断来检测某个输入引脚的“电平下降”。那么需要进行哪些修改呢?

5.1 修改代码

ipc_ex2_mailbox_pollingcpu1的原始代码只是一个简单的IPC消息发送和接收演示,没有外部中断。我们将CPU1的中断响应添加到XINT1(相当于EXTI_LINE_4)。主要有以下几点:

1. 注册并使能INT_XINT1中断向量

2. 在初始化阶段,禁止CPU0 的XINT1 中断,使能CPU1 的XINT1 中断。

3.配置GPIO0触发XINT1

4、在ISR中,使用EXTI_getInterruptStatus等方法来判断和清除中断,同时也做一些LED状态改变、计数打印等。

5.2 实际代码

要重现,可以将以下所有代码复制到ipc_ex2_mailbox_pollingcpu1sourceipc_ex2_mailbox_polling_cpu1.c

//##########################################################################

//

//FILE: ipc_ex2_mailbox_polling_cpu1.c

//

//TITLE: 带轮询的IPC 邮箱示例

//

//版本: 1.0.0

//

//日期: 2025-01-15

//

//!添加到组driver_example_list

//!

IPC Mailbox with Polling

//!

//!本例演示如何使用Inter-Processor的邮箱机制

//!通信(IPC)模块,以轮询模式在两个CPU之间发送和接收数据。

//!

//!在这个例子中:

//! 1. CPU0 通过IPC 模块以轮询方式向CPU1 发送消息。

//! 2. CPU1 以轮询方式向CPU0 发送消息。

//! 3. CPU0 以轮询方式接收CPU1 发送的消息,以此类推。

//!

//!ote Note: IPC 示例包括CPU0 和CPU1 项目。

//!请先编译CPU1 工程,然后再编译CPU0 工程。

//!

//!运行应用程序

//!使用terminal:打开具有以下设置的COM端口

//! - 找到正确的COM端口

//! - 每秒位数=115200

//! - 数据位=8

//! - 奇偶校验=无

//! - 停止位=1

//! - 硬件控制=无

//!

//!外部连接

//!通过收发器和电缆将UART-A 端口连接到PC。

//! - GPIO28 是UART_RXD(连接到串行DB9 电缆的Pin3、PC-TX)

//! - GPIO29 是UART_TXD(连接到串行DB9 电缆的Pin2、PC-RX)

//!

//!观察变量

//! - 没有任何。

//!

//

//##########################################################################

//

//

//$Copyright:

//版权所有(C) 2024 吉希半导体- http://www.geehy.com/

//

//除非符合以下规定,否则您不得使用此文件

//GEEHY 版权声明(GEEHY 软件包许可证)。

//

//程序仅供参考,希望分发

//这对于客户的开发来说是有用且具有指导意义的

//他们的软件。除非适用法律要求或双方同意

//写作时,程序是按“原样”分发的,没有

//任何明示或暗示的保证或条件。

//请参阅GEEHY 软件包许可证以获取管理权限

//以及许可证下的限制。

//$

//##########################################################################

//

//包含的文件

//

#include'driverlib.h'

#include'设备.h'

#include'board.h'

#包括

//

//用多少条消息来测试邮箱发送和接收

//

#defineMESSAGE_LENGTH 4U

//

//变量

//

静态uint32_t g_msgRecv[MESSAGE_LENGTH];

易失性uint32_t xint1Count;

//

//函数原型

//

无效clearMsgRecv(无效);

无效UART_Init(无效);

无效xint1ISR(无效);

//

//主要

//

无效example_main(无效)

{

//

//初始化设备时钟和外设

//

Device_init();

//

//初始化NVIC 并清除NVIC 寄存器。禁用CPU 中断。

//

中断_initModule();

//

//使用指向shell 中断的指针初始化NVIC 向量表

//服务例程(ISR)。

//

中断_initVectorTable();

//

//本例中使用的中断被重新映射到

//在此文件中找到ISR 函数。

//

中断_寄存器(INT_XINT1, xint1ISR);

//

//板初始化

//

Board_init();

//

//允许CPU中断

//

中断_enableMaster();

//

//串口初始化

//

UART_Init();

xint1Count=0; //计数XINT1 中断

//

//CPU1打印信息

//

printf('CPU1已完成启动,中断。');

//

//接收前清空g_msgRecv数组

//

清除MsgRecv();

//

//CPU1接收CPU0发来的消息

//

while (IPC_isRxBufferFull(IPC_RX_0) !=true);

g_msgRecv[0]=IPC_receive32Bits(IPC_RX_0);

while (IPC_isRxBufferFull(IPC_RX_1) !=true);

g_msgRecv[1]=IPC_receive32Bits(IPC_RX_1);

while (IPC_isRxBufferFull(IPC_RX_2) !=true);

g_msgRecv[2]=IPC_receive32Bits(IPC_RX_2);

while (IPC_isRxBufferFull(IPC_RX_3) !=true);

g_msgRecv[3]=IPC_receive32Bits(IPC_RX_3);

//

//CPU1将消息发送回CPU0

//

while (IPC_isTxBufferEmpty(IPC_TX_0) !=true);

IPC_transmit32Bits(IPC_TX_0, g_msgRecv[0]);

while (IPC_isTxBufferEmpty(IPC_TX_1) !=true);

IPC_transmit32Bits(IPC_TX_1, g_msgRecv[1]);

while (IPC_isTxBufferEmpty(IPC_TX_2) !=true);

IPC_transmit32Bits(IPC_TX_2, g_msgRecv[2]);

while (IPC_isTxBufferEmpty(IPC_TX_3) !=true);

IPC_transmit32Bits(IPC_TX_3, g_msgRecv[3]);

//

//设置优先级组以指示PREEMPT 和SUB 优先级位。

//

Interrupt_setPriorityGroup(INTERRUPT_PRIGROUP_PREEMPT_7_6_SUB_5_0);

//

//设置全局和组优先级以允许CPU中断

//具有更高的优先级

//

中断_设置优先级(INT_XINT1,1,0);

//

//启用XINT1 和XINT2 i

n the NVIC. // Enable INT1 which is connected to WAKEINT: // Interrupt_enable(INT_XINT1); // // Enable Global Interrupt and real time interrupt // EINT; ERTM; // // GPIO0 is mapped to XINT1 // GPIO_setMasterCore(0, GPIO_CORE_CPU1); GPIO_setInterruptPin(0, GPIO_INT_XINT1); EXTI_disableInterrupt(EXTI_CORE_CPU0, EXTI_LINE_4); EXTI_enableInterrupt(EXTI_CORE_CPU1, EXTI_LINE_4); // // Configure falling edge trigger for XINT1 // GPIO_setInterruptType(GPIO_INT_XINT1, GPIO_INT_TYPE_FALLING_EDGE); // // Enable XINT1 // GPIO_enableInterrupt(GPIO_INT_XINT1); // Enable XINT1 // // Loop. // for(;;) { } } // // xint1ISR - External Interrupt 1 ISR // void xint1ISR(void) { // // Get External Interrupt 1 status // if(EXTI_getInterruptStatus(EXTI_CORE_CPU1, EXTI_LINE_4)) { GPIO_togglePin(myGPIOOutput_LED2); xint1Count++; printf("CPU1 EXTI Interrupt: %02d ",xint1Count); // // Clear external interrupt 1 status // EXTI_clearInterruptStatus(EXTI_CORE_CPU1, EXTI_LINE_4); } } // // Function to clear the g_msgRecv array. // This function set g_msgRecv to be 0. // void clearMsgRecv(void) { uint32_t i; for (i = 0U; i < MESSAGE_LENGTH; i++) { g_msgRecv[i] = 0U; } } // // UART initialize // void UART_Init(void) { // // GPIO28 is the UART Rx pin. // GPIO_setMasterCore(DEVICE_GPIO_PIN_UARTRXDA, GPIO_CORE_CPU1); GPIO_setPinConfig(DEVICE_GPIO_CFG_UARTRXDA); GPIO_setDirectionMode(DEVICE_GPIO_PIN_UARTRXDA, GPIO_DIR_MODE_IN); GPIO_setDrivingCapability(DEVICE_GPIO_PIN_UARTRXDA,GPIO_DRIVE_LEVEL_VERY_HIGH); GPIO_setPadConfig(DEVICE_GPIO_PIN_UARTRXDA, GPIO_PIN_TYPE_STD); GPIO_setQualificationMode(DEVICE_GPIO_PIN_UARTRXDA, GPIO_QUAL_ASYNC); // // GPIO29 is the UART Tx pin. // GPIO_setMasterCore(DEVICE_GPIO_PIN_UARTTXDA, GPIO_CORE_CPU1); GPIO_setPinConfig(DEVICE_GPIO_CFG_UARTTXDA); GPIO_setDirectionMode(DEVICE_GPIO_PIN_UARTTXDA, GPIO_DIR_MODE_OUT); GPIO_setDrivingCapability(DEVICE_GPIO_PIN_UARTTXDA,GPIO_DRIVE_LEVEL_VERY_HIGH); GPIO_setPadConfig(DEVICE_GPIO_PIN_UARTTXDA, GPIO_PIN_TYPE_STD); GPIO_setQualificationMode(DEVICE_GPIO_PIN_UARTTXDA, GPIO_QUAL_ASYNC); // // Initialize UARTA and its FIFO. // UART_performSoftwareReset(UARTA_BASE); // // Configure UARTA for echoback. // UART_setConfig(UARTA_BASE, DEVICE_LSPCLK_FREQ, 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE)); UART_resetChannels(UARTA_BASE); UART_resetRxFIFO(UARTA_BASE); UART_resetTxFIFO(UARTA_BASE); UART_clearInterruptStatus(UARTA_BASE, UART_INT_TXFF | UART_INT_RXFF); UART_enableFIFO(UARTA_BASE); UART_enableModule(UARTA_BASE); UART_performSoftwareReset(UARTA_BASE); } #ifdefined(__CC_ARM) || defined(__ARMCC_VERSION) // // Redefine the fputc function to the serial port // int fputc(int ch, FILE* f) { if (ch == ' ') { UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)' '); } UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)ch); return (ch); } #elifdefined(__ICCARM__) int __io_putchar(int ch) { if (ch == ' ') { UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)' '); } UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)ch); return (ch); } int __write(int file, char* ptr, int len) { int i; for (i = 0; i < len; i++) { __io_putchar(*ptr++); } return len; } #elifdefined (__clang__) && !defined (__ARMCC_VERSION) int uart_putc(char ch, FILE *file) { if (ch == ' ') { UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)' '); } UART_writeCharBlockingNonFIFO(UARTA_BASE, (uint8_t)ch); return (ch); } static FILE __stdio = FDEV_SETUP_STREAM(uart_putc, NULL, NULL, _FDEV_SETUP_WRITE); FILE *const stdin = &__stdio; __strong_reference(stdin, stdout); __strong_reference(stdin, stderr); #endif // // End of File // 5.3 运行效果 如果一切顺利,当GPIO0产生上下跳沿时,CPU1的XINT1服务例程会被触发。 LED随之闪烁,串口打印出类似“CPU1EXTIInterrupt:01”“CPU1EXTIInterrupt:02”等等。 当你看到CPU1自信地响应外部中断,CPU0则不为所动,就说明“多核外部中断独立控制”成功啦! e300f9cc-ed25-11f0-92de-92fbcf53809c.png 6、踩坑 凡事说起来都挺美好,可实际开发过程中,踩坑是免不了的。下面就分享一些常见“翻车”瞬间,让各位少走点弯路: 1. 开小差就忘了WRPRT_DISABLE/WRPRT_ENABLE 在写各种访问控制寄存器时,经常需要先“解锁”再写,然后再“上锁”。如果忘了在修改前后来一句 WRPRT_DISABLE; …(寄存器操作)… WRPRT_ENABLE; 那么你会发现自己改了半天没生效。这个“解锁-上锁”机制就像给寄存器设置了“防熊孩子模式”,不解锁是改不动的。没做对的话,一定会抓瞎很久。 2. 访问权限没开足 你可能会纳闷:“为什么CPU0能读写外设,CPU1却获取不到数据?”别苦恼,先检查一下PERIPH_AC里面有没有给CPU1设置Full Access。要是权限只开了半截(Protected Read之类),CPU1写不进去也正常啊! 3. GPIO主核选择忘了改 当你兴致勃勃地在CPU1里调用GPIO_writePin(XX, HIGH),结果测量脚位却毫无波动——很可能是GPIO主核依然挂在CPU0身上… 莫名其妙,就像你在隔壁家灯的开关上乱按,当然亮不了自家灯。 所以一定别忘了GPIO_setMasterCore(pin, GPIO_CORE_CPU1)或GPIO_CORE_CPU0! 欢迎各位在评论区留下配置双核访问不同外设的小tip吧! 原文地址:https://bbs.21ic.com/icview-3501421-1-1.html?_dsign=fe2bb841
标题:详解极海G32R501 MCU的两核外设分配
链接:https://yqqlyw.com/news/sypc/69304.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
更多
  • 当RA MCU遇见Zephyr系列(4)——闪灯
  • 瑞芯微(EASY EAI)RV1126B GPIO使用
  • 绯红之境兑换码最新2021 礼包兑换码大全

    绯红之境兑换码最新2021 礼包兑换码大全[多图],绯红之境兑换码怎么领取?绯红之境兑换码有哪些?绯红之境在今日

    2026-01-13
    三国群英传7霸王再临攻略 霸王再临攻略技巧开启方法

    三国群英传7霸王再临攻略 霸王再临攻略技巧开启方法[多图],三国群英传7霸王再临怎么玩?三国群英传7霸王再临

    2026-01-13
    妄想山海怎么加好友 加好友方法大全

    妄想山海怎么加好友 加好友方法大全[多图],妄想山海添加好友功能在哪里?妄想山海添加好友的方法是什么?好友添

    2026-01-13
    江南百景图又见桃花村钓鱼位置在哪?又见桃花村钓鱼攻略

    江南百景图又见桃花村钓鱼位置在哪?又见桃花村钓鱼攻略[多图],江南百景图又见桃花村钓鱼怎么钓?又见桃花村钓

    2026-01-13