打印
[STM8]

请教:STM8的定时中断处理端口问题

[复制链接]
7319|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
black.lu|  楼主 | 2010-8-25 17:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近STM8遇到比较郁闷的问题,现在有针对方案,但是还是想破头了想不明白。
型号是STM8S105K6:
1、定时中断TIM2溢出时间为1ms,主要用于数码管的显示(直接端口驱动,电流没有超过额定限制),用到端口PC2、PC3、PC4、PC5;
2、输出端口PC1,在控制函数中用到,控制函数在主循环体中。

问题是这样的:
只要我在中断外操作PC1,数码管显示就会偶尔出现毛刺(即位选或段码端口有误操作);将PC1放入中断中,现象消除,已经排除硬件问题和干扰问题。测试了PB端口,同样有这个问题,现在比较头疼,ST的工程师有给出一个信息是,ST采用流水式工艺,端口的输出会有1~2个指令的延时,所以我就猜测是不是这种延时被定时中断打乱,PC寄存器被重新定义时发生了错误?

大家有没有遇到过这种问题啊?
沙发
香水城| | 2010-8-25 17:32 | 只看该作者
把输出端口PC1和中断中输出端口PC2、PC3、PC4、PC5的操作语句写出来看看。

使用特权

评论回复
板凳
black.lu|  楼主 | 2010-8-25 22:19 | 只看该作者
我的开发环境是IAR,为防止意外,我写了两个版本,
一个是包含iostm8.h,涉及的代码如下:
端口的宏定义:
#define IO_F  PC_ODR_ODR6 //O
#define IO_C  PC_ODR_ODR5 //O
#define IO_G  PC_ODR_ODR4 //O
#define IO_B  PC_ODR_ODR3 //O

#define OUT   PC_ODR_ODR1 //O

端口段码显示驱动:
void IO_Dis(uint8 buf)
{
uint8 dat;
dat=(buf&0xff);
IO_A=!((bit)(dat&0x01));
IO_B=!((bit)(dat&0x02));
IO_C=!((bit)(dat&0x04));
IO_D=!((bit)(dat&0x08));
IO_E=!((bit)(dat&0x10));
IO_F=!((bit)(dat&0x20));
IO_G=!((bit)(dat&0x40));
IO_P=!((bit)(dat&0x80));
}
上面的bit是宏定义了bool;
PC1的操作就是将OUT=1或OUT=0;

第二个是使用了库头文件
端口操作定义
#define PORTC_SET(pin,var)    GPIOC->ODR = (GPIOC->ODR&(~pin))|(var?pin:0)

#define B_A  0x20
#define B_D  0x10
#define B_E  0x08
……

显示驱动为:
void IO_Dis(uint8 buf)
{
uint8 dat;
dat=~(buf&0xff);

PORTB_SET(B_A,(dat&0x01));
PORTC_SET(B_B,(dat&0x02));
PORTC_SET(B_C,(dat&0x04));
PORTB_SET(B_D,(dat&0x08));
PORTB_SET(B_E,(dat&0x10));
PORTC_SET(B_F,(dat&0x20));
PORTC_SET(B_G,(dat&0x40));
}

显示驱动函数void IO_Dis(uint8 buf)是在定时中断中执行的,1ms溢出中断,OUT的操作在主函数中。


2# 香水城

使用特权

评论回复
地板
香水城| | 2010-8-25 22:38 | 只看该作者
请问PC_ODR_ODR6、PC_ODR_ODR5、PC_ODR_ODR5等是怎么定义的?对应怎样的操作?

使用特权

评论回复
5
black.lu|  楼主 | 2010-8-25 22:47 | 只看该作者
这个是在IAR自带的iostm8.h中已经定义了的,定义代码如下:
typedef struct
{
  unsigned char ODR0        : 1;
  unsigned char ODR1        : 1;
  unsigned char ODR2        : 1;
  unsigned char ODR3        : 1;
  unsigned char ODR4        : 1;
  unsigned char ODR5        : 1;
  unsigned char ODR6        : 1;
  unsigned char ODR7        : 1;
} __BITS_PC_ODR;

__IO_REG8_BIT(PC_ODR,      0x500A, __READ_WRITE, __BITS_PC_ODR);

#define PC_ODR_ODR0              PC_ODR_bit.ODR0
#define PC_ODR_ODR1              PC_ODR_bit.ODR1
#define PC_ODR_ODR2              PC_ODR_bit.ODR2
#define PC_ODR_ODR3              PC_ODR_bit.ODR3
#define PC_ODR_ODR4              PC_ODR_bit.ODR4
#define PC_ODR_ODR5              PC_ODR_bit.ODR5
#define PC_ODR_ODR6              PC_ODR_bit.ODR6
#define PC_ODR_ODR7              PC_ODR_bit.ODR7

4# 香水城

使用特权

评论回复
6
black.lu|  楼主 | 2010-8-26 11:21 | 只看该作者
本帖最后由 black.lu 于 2010-8-26 11:23 编辑

我昨天仿真下看了汇编的操作,循环外的对PC1端口操作的汇编指令

像这种没有对整个端口进行“=”赋值操作的语句,会不会有可能在ADD指令之后,OR指令之前被中断打断,地址0X00又被中断中的程序调用,导致了混乱?

