Disabling interrupts will not prevent an I/O register that may change independently of the code sequence from changing.
Often where data consistency is required between two hardware registers that are larger than the architecture width, the hardware data sheet or reference manual will advise on how to read the data - usually by specifying the order in which the registers must be read to work with hardware mechanisms that make that "safe".
In other cases the method might be dictated by the nature of the registers and their function/behaviour. For example if you have two 32bit timer/counters, with the overflow of one triggering an increment of the other, to form a 64 bit counter, then clearly the high-order counter will only change when the low-order counter overflows. In that case you can simply read the low, then the high and repeat if the low has since wrapped :
uint32_t low_word = 0, high_word = 0;
do
{
low_word = *low_reg_addr ;
high_word = *high_reg_addr ;
} while( *low_reg_addr > low_word ) ; // If low has wrapped, re-read
uint64_t full_word = (uint64_t)high_word << 32 | low_word;
So if the low-order register not wrapped after the high-order register has been read, then the data must be consistent, and the loop exit. If it has wrapped, the data may not be consistent, and must be re-read.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…