一、APP中的中断函数
1、本来以为经过前3篇文章的说明,stm32运行APP的功能已经实现了,但是在今天编写app代码的时候总感觉差点什么,由于需要写一个支持串口收发数据功能的app,发现不能实现接收串口数据,因为app不能定义中断服务函数(中断服务函数全部弱定义在stm32_startup.s文件中),想到安卓手机app开发有一种叫做监听的做法,监听到某事件发生是自动执行一段代码,本文将实现类似功能。
二、系统工程的修改
新建 irq_vector.c文件,编写以下代码:
#include "irq_vector.h"
#include "base.h"
#include "mymem.h"
#include "rthw.h"
extern int __Vectors;
extern int __Vectors_Size;
static void *g_base=0;
static void **g_irq_vector=0;
//此函数用于在系统初始化后重定位中断向量表
//把中断向量表定位在内存中才可以在运行时动态修改
void irq_vector_init(void)
{
if(g_irq_vector==0)
{
//向量表必须0x200字节对齐,这里申请大于0x200字节的内存,然后所申请的内存一定有0x200对齐的部分
g_base=mymalloc((int)&__Vectors_Size+0x200);
g_irq_vector=(void **)(((int)g_base+0x200)&0xfffffe00);
mymemcpy(g_irq_vector,(void *)&__Vectors,(int)&__Vectors_Size);
rt_base_t level=rt_hw_interrupt_disable();
NVIC_SetVectorTable(NVIC_VectTab_RAM,(u32)g_irq_vector-NVIC_VectTab_RAM);
rt_hw_interrupt_enable(level);
}
}
//设置中断函数,同时返回旧的中断函数,irq_fun==0时不做修改
void *irq_vector_set_irq(int irq_num,void *irq_fun)
{
void *ret=0;
irq_num+=16;//前16个中断是内核中断,中断号为负数
rt_base_t level=rt_hw_interrupt_disable();
ret=g_irq_vector[irq_num];
if(irq_fun)
g_irq_vector[irq_num]=irq_fun;
rt_hw_interrupt_enable(level);
return ret;
}
其中 irq_vector_init 函数用于重定位中断向量表,并把以前中断向量表的内容复制出来,这个函数越早调用越好,因为要使用内存分配,所以我把他安排在内存初始化之后:
/**
* This function will initial your board.
*/
void rt_hw_board_init()
{
_SysTick_Config (SystemCoreClock/1000);
mymem_init();
irq_vector_init();//重定位中断向量表
Touch_Init();
USART3_Init();
RANDOM_Init();
USBD_InitAsVcp ( );
}
irq_vector_set_irq 函数用来修改中断函数,相当于注册监听事件,当中断事件发生时,函数自动执行。
三、APP工程的修改
找到上篇文章中的app示例程序,做如下修改:
https://blog.youkuaiyun.com/Ranchaun/article/details/108394101
//串口中断服务函数
void usart1_irq(void)
{
u8 res=0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
res=USART_ReceiveData(USART1);
USART_SendData( USART1,res ); // 原样返回串口收到的数据
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完毕
}
}
//打开或关闭串口接收中断
void usart1_irq_power(int power)
{
NVIC_InitTypeDef NVIC_InitStructure;
if(power)
{
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
else
{
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
}
}
WIN_DemoStruct *WIN_CreatDemo (WIN_WindowStruct *base,
void (*msgLoop)(struct _WIN_WindowStruct *win,WIN_MsgStruct *msg),
int x,int y,int x_size,int y_size)
{
...
//省略无关代码
...
//窗口关闭时需要关闭串口中断,这里重新设置窗口销毁函数
((WIN_WindowStruct *)ret)->deleteWindow=(WIN_WindowStruct *))Demo_Delete;
//重设串口中断服务函数
ret->uart_irq=irq_vector_set_irq(USART1_IRQn,usart1_irq);
//打开串口中断
usart1_irq_power(1);
}
}
return ret;
}
//窗口销毁时关闭串口中断并设置为以前的中断服务函数
void Demo_Delete(WIN_DemoStruct *axis)
{
irq_vector_set_irq(USART1_IRQn,axis->uart_irq);
usart1_irq_power(0);
//调用父类的销毁函数
WIN_DeleteWindow((WIN_WindowStruct *)axis);
}
//绘制函数
void Demo_DefaultPaint (WIN_DemoStruct *axis)
{
...
//省略无关代码
...
char *txt=mymalloc(128);
int font=WIN_SetFontType(16);
//显示中断服务函数地址
sprintf(txt,"-原中断函数地址-%0X",(int)axis->uart_irq);
WIN_DrawTxtAt(txt,0,16);
sprintf(txt,"-新中断函数地址-%0X",(int)usart1_irq);
WIN_DrawTxtAt(txt,0,32);
WIN_SetFontType(font);
myfree(txt);
}
此APP的功能是打开后把系统的串口中断服务函数替换为app中定义的中断服务函数,打开串口接收中断,然后会原样返回收到的数据。
四、实验现象
运行串口助手,正常收到了数据:
在syscall.sym文件中查找地址0x08024DE5,发现正是串口1的中断服务函数地址:
本次调用时新的中断服务函数地址是0x2001A441,在我后台播放音乐(增加内存使用)之后,新中断函数的地址改为了0x200A8E1:
暂时就写这么多吧。