本帖最后由 freebsder 于 2014-9-17 01:30 编辑
interrupt部分不是这里讨论的重点,就是一堆中断条件处理。pattern match印象中在其他厂的片子里还没有出现过,甚至lpcopen的例程里面也没有关于PM的示例。除了swm,sct有点意思之外,前两天发现这个有意思的小功能pattern match,可以简化gpio一些情况下的工作量,再一次用状态机简化了应用需要处理的代码量,节省单片机的处理资源,这功能虽小,但是很好用。
假设有两个键A,B,LED初始为亮,首先按A键,然后按B键,LED反转关闭,然后按住A键的同时按B键,LED再次反转打开。做过组合键的同学可能首先会想到定时,轮询,等工作,组合键越复杂,代码处理的逻辑状态相应也越多。
MP主要有三个寄存器,都很简单:
PMCTRL用来设置pm功能,同时也可以设置是否给arm core引发event事件;
PMSRC用来设置模式中的元素,也就是哪些GPIO输入用作模式匹配中的元素;
PMCFG用来设置模式,和GPIO的匹配条件。GPIO中断事件可以有8个源,在
LPC_INMUX->PINTSEL[7:0]中设置;而模式元素也有8个,在
PMSRC中分别是SRC0-SRC7,这里用
PINTSEL中设置的输入源的编号来设置模式元素;
PMCFG中对8个模式元素进行模式匹配条件的设置,分别为
CFG0-CFG7,匹配条件可以是:始终真,带有粘着的上升沿,带有粘着的下降沿,带有粘着的沿(上升或下降),高电平,低电平,始终假(可以用来取消某个模式),事件(不带有粘着的沿,上升或下降),同时
PMCFG设置子模式,用
DPTS0-6(7默认是模式终结) 表示在哪一个模式元素进行子模式终结,这个终结元素之前,一直到上一个子模式终结元素的所有模式元素进行AND逻辑,每一个子模式之间进行OR逻辑。一旦子模式中的所有模式元素都得到匹配,则引发一个中断,中断号就是终结元素的中断号,同时可能为arm core引发一个RxEv事件。
手册上明确的说应用层配合的话,可以实现更复杂的状态机。下面是以上AB按键的实例代码:
#include "board.h"
#include "chip.h"
#include
#define SEL_PMATCH_PI (0 << 0)
#define SEL_PMATCH_MP (1 << 0)
#define ENA_RXEV_DIS (0 << 1)
#define ENA_RXEV_EN (1 << 1)
STATIC INLINE void Chip_PININT_ConfigPinFunction(LPC_PIN_INT_T *pPININT, uint32_t func)
{
pPININT->PMCTRL |= func;
}
#define SRC(n) (n)
#define INPUT(n) (n)
STATIC INLINE void Chip_PININT_SetMPSRC(LPC_PIN_INT_T *pPININT, uint32_t src, uint32_t input)
{
int bit_offset = 8 + 3 * src;
uint32_t tmp = pPININT->PMSRC & (~(0x7 << bit_offset));
pPININT->PMSRC = tmp | ((input & 0x7) << bit_offset);
}
#define SLICE(n) (n)
STATIC INLINE void Chip_PININT_SetEndpoint(LPC_PIN_INT_T *pPININT, uint32_t slice)
{
pPININT->PMCFG |= 1 << slice;
}
STATIC INLINE void Chip_PININT_ClrEndpoint(LPC_PIN_INT_T *pPININT, uint32_t slice)
{
pPININT->PMCFG &= ~(1 << slice);
}
#define ALWAYS_TRUE 0
#define STICKY_RE 1
#define STICKY_FE 2
#define STICKY_RF 3
#define HIGH_LEVEL 4
#define LOW_LEVEL 5
#define ALWAYS_FALSE 6
#define PM_EVENT 7
STATIC INLINE void Chip_PININT_SetPinPattern(LPC_PIN_INT_T *pPININT, uint32_t slice, uint32_t val)
{
int bit_offset = 8 + 3 * slice;
uint32_t tmp = pPININT->PMCFG & (~(0x7 << bit_offset));
pPININT->PMCFG = tmp | ((val & 0x7) << bit_offset);
}
/**
* @brief Handle interrupt from GPIO pin or GPIO pin mapped to PININT
* @return Nothing
*/
void PIN_INT1_IRQHandler(void)
{
static int s = 0;
if (s == 1) {
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(0), STICKY_FE);
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(1), LOW_LEVEL);
s = 0;
} else {
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(0), LOW_LEVEL);
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(1), LOW_LEVEL);
s = 1;
}
Chip_PININT_ClearIntStatus(LPC_GPIO_PIN_INT, PININTCH(1));
Board_LED_Toggle(0);
}
/**
* @brief Main program body
* @return Does not return
*/
int main(void)
{
SystemCoreClockUpdate();
Board_Init();
Board_LED_Set(0, true);
/* Set pin back to GPIO (on some boards may have been changed to something
else by Board_Init()) */
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 17, (IOCON_DIGMODE_EN | IOCON_MODE_INACT) );
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 11, (IOCON_DIGMODE_EN | IOCON_MODE_INACT) );
/* Enable PININT clock */
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_PININT);
/* Initialize PININT driver */
Chip_PININT_Init(LPC_GPIO_PIN_INT);
/* Configure interrupt channel for the GPIO pin in INMUX block */
Chip_INMUX_PinIntSel(0, 0, 17);
Chip_INMUX_PinIntSel(1, 1, 11);
Chip_PININT_SetMPSRC(LPC_GPIO_PIN_INT, SLICE(0), INPUT(0));
Chip_PININT_SetMPSRC(LPC_GPIO_PIN_INT, SLICE(1), INPUT(1));
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(0), STICKY_FE);
Chip_PININT_SetPinPattern(LPC_GPIO_PIN_INT, SLICE(1), LOW_LEVEL);
Chip_PININT_SetEndpoint(LPC_GPIO_PIN_INT, SLICE(1));
Chip_PININT_ConfigPinFunction(LPC_GPIO_PIN_INT, SEL_PMATCH_MP | ENA_RXEV_DIS);
/* Enable interrupt in the NVIC */
NVIC_ClearPendingIRQ(PIN_INT0_IRQn);
NVIC_EnableIRQ(PIN_INT0_IRQn);
NVIC_ClearPendingIRQ(PIN_INT1_IRQn);
NVIC_EnableIRQ(PIN_INT1_IRQn);
/* Spin in a loop here. All the work is done in ISR. */
while (1) {
__WFI();
}
return 0;
}
除了前面初始化部分,在中断中用S简单模拟了一个两态的状态机,可以看出至少在一些情况下的按键处理已经无需应用再处理了,组合按键更多的时候,更能显示出工作量减小的优势。
有板子的同学可以试一下,SW1是A,SW2是B,视频之类的就不上了。
=============================
大部分同学要么用的IAR,要么用的Keil,也有朋友问到过gcc和lpcxpresso,借这里简单说一下lpcxpresso的使用,lpxso是一个基于eclipse的很方便的工具,不要钱的版本支持256kflash,对于大部分片子来说已经足够了。可能有人说eclipse启动太慢,又耗内存呃,我发现IAR启动也快不到哪里去,内存也不算小,最关键的是IDE环境一不小心就崩掉了(和谐来的吧)。
gcc很多做单片机的可能不熟悉,这货编译了几乎所有的linux内核、库,应用等,android核心,可能在优化上和对单片机特殊指令的支持上不如商业编译器及时,可现在同样也支持fpu等cortexm4里面的配置,发展很快,前两天装了个freescale的KDS,发现里面基本商业编译器能有的,这货也有了。
单片机项目里面个人只用IAR和GCC两种C编译器。
我用的板子是lpcxpresso lpc1549,它默认支持两种调试器,一种是CMSIS-DAP,一种是Redlink,还可以烧成mbed,不过默认的就前面两种。JP3断路的时候是CMSIS-DAP,短路的时候默认是boot模式,DFU工具用来进行固件烧录的,但是lpxso环境可以下载redlink进去。
首先第一步,短路JP3,上电。
第二步打开lpxso之后点击
中间的红靴子,选择redlink server,然后ok进行固件下载(sram,掉电就没了),
在调试选项中选择redlink
,其余的可以依据自己的项目来选择。这样工程就可以用redlink进行调试了。
第三步,
在项目的setting里面对编译器、汇编器、连接器进行配置,包括常见的目标代码架构,优化,路径,宏,等。注意里面的tool chain edit,这里可以选择不同的编译器来编项目,windows版本的lpxso里面我配置的有gcc,iar和keil三种编译器(当然.s文件需要换成不同编译器支持的文件)。
至于使用Eclipse的好处,这个仁者见仁吧,keil用的实在少,我只说说和IAR IDE的比较。
1、iar有eclipse插件,可以使用iar编译器对项目进行编译,c-spy进行调试,这样对编译器有要求的同学不用担心gcc满足不了需求。同样,keil也有eclipse插件,也可以用keil编译器和调试环境来开发,eclipse说来具备最大兼容性:不同项目配置成不同的编译器即可。
2、iar ide的代码感知是垃圾,eclipse毕竟是专业IDE,有强大的代码处理能力,包括代码感知,代码结构分析,重构,搜索,格式化,版本控制,等等,就凭代码感知一点就秒掉IAR的IDE环境。keil前天花了半个小时硬是没找到哪里有选项打开详细的编译、链接步骤和过程,可能人老眼花了吧。
eclipse同样也可以进行内存查看,寄存器查看,如图
是arm core的寄存器,
右边栏选择的是PINT也就是这里说的功能模块,下边青色的是模块中的寄存器。还有其他常用的比如条件监控,调试期表达式计算,内存搜索/匹配等等支持都很强大。
3、eclipse跨平台。这个可能只是用一个平台的人没有感觉,对于我个人来说windows反而用的最少,做服务最多的还是Unix环境,所以在lpxso里面加上了jlink支持(幸好segger做了win/linux/osx的平台支持),加上了freescale的ProcessorExpert,就可以在非windows环境开发freescale单片机和nxp单片机。至于unix下面做开发环境的优势,一堆小命令包括grep,find,od等很好很强大。
不过一些地方没有IAR,keil适合,就是在RTOS支持和栈溢出监控上,eclipse插件个人不会写,所以这里就不好说自己写插件支持可不可行了。
至于gcc具体的开发方式,和linux下面没有任何区别,都需要Makefile,乱七八糟的编译器选项,ld脚本等等,除了调试需要特殊工具(如redlink,jlink)支持之外。
另外需要说一点的是,CMSIS-DAP似乎在osx/linux下面有些许问题,USB口取电不够,不知道什么原因,我之前调试的时候得把所有USB外设全部拔掉并且用USB Hub外接电源供电才能驱动这个仿真器。然而redlink确是正常的。