STM8S之外部中断应用之长按键识别

详见博客:http://blog.csdn.net/freeape/article/details/46990181

STM8常用中断指令

  • 开总中断
    • _asm(“rim”);
  • 禁止中断
    • _asm(“sim”);
  • 进入停机模式
    • _asm(“halt”);
  • 中断返回
    • _asm(“iret”);
  • 等待中断
    • _asm(“wfi”);
  • 软件中断
    • _asm(“trap”);

      STM8S常用中断映射

中断映射表

如使用中断函数时,可以通过在上图中查找相对应的中断向量号,而中断函数的名字可以自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*	BASIC INTERRUPT VECTOR TABLE FOR STM8 devices
* Copyright (c) 2007 STMicroelectronics
*/


typedef void @far (*interrupt_handler_t)(void);

struct interrupt_vector {
unsigned char interrupt_instruction;
interrupt_handler_t interrupt_handler;
};

@far @interrupt void NonHandledInterrupt (void)
{

/* in order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction
*/

return;
}

extern void _stext(); /* startup routine */
extern @far @interrupt void EXTI2_Hand_Fun(void);
extern @far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void);


struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, NonHandledInterrupt}, /* trap */
{0x82, NonHandledInterrupt}, /* irq0 */
{0x82, NonHandledInterrupt}, /* irq1 */
{0x82, NonHandledInterrupt}, /* irq2 */
{0x82, NonHandledInterrupt}, /* irq3 */
{0x82, NonHandledInterrupt}, /* irq4 */
{0x82, EXTI2_Hand_Fun}, /* irq5 */
{0x82, NonHandledInterrupt}, /* irq6 */
{0x82, NonHandledInterrupt}, /* irq7 */
{0x82, NonHandledInterrupt}, /* irq8 */
{0x82, NonHandledInterrupt}, /* irq9 */
{0x82, NonHandledInterrupt}, /* irq10 */
{0x82, TIM1_UPD_OVF_TRG_BRK_IRQHandler}, /* irq11 */
{0x82, NonHandledInterrupt}, /* irq12 */
{0x82, NonHandledInterrupt}, /* irq13 */
{0x82, NonHandledInterrupt}, /* irq14 */
{0x82, NonHandledInterrupt}, /* irq15 */
{0x82, NonHandledInterrupt}, /* irq16 */
{0x82, NonHandledInterrupt}, /* irq17 */
{0x82, NonHandledInterrupt}, /* irq18 */
{0x82, NonHandledInterrupt}, /* irq19 */
{0x82, NonHandledInterrupt}, /* irq20 */
{0x82, NonHandledInterrupt}, /* irq21 */
{0x82, NonHandledInterrupt}, /* irq22 */
{0x82, NonHandledInterrupt}, /* irq23 */
{0x82, NonHandledInterrupt}, /* irq24 */
{0x82, NonHandledInterrupt}, /* irq25 */
{0x82, NonHandledInterrupt}, /* irq26 */
{0x82, NonHandledInterrupt}, /* irq27 */
{0x82, NonHandledInterrupt}, /* irq28 */
{0x82,


NonHandledInterrupt}, /* irq29 */
};

外部中断长按键识别相关配置

  STM8S为外部中断事件专门分配了五个中断向量:

  • PortA 口的5个引脚:PA[6:2]
  • PortB 口的8个引脚:PB[7:0]
  • PortC 口的8个引脚:PC[7:0]
  • PortD 口的7个引脚:PD[6:0]
  • PortE口的8个引脚:PE[7:0]

  PD7是最高优先级的中断源(TLI);

中断IO设置

  这里选用EXTI2(端口C外部中断)。那么需要将中断促发的IO(PC5)设置为上拉输入或中断上拉输入,悬浮输入的话很容易受干扰。

1
2
3
4
5
6
7
/*PC5设置为上拉输入*/
void Init_EXTI2_GPIO(void)
{

PC_DDR &= 0XDF;
PC_CR1 &= 0XDF;
PC_CR2 |= 0x20;
}

外部中断寄存器配置

CPU CC寄存器中断位:

  I1 I0不能直接写,只能通过开中断或关中断来写,上电默认是11;当用指令开中断时( _asm(“rim\n”);),为00;当发生中断时,由当前中断(ITC_SPRx)载入I[1:0],主要用于做中断优先级;退出中断自动清0;因此在写EXTI_CR1,需将ITC_SPRx配置成11,或加入禁中断指令 。

