We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hello,
I'm using the LPC2468 micro controller. I have a struct mapped to the I2C register and in the I2C interrupt I statically access that struct, and everything works fine.
I've since broken off into a separate function the processing part of the interrupt code and now the interrupt passes a reference to its register to that function (so I can use one function for the three I2C buses). And suddenly the code doesn't work. Just to make sure I didn't mess up anything when creating the new subroutine, I replaced all the by-reference register accesses with a static call, and it worked again. I also verified that the reference points to the correct address.
So, it seems that when accessing the register statically, it works, and when accessing by reference it doesn't work. I can't figure out why. Does anyone know what I'm doing wrong? Or is there some technical reason why this won't work?
Here's a code snippet:
struct i2c_regs { U32 I2CONSET; U32 I2STAT; U32 I2DAT; U32 I2ADR; U32 I2SCLH; U32 I2SCLL; U32 I2CONCLR; } __attribute__((packed)); struct i2c_data { volatile struct i2c_regs *regs; U8 len; U8 pos; U8 addr; char *buf; char *base; U8 state; OS_MUT lock; OS_SEM sem; }; static volatile struct i2c_regs i2c0_regs __attribute__((at(I2C0_BASE_ADDR))); static struct i2c_data i2c0; // i2c0.regs is properly initialized void i2c0_handler(void) __irq { IENABLE; i2c_master_handler(&i2c0); IDISABLE; VICVectAddr = 0; } static void i2c_master_handler(struct i2c_data *i2c) { int status; status = i2c->regs->I2STAT; switch(status) { case I2C_STATUS_START: #if 0 // Doesn't work i2c->regs->I2DAT = i2c->addr; i2c->pos = 0; i2c->buf = i2c->base; i2c->regs->I2CONSET = I2CONSET_AA; i2c->regs->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC; #else // Does work i2c0_regs.I2DAT = i2c->addr; i2c->pos = 0; i2c->buf = i2c->base; i2c0_regs.I2CONSET = I2CONSET_AA; i2c0_regs.I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC; #endif break; ... }
Thanks,
Jon
Yep, I call an init routine that sets it:
i2c0.regs = &i2c0_regs;
Does status = i2c->regs->I2STAT always work?
I don't see information telling the compiler about alignment of the data pointed to by regs .
The compiler cannot assume that the data is 32 bit aligned and must perform byte accesses to successive byte addresses of a 32 bit location.
Peripherals on cores that I would be familiar with would not like that.
There is probably some alignment attribute that you can apply, check your compiler manual.
Yes, that line works when the routine is statically accessing i2c_regs
I don't see information telling the compiler about alignment of the data pointed to by regs
That seemed to be the problem. Thanks. I changed the register struct to:
struct i2c_regs { U32 I2CONSET; U32 I2STAT; U32 I2DAT; U32 I2ADR; U32 I2SCLH; U32 I2SCLL; U32 I2CONCLR; } __attribute__((aligned(32), packed));
Interesting, thought, that the without the alignment pragma the code still worked when accessing statically. And as noted above, at least one read from a pointer worked, too.
The compiler is smart. When you used the pointer directly, the compiler could see the absolute definition and deduce the alignment.
The I2STAT access either failed partially or did not trig any destructive action by being accessed as multiple byte reads.
When debugging with uv3, it is possible to setup its behavior when facing non-aligned access etc: "Debug->Debug Settings".