PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bitdefinition mit fatalen Folgen



Siro
25.01.2016, 13:45
Hallo zusammen,
ich habe nach einem etwas merkwürdigem Fehler in meiner Software gesucht und bin dann fündig geworden.
Das Problem wollte ich Euch mal eben offen legen und vielleicht gibts da ja auch eine andere Lösung:

Meine gesamten headerfiles für die Prozessorregister schreibe ich generell selbst ;)

Definition des LPC1768 Timer Interrupt Registers
Schön ordentlich mit den Bits definiert


typedef volatile union
{
struct {
U32 MR0INT : 1; /* Interrupt flag for match channel 0 */
U32 MR1INT : 1; /* Interrupt flag for match channel 1 */
U32 MR2INT : 1; /* Interrupt flag for match channel 2 */
U32 MR3INT : 1; /* Interrupt flag for match channel 3 */
U32 CR0INT : 1; /* Interrupt flag for capture channel 0 event */
U32 CR1INT : 1; /* Interrupt flag for capture channel 0 event */
U32 reserved : 26; /* unused bits */
} bits;
U32 value;
} TIMER_IR_TypeDef;

#define LPC_T0IR (* (TIMER_IR_TypeDef *)(0x40004000))
#define LPC_T1IR (* (TIMER_IR_TypeDef *)(0x40008000))
#define LPC_T2IR (* (TIMER_IR_TypeDef *)(0x40090000))
#define LPC_T3IR (* (TIMER_IR_TypeDef *)(0x40094000))


/*---- die Interrupt Funktion (nur zum Verständis, ohne jegliche Funktion) ---*/
void TIMER0_IRQHandler(void)
{
if (LPC_T0IR.bits.CR0INT) /* if Capture 0 interrupt occurred */
{
LPC_T0IR.bits.CR0INT = 1; /* clear the correspond interrupt flag */
}

if (LPC_T0IR.bits.CR1INT) /* if Capture 1 interrupt occurred */
{
LPC_T0IR.bits.CR1INT = 1; /* clear the correspond interrupt flag */
}

}


sieht doch eigentlich okay aus, A B E R :
Der Compiler erzeugt bei Bitdefinitionen (bei meinem GNU Compiler) IMMER eine Read Modify Write Sequenz
Read : Register "komplett" lesen
Modify : entsprechendes Bit maskieren
Write : "komplettes" Register zurück schreiben

Die Funktionsweise des Interruptregisters ist aber wie folgt definiert:
Eine 1 signalisiert einen Interrupt.
Um dieses Interrupt Bit zu löschen, muss es per Software auf 1 gesetzt werden.
Klingt nicht nur merkwürdig funktioniert auch NICHT richtig, WEIL:

Wenn sich zusätzliche eine "1" beim Lesen im Register befindet, dann wird unweigerlich
auch dieses Interrupt Bit zurückgesetzt, obwahl man durch die Maskierung es ja eigentlich verhindern wollte.
Der Compiler liest das Register mit z.B. 2 Einsen, setzt das gewünschte Bit auf 1 und schreibt den kompletten Wert zurück ins Register.
Alle anderen Bits bleiben also erhalten. Durch das Zurückschreiben von vorher gesetzten Einsen werden nun diese Interruptflags auch gelöscht. :(

Fazit: Bei deisem Register darf auf keinen Fall eine Bitdefinition erfolgen.
Wir müssen im Interrrupt nur einen Wert schreiben (WRITE)
also wenn wir nur das CR0INT Bit löschen wollen, müssen wir das mit
Register = (1 << 4); tun.
Schade eigentlich, ich fand die Bitdefinition viel übersichtlicher....
Aber vielleicht geht das ja auch noch anders.
Siro


So sieht es ja auch noch vernünftig aus:


#define CPU_REG_32 *(volatile unsigned int*) /* points to an 32 Bit register */

#define TIMER0_IR CPU_REG_32 0x40004000

#define IR_MR0 (1 << 0)
#define IR_MR1 (1 << 1)
#define IR_MR2 (1 << 2)
#define IR_MR3 (1 << 3)
#define IR_CR0 (1 << 4)
#define IR_CR1 (1 << 5)


void TIMER0_IRQHandler(void)
{
if (TIMER0_IR & IR_CR0) /* Capture 0 interrupt occured */
{
TIMER0_IR = IR_CR0; /* clear the corresponing interrupt flag */
}

if (TIMER0_IR & IR_CR1) /* Capture 1 interrupt occured */
{
TIMER0_IR = IR_CR1; /* clear the corresponing interrupt flag */
}