Introduce
在LPC5411x上有多个Flexcomm接口可供选择使用。每一个Flexcomm接口都可以提供一种外设功能,包括USART、SPI、I2C、I2S(不是所有flexcomm都支持I2S)。对于USART、SPI和I2S的数据传输可以使用FIFO,但I2C并不能使用Flexcomm的FIFO;
本博客主要分析当Flexcomm Interface6和Interface7选作I2S外设时:
如何注册flexcomm6,7的中断函数?如何将用户写的中断处理函数注册为实际中断入口函数会调用的函数?
如何将中断函数中需要处理的数据传给中断入口函数,使其成为中断入口函数里调用中断处理函数的参数?
程序分析
从FLEXCOMM的系统定义的中断函数入口着手
首先看fsl_flexcomm.c中函数定义
/*! @brief Pointers real ISRs installed by drivers for each instance. */
static flexcomm_Isr_t s_flexcommIsr[FSL_FEATURE_SOC_FLEXCOMM_COUNT];
/*! @brief Pointers to handles for each instance to provide context to interrupt routines */
static void *s_flexcommHandle[FSL_FEATURE_SOC_FLEXCOMM_COUNT];
#if (FSL_FEATURE_SOC_FLEXCOMM_COUNT > 6U)
void FLEXCOMM6_DriverIRQHandler(void)
{
s_flexcommIsr[6]((void *)s_flexcommBaseAddrs[6], s_flexcommHandle[6]);
}
#endif
fsl_flexcomm.h
/*! @brief Typedef for interrupt handler. */
typedef void (*flexcomm_isr_t)(void *base, void *handle);
FLEXCOMM6_DriverIRQHandler就是系统定义好的中断入口(startup_LPC54114_cm4.s 中定义)
重点其实就是分析
s_flexcommIsr[6]((void*)s_flexcommBaseAddrs[6], s_flexcommHandle[6]);
s_flexcommIsr[6]
这个静态局部数组其实就是指针数组。数组的每一个元素都是指向flexcomm_isr_t类型的函数的一个指针。/! @brief Pointers to real IRQ handlers installed by drivers for each instance. /
s_flexcommBaseAddrs[6]
s_flexcommBaseAddrs这个数组的每一个元素都是对应flexcomm接口的硬件寄存器数组首地址。/! @brief Array to map FLEXCOMM instance number to base address. /
s_flexcommHandle[6]
s_flexcommHandle这个数组的每一个元素都是一个void* 型的指针,用来给中断函数提供context参数。s_flexcommHandle是一个结构体数组,每一个元素都自带callback函数及callback函数的输入参数/! @brief Pointers to real IRQ handlers installed by drivers for each instance. /
s_flexcommIsr这个指针数组和s_flexcommHandle数组何时被赋值???
fsl_i2s.c
void I2S_TxTransferCreateHandle(I2S_Type *base, i2s_handle_t *handle, i2s_transfer_callback_t callback, void *userData)
{
/* Register IRQ handling */
FLEXCOMM_RegisterISR(base, (flexcomm_isr_t)I2S_TxHandleIRQ, handle);
}
void I2S_TxHandleIRQ(I2S_Type *base, i2s_handle_t *handle)
{
//此处添加中断处理程序
}
fsl_flexcomm.c
void FLEXCOMM_RegisterISR(void *base, flexcomm_isr_t handler, void *handle)
{
uint32_t instance;
/* Look up instance number */
instance = FLEXCOMM_GetInstance(base);
s_flexcommIsr[instance] = NULL;
s_flexcommHandle[instance] = handle;
s_flexcommIsr[instance] = handler;
}
主函数中调用I2S_TxTransferCreateHandle函数,I2S_TxTransferCreateHandle直接调用FLEXCOMM_RegisterISR这个flexcomm的中断注册函数,传入I2S的中断处理函数的函数指针I2S_TxHandleIRQ和中断函数的入口参数handle;FLEXCOMM_RegisterISR将I2S的中断处理函数的函数指针I2S_TxHandleIRQ和中断函数的入口参数handle分别赋值到两个数组对应元素中。
注:
handler:中断处理函数的地址
handle:要处理的数据(发送的数据)在中断入口函数中作为参数传给用户写的中断处理函数。
总结上面的理解
/* IRQ handler functions overloading weak symbols in the startup */
void FLEXCOMM0_DriverIRQHandler(void)
{
s_flexcommIsr[0]((void *)s_flexcommBaseAddrs[0], s_flexcommHandle[0]);
}
其实就是用一个注册函数,将具体的中断处理的函数的地址和中断函数中需要的数据放到s_flexcommIsr和s_flexcommHandle两个数组中而已。
然后系统的默认中断函数中,用s_flexcommIsr调用具体的处理函数。
回调:具体的中断处理函数(用户处理函数)是上层软件
系统提供的默认中断处理函数是下层软件
通过上层软件注册,下层软件调用,就实现了所谓回调。
Markdown Tip
<font face="黑体">我是黑体字</font>
<font face="微软雅黑">我是微软雅黑</font>
<font face="STCAIYUN">我是华文彩云</font>
<font color=#0099ff size=7 face="黑体">color=#0099ff size=72 face="黑体"</font>
<font color=#00ffff size=72>color=#00ffff</font>
<font color=gray size=72>color=gray</font>
<table><tr><td bgcolor=red>这里的背景色是:red</td></tr></table>
我是黑体字
我是微软雅黑
我是华文彩云
color=#0099ff size=72 face=”黑体”
color=#00ffff
color=gray
这里的背景色是:red |