EXTI_CR1:

  配置促发方式;

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include<stm8s003f3p.h>

char keyFlag;
char keyPressStatus = 1;
unsigned int keyCount;

/*Output Pin*/
_Bool PD2 @PD_ODR:2;
_Bool PC7 @PC_ODR:7;
_Bool PC6 @PC_ODR:6;
_Bool PC3 @PC_ODR:3;
/*Input Pin*/
_Bool PC5 @PC_IDR:5;

/*电量指示灯*/
#define LED1 PD2
#define LED2 PC7
#define LED3 PC6
#define LED4 PC3
/*按键*/
#define KEY PC5


/*主时钟频率为8Mhz*/
void Init_CLK(void)
{

CLK_ICKR |= 0X01; //使能内部高速时钟 HSI
CLK_CKDIVR = 0x08; //16M内部RC经2分频后系统时钟为8M
while(!(CLK_ICKR&0x02)); //HSI准备就绪
CLK_SWR=0xE1; //HSI为主时钟源
}

void Init_LED(void)
{

/*LED 配置为推挽输出*/
PD_DDR |= 0X04; //PD2输出模式,0为输入模式
PD_CR1 |= 0X04; //PD2模拟开漏输出
PD_CR2 &= 0XFB; //PD2输出速度最大为2MHZ,CR1/CR2悬浮输入

PC_DDR |= 0XC8;
PC_CR1 |= 0XC8;
PC_CR2 &= 0X27;
}

/*PC5设置为上拉输入*/
void Init_EXTI2_GPIO(void)
{


PC_DDR &= 0XDF;
PC_CR1 &= 0XDF;
PC_CR2 |= 0X20;
}

void Init_EXTI2(void)
{

EXTI_CR1 |= 0x30; //上升沿和下降沿促发
}


void Init_TIM1(void)
{

TIM1_IER = 0x00;
TIM1_CR1 = 0x00;

TIM1_EGR |= 0x01;
TIM1_PSCRH = 199/256; // 8M系统时钟经预分频f=fck/(PSCR+1) TIM1 为16位分频器
TIM1_PSCRL = 199%256; // PSCR=0x1F3F,f=8M/(200)=40000Hz,每个计数周期1/40000ms

TIM1_CNTRH = 0x00;
TIM1_CNTRL = 0x00;

TIM1_ARRH = 400/256; // 自动重载寄存器ARR=400
TIM1_ARRL = 400%256; // 每记数400次产生一次中断,即10ms

TIM1_CR1 |= 0x81;
TIM1_IER |= 0x01;
}

unsigned int Key_Scan_Test(void)
{

unsigned int count = 0;
unsigned char keyMode;

if(0 == keyPressStatus)
{
keyFlag = 1;
while(!keyPressStatus);
keyFlag = 0;
count = keyCount;
keyCount = 0;
}
else
{
count = 0;
}
/*10ms * 150 = 1.5s*/
if(count >= 150)keyMode = 2; //长按
else if(count >= 4)keyMode = 1; //短按
else keyMode = 0; //抖动

return keyMode;
}

main()
{
char keynum = 0;

_asm("sim");
Init_CLK();
Init_LED();
Init_EXTI2_GPIO();
Init_EXTI2();
Init_TIM1();
_asm("rim");
while (1)
{
keynum = Key_Scan_Test();
if(1 == keynum)LED3 = ~LED3;
if(2 == keynum)LED4 = ~LED4;
}
}

@far @interrupt void EXTI2_Hand_Fun(void)
{

keyPressStatus = !keyPressStatus;
LED1 = ~LED1;
}

@far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{

static unsigned int i = 0;

TIM1_SR1 &= ~(0X01);

++i;
if(50 == i)
{
LED2 = ~LED2;
i = 0;
}

/*Within Key Press Hand*/
if(1 == keyFlag)
{
++keyCount;
}
}
注意:
中断向量需声明,在stm8_interrupt_vector.c中加入:
extern @far @interrupt void EXTI2_Hand_Fun(void);
extern @far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void);
{0x82, EXTI2_Hand_Fun}, /* irq5  */
{0x82, TIM1_UPD_OVF_TRG_BRK_IRQHandler}, /* irq11 */

  另参见不用外部中断长按键识别:不用外部中断识别长按键