Python 3 min read

Bits, hex and masks

Reference manuals describe registers as bit fields. In Python you read a field with a shift and a mask, change bits with |= and &= ~, and print values in hex or binary with format strings, exactly as you would in C.

The register value 0x1A5 is shifted right by 4, which moves bits 5:4 down to bits 1:0, giving 0x1A. ANDing with 0b11 keeps only those two bits, so MODE is 2.
Shift the field down, then mask off everything else.

Reading a bit field

Say a status register holds 0x1A5, and the manual says bit 0 is READY and bits 5:4 are MODE. Shift right until the field starts at bit 0, then AND with a mask as wide as the field: 0b11 for two bits. Format strings show the value the way the manual does: #06x is hex with the 0x prefix, padded to six characters, and 012b is twelve binary digits.

fields.pyPython
status = 0x1A5                  # read from a status registerready = status & 0x01           # bit 0mode = (status >> 4) & 0b11     # bits 5:4print(f"{status:#06x} {status:012b}")print("ready:", ready, "mode:", mode)
Output
0x01a5 000110100101
ready: 1 mode: 2

Setting and clearing bits

Writing works the same way as in firmware: OR sets a bit, AND with the inverted mask clears it. That makes Python a good place to work out the value you will write to a control register, before you put it into C.

ctrl.pyPython
ctrl = 0x01ctrl |= 1 << 3                  # set bit 3ctrl &= ~(1 << 0)               # clear bit 0print(f"{ctrl:#04x}", bin(ctrl))
Output
0x08 0b1000

hex() and bin() add the prefix but no padding, so 8 prints as 0x8. Format specs give you control: x for hex, b for binary, a width, and # for the prefix. Padding every value to the register's width keeps a column of readings lined up, so a bit that changes stands out in a long log.