condensed version-
nRF52 and twi dma, or any mcu with dma-
if you give the compiler information about the buffer you use for dma, in certain conditions it may think it knows all about that buffer-
bool isReady(){
uint8_t buffer[2] = { 0,0 }; //init buffer for some reason
twi.read(buffer); //twi uses dma, blocking call (and compiler inlined this code)
return (buffer[0] & 0x20); //return a status bit
}
//compiler 'knows' buffer[0] is 0, so probably optimizes away parts of it like the bit test
bool isReady(){
uint8_t buffer[2]; //do not init buffer (contents unknown)
twi.read(buffer); //twi uses dma, blocking call
return (buffer[0] & 0x20); //return a status bit
}
//compiler does not know what buffer[0] contains, so the code is used as expected
bool isReady(){
volatile uint8_t buffer[2] = { 0,0 }; //init buffer for some reason
twi.read(buffer); //twi uses dma, blocking call
return (buffer[0] & 0x20); //return a status bit
}
// volatile prevents the compiler from optimizing away the bit test, since it is required to read buffer[0]
I was doing the first version, and it was acting like the buffer was unused, but only in some optimizations. Adding a simple asm("nop") anywhere in that sequence made the compiler 'forget' what it knew about buffer and then the code would work. Making the buffer volatile works, or simply not initializing the buffer also works. Probably not a typical use of dma- a small buffer that is init, then used in the same function where the compiler then can optimize away its use, but its possible. So, just like isr's where it requires thought when using vars from outside the isr, dma also requires some thought even if its to a lesser extent.
I now use the second version, as I really didn't need to init the buffer. It was initially init so I could see if the buffer was changing during debugging, but didn't realize it was then the cause of problems.