我现在暂时操作,是在处理端口之前将定时中断关闭,在处理完成之后将中断开启,这是无奈的办法。大家有没有好的建议?

使用特权

评论回复
7
香水城| | 2010-8-26 12:28 | 只看该作者
实际上,按照3楼和5楼给出的定义,你对I/O口的操作都不是原子操作,即不是独立的赋值操作,而是读-修改-写操作;如果在读-修改-写操作的执行中途被中断,并插入另一个读-修改-写操作,显然被中断的操作结果将会出错。

STM8S的参考手册上,对这种需要原子操作的情况,建议直接使用BSET和BRST指令操作ODR寄存器,这样就可以避免“读-修改-写操作”,从而避免操作被中断的可能。

使用特权

评论回复
8
black.lu|  楼主 | 2010-8-26 13:15 | 只看该作者
谢谢香主的提醒,我按照你的做法已经实现了,这也提醒我在其他端口的处理上也采用这种方法,对了,汇编指令的设置位和清除位,PDF上面的指令有一个写错了
正确的是:
asm("BSET 0x500A,#1");//对端口PC1置1
asm("BRES 0x500A,#1");//对端口PC1置0

7# 香水城

使用特权

评论回复
9
香水城| | 2010-8-26 13:34 | 只看该作者
谢谢香主的提醒,我按照你的做法已经实现了,这也提醒我在其他端口的处理上也采用这种方法,对了,汇编指令的设置位和清除位,PDF上面的指令有一个写错了
正确的是:
asm("BSET 0x500A,#1");//对端口PC1置1
asm("BRE ...
black.lu 发表于 2010-8-26 13:15


哪个PDF?第几页?第几行?

使用特权

评论回复
10
black.lu|  楼主 | 2010-8-26 13:43 | 只看该作者
中文手册的85页,和英文手册的105页,都用BRST指令来端口输出清零,但是在英文版的文件(PM0040)上没有看到BRST指令,
位操作指令都是BRES,在PM0040文件的第70页,有相关说明。
9# 香水城

使用特权

评论回复
11
香水城| | 2010-8-26 14:15 | 只看该作者
中文手册的85页,和英文手册的105页,都用BRST指令来端口输出清零,但是在英文版的文件(PM0040)上没有看到BRST指令,
位操作指令都是BRES,在PM0040文件的第70页,有相关说明。
9# 香水城  ...
black.lu 发表于 2010-8-26 13:43


谢谢。

不记得哪个文件编号是PM0040,只见过PM0044;即使是PM0044,也不记得谁把它翻译成中文,请上载这个文档到这里让我看看。想问问这个PM0040的名称是什么?

使用特权

评论回复
12
black.lu|  楼主 | 2010-8-26 15:23 | 只看该作者
这个文件是英文版的
汇编指令.pdf (3.92 MB)
11# 香水城

使用特权

评论回复
13
香水城| | 2010-8-26 15:37 | 只看该作者
12楼这个是PM0044(STM8 CPU programming manual),而不是PM0040。

不过在RM0016(STM8S参考手册)中,在GPIO寄存器章节,确实把BRES指令写成了BRST,估计是笔误。

使用特权

评论回复
14
香水城| | 2010-8-26 16:59 | 只看该作者
这个很简单,比如你要对PC6置'1'又不想改变其它位,你会先读出GPIOC->ODR,再'或'上0x40,再写回结果;这个过程至少需要3条指令:
1.  LD A, 0x500A   ; 读出PC_ODR
2.   OR A, #0x40
3.   LD 0x500A, A   ; 写回PC_ODR

如果在执行完上述第1条或第2条指令后,执行第3条指令前发生中断,同时在中断中有对PC5的操作,比如中断前PC5=0,在中断中把PC5改为'1',当中断结束后,上述第3条指令又会把PC5改回'0',这不是就错了吗?!

使用特权

评论回复
15
black.lu|  楼主 | 2010-8-26 17:11 | 只看该作者
恩,理解了,看的挺直观的。其实就是最后那么一下,把之前保护现场的堆栈里面的值又全部取出来,对PC端口进行了原始的“正确”操作,反不知把中断处理PC的“功劳”给抹杀了。明白了,非常感谢!
15# 香水城

使用特权

评论回复
16
香水城| | 2010-8-26 17:16 | 只看该作者
如果用BSET和BRES指令,更改PC6只需一条指令,更改操作是不可被中断的,所以不会有冲突出现!

    BSET  0x500A, #6

使用特权

评论回复
17
CrazyST| | 2010-8-30 21:26 | 只看该作者
布尔变量的操作都不会被中断掉

使用特权

评论回复
18
香水城| | 2010-8-30 21:55 | 只看该作者
标准的C语言中没有布尔变量的类型,因此布尔变量的操作是否会被中断掉,要看具体编译器的实现。

使用特权

评论回复
19
McuPlayer| | 2010-8-30 22:10 | 只看该作者
编译器不保证一条C语言指令做为一个原子操作。
比如用8bit的MCU实现u32+u32的操作,中途有可能被打断,所以需要OS的设计者实现临界区或信号量等方式来辅助此类操作。

使用特权

评论回复
20
CrazyST| | 2010-8-31 17:49 | 只看该作者
受教了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:零起点,电子承载梦想,为生活debug!

0

主题

162

帖子

1

粉丝