DSP28377 memcpy函数导致概率性进入interrupt void ILLEGAL_ISR(void)

本文介绍了在CCS编程环境中,针对DSP28377s进行编程时memcpy函数的正确使用方法。强调了在不同数据类型下memcpy的长度计算方式,避免因长度计算错误导致的操作越界问题。

在CCS对DSP28377s编程环境下,memcpy拷贝的个数需按照16位的方式计算,按照8位计算出的个数赋值会导致操作越界,从而进入interrupt void ILLEGAL_ISR(void)函数。

例:unsigned short S[10];

       unsigned short D[10];

      memcpy(D,S,10);

 例:unsigned int S[10];

        unsigned int D[10];

        memcpy(D,S,20);



通常情况下,为了产品后续的升级,程序都会分为两部分Bootloader+App,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的 在STM32F103 F3内核上是可以配置寄存器SCB->VTOR设置中断向量表偏移,CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上 解决思路: 将中断向量表重映射到RAM(内存) 根据当前运行程序将FLASH中的向量表拷贝到RAM中 解决步骤 将地址0x00000000的中断向量表中的全部中断函数都设置为同一个函数,用于映射 编写映射函数 修改RAM配置信息 根据前当前运行的程序拷贝中断向量表到RAM (注意:以下代码都是编写在Bootloader程序中) 第一步:设置映射 这里利用编译器设置函数的就可以简单实现 #define ALIAS(x) __attribute__ ((alias (#x))) void NMI_Handler(void) ALIAS(interrupt_vector_relay); void HardFault_Handler(void)ALIAS(interrupt_vector_relay); void SVC_Handler(void) ALIAS(interrupt_vector_relay); void PendSV_Handler(void) ALIAS(interrupt_vector_relay); void SysTick_Handler(void) ALIAS(interrupt_vector_relay); void TMR0_IRQHandler(void) ALIAS(interrupt_vector_relay); void GPIO_IRQHandler(void) ALIAS(interrupt_vector_relay); void SLAVE_IRQHandler(void) ALIAS(interrupt_vector_relay); void SPI0_IRQHandler(void) ALIAS(interrupt_vector_relay); void BB_IRQHandler(void) ALIAS(interrupt_vector_relay); void LLE_IRQHandler(void) ALIAS(interrupt_vector_relay); void USB_IRQHandler(void) ALIAS(interrupt_vector_relay); void ETH_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR1_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR2_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART0_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART1_IRQHandler(void) ALIAS(interrupt_vector_relay); void RTC_IRQHandler(void) ALIAS(interrupt_vector_relay); void ADC_IRQHandler(void) ALIAS(interrupt_vector_relay); void SPI1_IRQHandler(void) ALIAS(interrupt_vector_relay); void LED_IRQHandler(void) ALIAS(interrupt_vector_relay); void TMR3_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART2_IRQHandler(void) ALIAS(interrupt_vector_relay); void UART3_IRQHandler(void) ALIAS(interrupt_vector_relay); void WDT_IRQHandler(void) ALIAS(interrupt_vector_relay); 这里利用**attribute ((alias ()))**,将全部的中断函数都设置为函数interrupt_vector_relay()的别名,通过这样声明函数,所有的中断函数等于interrupt_vector_relay()这个函数 步骤2:编写映射函数 我们只需要读取寄存器查看当前是发生了哪个中断,然后对应跳转到真正执行动作的函数即可 _vector_table_pointer 这个是RAM地址,这里利用RAM前面的0x100字节作为中断向量表存放位置 //中断向量表位置 #define _vector_table_pointer 0x20000000 __asm void interrupt_vector_relay(void) { //IPSR是中断状态寄存器,因为每个中断函数地址是4个字节,因此将读取数值*4(左移两位)得到的就是地址偏移 mrs r0, ipsr; //将值左移2位 lsls r0, r0, #0x02; //从存储器地址取数据到R1 ldr r1, =_vector_table_pointer; //计算出偏移 adds r1, r1, r0; //将该地址的值给r1 ldr r1, [r1]; //跳转到相对应的中断 bx r1; nop } 在跳转到APP前,将APP的中断向量表拷贝到RAM里面即可 //APP的地址,根据实际修改 #define APP_BASE 0x00004000 //拷贝中断向量表 memcpy((void *)(_vector_table_pointer),(void *)(APP_BASE),0x100); . 补充:app的中断函数编写和使用与正常情况下一致,无需特殊处理 但如果在Bootloader需要用到中断,比如使用定时器中断 UINT32 TIM_IRQ_ADDR; TIM_IRQ_ADDR = (UINT32)DFU_TMR0_IRQHandler; memcpy((void *)(_vector_table_pointer + 0x40),&TIM_IRQ_ADDR,4); Bootloader不能直接编写TMR0_IRQHandler() 因为这个名字已经被占用了, 需要自定义一个函数比如DFU_TMR0_IRQHandler(),然后将该函数的地址拷贝到RAM中的特定位置,这里的0x40就是对应定时器的中断函数. 根据上述 帮我生成完整的代码
最新发布
07-25
<think>首先,用户的问题是关于在CH579(ARM Cortex-M0内核)上实现Bootloader和App之间的中断向量表切换。用户提到,在STM32F103(M3内核)上可以使用SCB->VTOR寄存器来设置中断向量表偏移,但CH579是M0内核,不支持这个操作,因为中断向量表固定在地址0x00000000。 用户想通过将向量表重映射到RAM并动态更新函数地址来实现这个切换。我需要基于提供的引用和一般知识来回答。 关键点: - CH579是Cortex-M0内核。 - Cortex-M0没有VTOR(Vector Table Offset Register),所以中断向量表总是固定在0x00000000。 - 为了在Bootloader和App之间切换中断,需要一种机制来重定向中断处理。 解决方案思路: 1. **重映射向量表到RAM**:将中断向量表复制到RAM中,并在运行时更新向量表的内容。 2. **设置中断向量表指针**:在M0上,虽然没有VTOR,但可以通过其他方式模拟。一种常见的方法是使用一个RAM区域作为向量表,并在启动时复制或更新向量表内容。 3. **Bootloader和App的协调**: - Bootloader运行时,中断向量表指向Bootloader的处理函数。 - 当跳转到App时,需要将向量表切换到App的处理函数。 - 由于向量表固定在0x00000000,但Flash可能被分成了Bootloader和App区域,所以我们需要将向量表的内容动态改变。 具体步骤(基于Cortex-M0的通用方法): - **向量表结构**:中断向量表是一个数组,每个条目是中断服务程序(ISR)的地址。在Cortex-M中,第一个条目是初始堆栈指针,然后是复位向量,接着是各种中断的ISR。 - **RAM向量表**:在RAM中分配一个区域作为新的向量表。在App启动时,将App的向量表内容复制到这个RAM区域。 - **中断向量重定向**:在系统启动后,通过软件修改向量表的内容。但M0没有硬件支持,所以我们需要在每次中断发生时,通过一个跳板(trampoline)或间接层来重定向。 - **常见方法**: - 在固定地址(0x00000000)放置一个简单的向量表,每个条目指向一个通用的中断处理函数(如一个跳转指令)。 - 这个通用处理函数读取当前状态(在Bootloader还是App),然后跳转到实际的处理函数。 对于CH579的具体实现: - 参考引用[^1]:它提到CH579不支持VTOR,所以需要其他方法。 - 建议方法:使用RAM中的向量表,并通过修改系统控制块或使用软件中断重定向。 详细步骤: 1. **Bootloader设计**: - Bootloader的向量表在Flash的起始地址(0x00000000)。 - 当Bootloader准备跳转到App时,它需要设置App的堆栈指针和复位向量,并切换到App的向量表。 2. **App设计**: - App的代码链接到另一个地址,例如0x00008000(假设Bootloader在0x00000000到0x00007FFF)。 - App的向量表应该被复制到RAM中。RAM的起始地址需要被用作向量表。 3. **向量表重映射**: - 在启动代码中,初始化时,将向量表复制到RAM。 - 使用一个函数来设置向量表指针。由于没有VTOR,我们需要手动处理中断分发。 - 一种方法是:在0x00000000的向量表,每个条目指向一个统一的处理函数。这个函数检查一个全局变量(指示当前模式),然后跳转到相应的ISR。 伪代码示例: - 定义一个全局变量:`uint32_t *current_vector_table;` - 在启动时,设置`current_vector_table`指向Bootloader或App的向量表。 - 编写一个通用的中断处理函数: ```c void Generic_Handler(void) { uint32_t *table = current_vector_table; // 根据中断号跳转到对应的ISR // 这需要知道中断号,可能通过汇编获取 } ``` - 但实现这个需要汇编支持,因为中断发生时,处理器会直接跳转到向量表中的地址。 在Cortex-M0上,更实际的做法是: - 使用一个小的汇编代码来设置向量表重定向。 - 例如,在reset handler中,复制向量表到RAM,并设置中断处理。 对于CH579,参考WCH提供的资料或示例代码。WCH(WCH Electronics)可能有特定的实现。 基于一般ARM Cortex-M0知识: 1. **在Bootloader中**: - 向量表在0x00000000。 - 当跳转到App时,先禁用中断,复制App的向量表到RAM,设置一个全局指针,然后启用中断。 2. **在App中**: - 在App的启动代码中,同样需要复制向量表到RAM,并设置重定向。 3. **重定向机制**: - 修改0x00000000的向量表,使其每个条目指向RAM中的一个位置,RAM中存储实际的ISR地址。 - 或者,使用一个跳转表。 具体代码示例(简化): - 在RAM中定义一个数组作为新向量表:`uint32_t vector_table[48];` // 假设48个条目 - 在系统初始化时: ```c // 复制原始向量表到RAM memcpy(vector_table, (void*)0x00000000, sizeof(vector_table)); // 现在,修改vector_table的内容为App的ISR // 但原始在Flash,不能修改,所以我们需要在App启动时重写RAM向量表 ``` - 为了重定向中断,我们需要改变处理器如何访问向量表。由于不能修改Flash,我们需要在运行时使用RAM向量表。 一个标准方法是: - 在reset后,立即将向量表复制到RAM。 - 然后,使用一个汇编函数来设置中断处理。 但是,在Cortex-M0上,中断向量表是只读的,除非我们重新映射地址0。 另一种方法:使用内存重映射或别名。 - CH579可能支持内存重映射寄存器。检查芯片手册。 - 引用中没有提到,但CH579可能有类似于VTOR的机制,或者通过系统控制寄存器。 从引用[^1]:它说“CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上”,所以我们必须处理这个固定地址。 因此,软件解决方案: 1. **在Bootloader中**: - 保持向量表在0x00000000指向Bootloader的ISR。 - 当跳转到App时: - 禁用中断。 - 复制App的向量表内容到RAM中的一个位置(例如0x20000000)。 - 设置一个全局变量来存储当前向量表地址。 - 修改0x00000000的向量表,但既然Flash是只读的,我们不能修改。所以,我们需要一个不同的方法。 关键点:我们不能修改Flash中的向量表,所以中断发生时,处理器总是跳转到Flash中的地址。 解决方案:在Flash的向量表,每个条目指向一个固定的RAM位置,该位置存储一个跳转指令或地址。 例如: - 在0x00000000的向量表,每个ISR条目指向同一个地址,比如0x20000000。 - 在0x20000000,我们放置一个跳转指令到通用处理函数。 - 通用处理函数读取中断号,然后跳转到current_vector_table[interrupt_number]。 在汇编中实现: - 定义一个区域在RAM,例如起始于0x20000000,作为中断重定向表。 - 每个中断,在Flash向量表中指向0x20000000 + offset,但所有可以指向同一个地址。 - 在0x20000000,我们有一个汇编函数: ```assembly ldr r0, =current_vector_table ldr r0, [r0] ldr r1, [r0, #offset] ; offset based on interrupt number, but how to get number? bx r1 ``` - 获取中断号:在Cortex-M,中断号可以通过ICSR寄存器或类似方式获取,但M0可能没有。通常,在向量表中,每个条目对应一个固定中断。 更好的方法: - 在RAM中设置一个完整的向量表副本。 - 在启动时,复制Flash向量表到RAM。 - 然后,通过修改系统控制或使用一个trampoline来重定向。 对于CH579,WCH可能有提供库或示例。 参考用户需求:用户想通过将向量表重映射到RAM并动态更新函数地址。 步骤总结: 1. **分配RAM空间**:在RAM中定义一个数组,大小等于向量表(例如,对于N个中断,大小N*4字节)。 2. **初始化RAM向量表**: - 在Bootloader启动时,复制自己的向量表到RAM。 - 在App启动时,复制App的向量表到RAM。 3. **设置中断重定向**: - 由于不能修改Flash向量表,我们需要在Flash向量表中,每个条目指向一个固定的处理函数。 - 这个处理函数读取RAM向量表中的地址并跳转。 4. **实现trampoline**: - 在Flash中,向量表条目指向一个统一的汇编处理函数。 - 这个函数读取当前中断号,然后从current_vector_table中加载地址并跳转。 如何获取中断号: - 在Cortex-M中,中断号可以通过IPSR(Interrupt Program Status Register)读取。M0有IPSR。 - 在异常处理中,IPSR包含当前中断号。 汇编代码示例: ```assembly .section .text .global Default_Handler Default_Handler: ldr r0, =current_vector_table ; 全局变量,存储当前向量表基址 ldr r0, [r0] mrs r1, ipsr ; 读取中断号 lsls r1, r1, #2 ; 乘以4,因为每个条目4字节 adds r0, r0, r1 ldr r0, [r0] ; 加载ISR地址 bx r0 ; 跳转到ISR ``` 在C中: - 定义一个全局变量:`uint32_t *current_vector_table;` - 在启动时,设置它指向RAM向量表。 对于CH579: - 需要确保RAM地址正确对齐。 - 在Bootloader跳转到App时: - 禁用中断。 - 设置current_vector_table指向App的RAM向量表。 - 复制App的向量表内容到RAM(从App的Flash地址)。 - 设置App的堆栈指针(从App的复位向量)。 - 跳转到App的复位处理函数。 App的复位处理函数: - 在App中,也需要初始化自己的向量表到RAM。 完整流程: 1. **Bootloader阶段**: - 系统启动,向量表在0x00000000(Bootloader的Flash)。 - 在Bootloader初始化时,复制Bootloader向量表到RAM,设置current_vector_table指向这个RAM副本。 - 当需要跳转到App时: - 禁用中断。 - 加载App的初始堆栈指针(从App的Flash起始地址)。 - 设置current_vector_table指向App的RAM向量表区域。 - 复制App的向量表(从App的Flash地址)到RAM。 - 跳转到App的复位地址(App Flash地址 + 4)。 2. **App阶段**: - App的复位处理函数执行,完成其他初始化。 - 启用中断。 在代码中: - 定义RAM向量表区域: ```c __attribute__((section(".ram_vector_table"))) uint32_t ram_vector_table[48]; ``` - 在启动文件或初始化代码中复制向量表。 对于CH579,链接器脚本需要调整。 现在,回答用户: - 结构清晰:先解释问题,然后给出解决方案步骤。 - 使用中文。 - 生成相关问题。 - 添加引用标识。 引用:基于用户提供的引用[^1]。 回答结构: 1. 介绍问题和挑战。 2. 解决方案概述。 3. 详细实现步骤。 4. 代码示例(伪代码或C/汇编)。 5. 注意事项。 6. 相关问题。</think>### CH579 M0内核Bootloader与App中断向量表切换实现 在CH579(基于ARM Cortex-M0内核)上实现Bootloader与App之间的中断向量表切换是可行的,但由于M0内核不支持VTOR(Vector Table Offset Register),中断向量表默认固定在Flash地址`0x00000000`处[^1]。直接修改Flash中的向量表是不可能的,因为Flash是只读的。因此,需要通过软件重定向机制,将向量表动态重映射到RAM中,并在运行时更新中断处理函数的地址。以下是清晰的实现步骤: --- ### 解决方案概述 核心思路: 1. **在RAM中创建动态向量表**:分配RAM区域存储中断服务程序(ISR)地址。 2. **使用统一中断代理函数**:Flash中的固定向量表所有条目指向同一个代理函数(trampoline)。 3. **动态跳转**:代理函数根据中断号和当前模式(Bootloader/App),跳转到RAM向量表中的实际ISR。 4. **模式切换**:在Bootloader跳转到App时,更新RAM向量表内容并切换全局指针。 优势: - 无需修改Flash内容,兼容M0内核限制。 - 中断响应延迟低(通常增加2-3个时钟周期)。 --- ### 详细实现步骤 #### 步骤1: 定义全局变量和RAM向量表 在Bootloader和App共享的代码区域(例如头文件)中定义: ```c // 定义向量表大小(CH579典型为16个异常+32个中断 = 48个条目) #define VECTOR_TABLE_SIZE 48 // 全局指针,指向当前活动的向量表(RAM地址) volatile uint32_t *current_vector_table = NULL; // RAM中的向量表存储区域(需4字节对齐) __attribute__((aligned(4))) uint32_t ram_vector_table[VECTOR_TABLE_SIZE]; ``` #### 步骤2: 实现统一中断代理函数(汇编/C混合) 在启动文件(如`startup_ch579.s`)或独立汇编模块中添加代理函数。该函数读取中断号并跳转到RAM向量表中的地址: ```assembly .section .text .global Default_Handler .thumb_func Default_Handler: ldr r0, =current_vector_table // 加载当前向量表基地址 ldr r0, [r0] mrs r1, ipsr // 读取IPSR寄存器获取中断号 lsls r1, r1, #2 // 中断号 × 4(每个条目4字节) add r0, r0, r1 // 计算RAM向量表条目地址 ldr r0, [r0] // 加载实际ISR地址 bx r0 // 跳转到实际ISR ``` 在C代码中声明该函数: ```c extern void Default_Handler(void); ``` #### 步骤3: 修改Flash向量表指向代理函数 在链接器脚本(如`CH579.ld`)中,确保Flash的向量表所有条目指向`Default_Handler`: ```ld /* 在Flash起始地址定义向量表 */ .vector_table : { . = ALIGN(4); KEEP(*(.vector_table)) // 保留向量表段 . = 0x00000000; // 固定在0地址 LONG(0x20010000); // 初始堆栈指针(示例值,需根据实际设置) LONG(Default_Handler); // 复位向量指向代理函数 LONG(Default_Handler); // NMI LONG(Default_Handler); // HardFault /* 重复所有48个条目指向Default_Handler */ } >FLASH ``` #### 步骤4: Bootloader初始化与跳转逻辑 在Bootloader代码中: ```c // Bootloader启动时初始化 void Bootloader_Init(void) { // 1. 复制Bootloader向量表到RAM memcpy(ram_vector_table, (void*)0x00000000, sizeof(ram_vector_table)); current_vector_table = ram_vector_table; // 指向Bootloader的向量表 // 2. 其他初始化(时钟、外设等) // ... } // 跳转到App的函数 void JumpToApp(uint32_t app_address) { // 1. 禁用中断 __disable_irq(); // 2. 检查App地址有效(可选) uint32_t app_sp = *(volatile uint32_t*)app_address; // App的初始堆栈指针 uint32_t app_reset = *(volatile uint32_t*)(app_address + 4); // App复位地址 // 3. 复制App向量表到RAM memcpy(ram_vector_table, (void*)app_address, VECTOR_TABLE_SIZE * 4); current_vector_table = ram_vector_table; // 切换指针到App向量表 // 4. 设置新堆栈指针并跳转 __set_MSP(app_sp); // 设置主堆栈指针 ((void(*)(void))app_reset)(); // 跳转到App复位函数 } ``` #### 步骤5: App初始化逻辑 在App代码中(需链接到偏移地址,如`0x00008000`): ```c // App的启动文件(如app_startup.c) void Reset_Handler(void) { // 1. 复制App向量表到RAM(确保与Bootloader共享的ram_vector_table已更新) // 注意:Bootloader跳转时已复制,此处可省略或验证 // 2. 设置向量表指针(可选,JumpToApp已设置) current_vector_table = ram_vector_table; // 3. 启用中断并运行App主函数 __enable_irq(); main(); } // 示例App中断服务程序 void TIMER0_IRQHandler(void) { // App的定时器中断处理 // ... } ``` #### 步骤6: 链接器脚本调整 确保Bootloader和App的Flash区域不重叠: - **Bootloader**: `0x00000000 - 0x00007FFF`(示例) - **App**: `0x00008000 - 0x0003FFFF`(示例) 在App的链接器脚本中: ```ld MEMORY { FLASH (rx) : ORIGIN = 0x00008000, LENGTH = 224K // App起始地址 RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K // 共享RAM区域 } ``` --- ### 关键注意事项 1. **中断禁用时机**: - 在切换向量表(`memcpy`和指针更新)前必须禁用中断,避免在更新过程中发生中断导致崩溃。 2. **RAM对齐要求**: - Cortex-M0要求向量表4字节对齐。使用`__attribute__((aligned(4)))`确保正确。 3. **全局变量共享**: - `current_vector_table`和`ram_vector_table`必须位于Bootloader和App共享的RAM区域(如`0x20000000`),避免被初始化覆盖。 4. **能影响**: - 代理函数增加约10-20个时钟周期的延迟,但对大多数应用可接受。 5. **CH579特定配置**: - 参考WCH官方SDK(如[ch58x_sdk](https://github.com/openwch/ch58x))中的启动文件,验证中断号定义[^1]。 --- ### 相关问题 1. **如何验证中断向量表切换是否成功?** 可通过调试器检查`current_vector_table`的值,或在代理函数中添加日志输出中断号。 2. **Bootloader跳转到App时,如何处理外设初始化冲突?** 需在跳转前重置外设(如关闭定时器、清除中断标志),避免App中状态混乱。 3. **在Cortex-M0上,是否有替代VTOR的硬件方案?** 否,M0内核无VTOR,必须通过软件重定向实现[^1]。更高内核(如M3/M4)可直接用VTOR寄存器。 4. **如何优化中断响应延迟?** 减少代理函数的指令数(使用纯汇编),或为高频中断(如SysTick)单独设置快速路径。 [^1]: CH579(M0内核)中断向量表偏移处理。通常情况下,为了产品后续的升级,程序都会分为两部分Bootloader+App,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的。在STM32F103 M3内核上是可以配置寄存器SCB->VTOR设置中断向量表偏移,CH579是M0内核,并不支持这样操作,中断向量表位置固定在(地址0x00000000)位置上。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值