Advertisement
creamygoat

Unified Port Bit Macros for 8-bit AVRs

Mar 10th, 2021 (edited)
2,581
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
MPASM 10.81 KB | None | 0 0
  1. #ifndef _UPB8MACROS8_INCLUDED
  2. #define _UFB8MACROS8_INCLUDED
  3.  
  4. ;-------------------------------------------------------------------------------
  5. ; Unified Port Bit macros for 8-bit AVR MCUs
  6. ; (For use with avr-gcc, an assembler with a C preprocessor)
  7. ;
  8. ; A Unified Port Bit (index) or UPB is a numerical code for a particular bit
  9. ; in a particular IO port. UPBs start at 0 for bit 0 of port A and end at 63
  10. ; for bit 7 of port H, if such a port existed. UPBs are not directly related
  11. ; to the pin numbers on the chip.
  12. ;
  13. ; Macros are provided to extract the PORT, PIN and DDR addresses and the bit
  14. ; numbers for any single UPB. Though these macros are convenient for dealing
  15. ; with single IO pins at a time, the Unified Port Mask macros are far more
  16. ; efficient for stuffing multiple bits at once into each Special Function
  17. ; Register for the IO ports.
  18. ;
  19. ; A Unified Port Mask can be assembled in the usual way by left-shifting bit
  20. ; 1s, bit 0s or multi-bit values by UPBs as if all the ports were accessed as
  21. ; a single 64-bit wide IO port. When it comes to dealing with the real 8-bit
  22. ; port registers, macros are provided to extract from a UPM the 8-bit field
  23. ; relevant to the SFR indicated by the macro name. Thus all twenty-four GPIO
  24. ; pins from PB0 to PD7 on an ATmega168 can have their data direction and
  25. ; pull-up or latch levels set with just six writes to SFRs.
  26. ;
  27. ; For example:
  28. ;   UPB_ACTIVITY_LED = UPB('C', 3)
  29. ; will set UPB_ACTIVITY_LED to 8*2 + 3 = 19
  30. ;
  31. ; Note that _single_ quotes are required for the C preprocessor to correctly
  32. ; recognise the port letter as a character and not a string, say.
  33. ;
  34. ; The SFR address (as an IO address or a memory address) is given by
  35. ;
  36. ;   PORT_REG_IO(u), PIN_REG_IO(u), DDR_REG_IO(u),
  37. ;   PORT_REG_MEM(u), PIN_REG_MEM(u) and DDR_REG_MEM(u)
  38. ;
  39. ; and the SFR register bit is given by
  40. ;
  41. ;   PBIT(u)
  42. ;
  43. ; (which merely extracts the lower three bits of the given UPB).
  44. ;
  45. ; For the SBI, CBI, SBIC and SBIS instructions, which take an IO address
  46. ; and a bit number, macros are provided to supply both arguments at once
  47. ; from one UPB number:
  48. ;
  49. ;   PORT_REGBIT_IO(upb), PIN_REGBIT_IO(upb), DDR_REGBIT_IO(upb)
  50. ;
  51. ; Remember that for AVRs, the PORTx symbols defined in the datasheets and
  52. ; the standard include files refer specifically to the output latch registers
  53. ; (which double as pull-up enable registers), not just the general concept of
  54. ; IO ports. (PINx registers are where the inputs are read.)
  55. ;
  56. ; A simple example which deals with single IO bits at a time:
  57. ;
  58. ;   UPB_MAGIC_BUTTON = UPB('B', 5)
  59. ;   UPB_SAUSAGE_READY = UPB('B', 0)
  60. ;   UPB_DOOM_LED = UPB('C', 7)
  61. ;           sbic    PIN_REGBIT_IO(UPB_MAGIC_BUTTON)
  62. ;           cbi     PORT_REGBIT_IO(UPB_DOOM_LED)
  63. ;           ; = cli    PPORT_REG_IO(UPB_DOOM_LED), PBIT(UPB_DOOM_LED)
  64. ;           in      r16, PORT_REG_IO(UPB_SAUSAGE_READY)
  65. ;           bst     PBIT(UPB_SAUSAGE_READY)
  66. ;
  67. ; A more complex example which deals with whole registers at a time:
  68. ;
  69. ;   UPB_BUTTON_MAGIC = UPB('B', 5) ; External pull-up provided
  70. ;   UPB_BUTTON_PIZZA = UPB('B', 4) ; Needs MCU weak pull-up enabled
  71. ;   UPB_BUTTON_BISCUITS = UPB('C', 0) ; Also needs MCU weak pull-ups.
  72. ;   UPB_I2C_SCL = UPB('B', 2) ; Latch high, toggle DDR to send
  73. ;   UPB_I2C_SDA = UPB('D', 6) ; Latch high, toggle DDR to send
  74. ;   UPB_DOOMSDAY = UPB('C', 1)
  75. ;   UPB_NOT_DISCONBOBULATE = UPB('D', 7)
  76. ;
  77. ;           Avoid setting reserved bits.
  78. ;           UPM_FULL_IO = 0xFFFFFF00
  79. ;           ; Set data direction.
  80. ;           DDR_UPM = ( \
  81. ;             (0 << UPB_BUTTON_MAGIC) | \
  82. ;             (0 << UPB_BUTTON_PIZZA) | \
  83. ;             (0 << UPB_BUTTON_BISCUITS) | \
  84. ;             (0 << UPB_I2C_SCL) | \
  85. ;             (0 << UPB_I2C_SCL) | \
  86. ;             (1 << UPB_DOOMSDAY) | \
  87. ;             (1 << UPB_NOT_DISCONBOBULATE) | \
  88. ;           0)
  89. ;           ; Set latches (for outputs) and pull-ups (for inputs).
  90. ;           PORT_UPM = UPM_FULL_IO & ( \
  91. ;             (0 << UPB_BUTTON_MAGIC) | \
  92. ;             (1 << UPB_BUTTON_PIZZA) | \
  93. ;             (1 << UPB_BUTTON_BISCUITS) | \
  94. ;             (1 << UPB_I2C_SCL) | \
  95. ;             (1 << UPB_I2C_SCL) | \
  96. ;             (0 << UPB_DOOMSDAY) | \
  97. ;             (1 << UPB_NOT_DISCONBULATE) | \
  98. ;           0)
  99. ;
  100. ;           ldi     r16, PBMASK_UPM(PORT_UPM)
  101. ;           ldi     r17, PBMASK_UPM(DDR_UPM)
  102. ;           out     _SFR_IO_ADDR(PORTB), r16
  103. ;           out     _SFR_IO_ADDR(DDRB), r17
  104. ;           ldi     r16, PCMASK_UPM(PORT_UPM)
  105. ;           ldi     r17, PCMASK_UPM(DDR_UPM)
  106. ;           out     _SFR_IO_ADDR(PORTC), r16
  107. ;           out     _SFR_IO_ADDR(DDRC), r17
  108. ;           ldi     r16, PDMASK_UPM(PORT_UPM)
  109. ;           ldi     r17, PDMASK_UPM(DDR_UPM)
  110. ;           out     _SFR_IO_ADDR(PORTD), r16
  111. ;           out     _SFR_IO_ADDR(DDRD), r17
  112. ;
  113. ; The SFR names must already be defined before this file is included so that
  114. ; the maps of valid PORTx, PINx and DDRx SFR addresses can be calculated.
  115. ;
  116. ;-------------------------------------------------------------------------------
  117.  
  118.  
  119. ; INTERNAL STUFF
  120.  
  121. ; Define addresses for ports which may or may not exist.
  122. ; These are used to construct port register address maps
  123. ; in symbols defined further below. (The maps do not
  124. ; consume MCU registers or memory.)
  125.  
  126. _DUMMY_PORT_ADDR = 0xFF
  127. _MAYBE_PORTA = _DUMMY_PORT_ADDR
  128. _MAYBE_PORTB = _DUMMY_PORT_ADDR
  129. _MAYBE_PORTC = _DUMMY_PORT_ADDR
  130. _MAYBE_PORTD = _DUMMY_PORT_ADDR
  131. _MAYBE_PORTE = _DUMMY_PORT_ADDR
  132. _MAYBE_PORTF = _DUMMY_PORT_ADDR
  133. _MAYBE_PORTG = _DUMMY_PORT_ADDR
  134. _MAYBE_PORTH = _DUMMY_PORT_ADDR
  135. _MAYBE_PINA = _DUMMY_PORT_ADDR
  136. _MAYBE_PINB = _DUMMY_PORT_ADDR
  137. _MAYBE_PINC = _DUMMY_PORT_ADDR
  138. _MAYBE_PIND = _DUMMY_PORT_ADDR
  139. _MAYBE_PINE = _DUMMY_PORT_ADDR
  140. _MAYBE_PINF = _DUMMY_PORT_ADDR
  141. _MAYBE_PING = _DUMMY_PORT_ADDR
  142. _MAYBE_PINH = _DUMMY_PORT_ADDR
  143. _MAYBE_DDRA = _DUMMY_PORT_ADDR
  144. _MAYBE_DDRB = _DUMMY_PORT_ADDR
  145. _MAYBE_DDRC = _DUMMY_PORT_ADDR
  146. _MAYBE_DDRD = _DUMMY_PORT_ADDR
  147. _MAYBE_DDRE = _DUMMY_PORT_ADDR
  148. _MAYBE_DDRF = _DUMMY_PORT_ADDR
  149. _MAYBE_DDRG = _DUMMY_PORT_ADDR
  150. _MAYBE_DDRH = _DUMMY_PORT_ADDR
  151.  
  152. #ifdef PORTA
  153. _MAYBE_PORTA = PORTA
  154. _MAYBE_PINA = PINA
  155. _MAYBE_DDRA = DDRA
  156. #endif
  157. #ifdef PORTB
  158. _MAYBE_PORTB = PORTB
  159. _MAYBE_PINB = PINB
  160. _MAYBE_DDRB = DDRB
  161. #endif
  162. #ifdef PORTC
  163. _MAYBE_PORTC = PORTC
  164. _MAYBE_PINC = PINC
  165. _MAYBE_DDRC = DDRC
  166. #endif
  167. #ifdef PORTD
  168. _MAYBE_PORTD = PORTD
  169. _MAYBE_PIND = PIND
  170. _MAYBE_DDRD = DDRD
  171. #endif
  172.  
  173. ; Port address maps as 64-bit words (for internal use)
  174. #define _PX_REG_MAP(a, b, c, d, e, f, g, h) (\
  175.   ((h) << 56) | ((g) << 48) | ((f) << 40) | ((e) << 32) | \
  176.   ((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
  177. #define _PORT_REG_MAP _PX_REG_MAP(\
  178.     _MAYBE_PORTA, _MAYBE_PORTB, _MAYBE_PORTC, _MAYBE_PORTD, \
  179.     _MAYBE_PORTE, _MAYBE_PORTF, _MAYBE_PORTG, _MAYBE_PORTH)
  180. #define _PIN_REG_MAP _PX_REG_MAP(\
  181.     _MAYBE_PINA, _MAYBE_PINB, _MAYBE_PINC, _MAYBE_PIND, \
  182.     _MAYBE_PINE, _MAYBE_PINF, _MAYBE_PING, _MAYBE_PINH)
  183. #define _DDR_REG_MAP _PX_REG_MAP(\
  184.     _MAYBE_DDRA, _MAYBE_DDRB, _MAYBE_DDRC, _MAYBE_DDRD, \
  185.     _MAYBE_DDRE, _MAYBE_DDRF, _MAYBE_DDRG, _MAYBE_DDRH)
  186.  
  187. ; Generic port address mapping function (for internal use)
  188. #define _PX_REG_AMBI(map, upb) (((map) >> (8 * ((upb) >> 3))) & 255)
  189.  
  190. ; Safe register mask for any single Unified Port Bit (for internal use)
  191. ; (If the UPB is not for the given port (specified by here by the UPB
  192. ; bit position of bit 0 of that port), zero will be returned.)
  193. #define _PXMASK(upb0, upb) (((255 << (upb0)) & (1 << (upb))) >> (upb0))
  194.  
  195. ; Safe register mask for any Unified Port Mask (for internal use)
  196. #define _PXMASK_UPM(upb0, upm) (((upm) >> (upb0)) & 255)
  197.  
  198. ; Unprotected UPB definition macros
  199. #define _UPB0_RAW(portletter) (((portletter) - 'A') * 8)
  200. #define _UPB_RAW(portletter, bit) (_UPB0_RAW(portletter) + (bit))
  201.  
  202. ; Port letter validation (returning 0 for False and 1 for True)
  203. #define _PORT_EXISTS(portletter) ( \
  204.     (((portletter) >= 'A') & ((portletter) <= 'H')) &\
  205.     (((_PORT_REG_MAP) >> _UPB0_RAW(portletter)) & 255 != (_DUMMY_PORT_ADDR)) &\
  206.     (((_PIN_REG_MAP) >> _UPB0_RAW(portletter)) & 255 != (_DUMMY_PORT_ADDR)) &\
  207.     (((_DDR_REG_MAP) >> _UPB0_RAW(portletter)) & 255 != (_DUMMY_PORT_ADDR)) &\
  208.     1)
  209.  
  210. ; For invalid port letters, the guarded UPB macro throws a division by zero
  211. ; to draw attention.
  212. #define _UPB_GUARDED(portletter, bit) ( \
  213.     ((1 / _PORT_EXISTS(portletter)) * _UPB_RAW((portletter), (bit))))
  214.  
  215.  
  216. ;-------------------------------------------------------------------------------
  217.  
  218.  
  219. ; UNIFIED PORT BITS MACROS
  220.  
  221. ; Unified Port Bit index assignment
  222. #define UPB(portletter, bit) _UPB_GUARDED((portletter), (bit))
  223.  
  224. ; Ambiguous IO/memory addresses
  225. #define PORT_REG_AMBI(upb) (_PX_REG_AMBI((_PORT_REG_MAP), (upb)))
  226. #define DDR_REG_AMBI(upb) (_PX_REG_AMBI((_DDR_REG_MAP), (upb)))
  227. #define PIN_REG_AMBI(upb) (_PX_REG_AMBI((_PIN_REG_MAP), (upb)))
  228.  
  229. ; IO addresses
  230. #define PORT_REG_IO(upb) (_SFR_IO_ADDR(PORT_REG_AMBI(upb)))
  231. #define DDR_REG_IO(upb) (_SFR_IO_ADDR(DDR_REG_AMBI(upb)))
  232. #define PIN_REG_IO(upb) (_SFR_IO_ADDR(PIN_REG_AMBI(upb)))
  233.  
  234. ; Memory addresses
  235. #define PORT_REG_MEM(upb) (_SFR_MEM_ADDR(PORT_REG_AMBI(upb)))
  236. #define DDR_REG_MEM(upb) (_SFR_MEM_ADDR(DDR_REG_AMBI(upb)))
  237. #define PIN_REG_MEM(upb) (_SFR_MEM_ADDR(PIN_REG_AMBI(upb)))
  238.  
  239. ; Register bit index (the lower three bits) of a Unified Port Bit index
  240. #define PBIT(upb) ((upb) & 7)
  241.  
  242. ; IO address and bit pairs for sbi/cbi and sbis/sbic
  243. #define PORT_REGBIT_IO(upb) PORT_REG_IO(upb), ((upb) & 7)
  244. #define DDR_REGBIT_IO(upb) DDR_REG_IO(upb), ((upb) & 7)
  245. #define PIN_REGBIT_IO(upb) PIN_REG_IO(upb), ((upb) & 7)
  246.  
  247. ; Safe port register masks for any single Unified Port Bit index
  248. ; (If the UPB bit is not for the given port, zero will be returned.)
  249. #define PAMASK(upb) (_PXMASK(UPB('A', 0), (upb)))
  250. #define PBMASK(upb) (_PXMASK(UPB('B', 0), (upb)))
  251. #define PCMASK(upb) (_PXMASK(UPB('C', 0), (upb)))
  252. #define PDMASK(upb) (_PXMASK(UPB('D', 0), (upb)))
  253. #define PEMASK(upb) (_PXMASK(UPB('E', 0), (upb)))
  254. #define PFMASK(upb) (_PXMASK(UPB('F', 0), (upb)))
  255. #define PGMASK(upb) (_PXMASK(UPB('G', 0), (upb)))
  256. #define PHMASK(upb) (_PXMASK(UPB('H', 0), (upb)))
  257.  
  258. ; Safe register masks for any Unified Port Mask
  259. ; (The resulting 8-bit mask only contains bits from the UPM
  260. ; belonging to the specific port.)
  261. #define PAMASK_UPM(upm) _PXMASK_UPM(UPB('A', 0), (upm))
  262. #define PBMASK_UPM(upm) _PXMASK_UPM(UPB('B', 0), (upm))
  263. #define PCMASK_UPM(upm) _PXMASK_UPM(UPB('C', 0), (upm))
  264. #define PDMASK_UPM(upm) _PXMASK_UPM(UPB('D', 0), (upm))
  265. #define PEMASK_UPM(upm) _PXMASK_UPM(UPB('E', 0), (upm))
  266. #define PFMASK_UPM(upm) _PXMASK_UPM(UPB('F', 0), (upm))
  267. #define PGMASK_UPM(upm) _PXMASK_UPM(UPB('G', 0), (upm))
  268. #define PHMASK_UPM(upm) _PXMASK_UPM(UPB('H', 0), (upm))
  269.  
  270. ; Return 1 if a port of the given letter exists, otherwise 0.
  271. #define PORT_EXISTS(portletter) (_PORT_EXISTS(portletter))
  272.  
  273.  
  274. ;-------------------------------------------------------------------------------
  275.  
  276. #endif /* UPB8MACROS8_INCLUDED */
  277.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement