Mirror of CollapseOS
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

344 řádky
9.6KB

  1. ; This is a copy of my seg7multiplex main program, translated for zasm.
  2. ; The output of zasm was verified against avra's.
  3. ; 7-segments multiplexer for an ATtiny45
  4. ;
  5. ; Register usage
  6. ; R0: Digit on AFF1 (rightmost, QH on the SR)
  7. ; R1: Digit on AFF2 (QG on the SR)
  8. ; R2: Digit on AFF3 (QF on the SR)
  9. ; R3: Digit on AFF4 (leftmost, QE on the SR)
  10. ; R5: always zero
  11. ; R6: generic tmp value
  12. ; R16: generic tmp value
  13. ; R18: value to send to the SR. cleared at every SENDSR call
  14. ; in input mode, holds the input buffer
  15. ; R30: (low Z) current digit being refreshed. cycles from 0 to 3
  16. ;
  17. ; Flags on GPIOs
  18. ; GPIOR0 - bit 0: Whether we need to refresh the display
  19. ; GPIOR0 - bit 1: Set when INT_INT0 has received a new bit
  20. ; GPIOR0 - bit 2: The value of the new bit received
  21. ; GPIOR0 - bit 4: input mode enabled
  22. ; Notes on register usage
  23. ; R0 - R3: 4 low bits are for digit, 5th bit is for dot. other bits are unused.
  24. ;
  25. ; Notes on AFF1-4
  26. ; They are reversed (depending on how you see things...). They read right to
  27. ; left. That means that AFF1 is least significant, AFF4 is most.
  28. ;
  29. ; Input mode counter
  30. ; When in input mode, TIMER0_OVF, instead of setting the refresh flag, increases
  31. ; the counter. When it reaches 3, we timeout and consider input invalid.
  32. ;
  33. ; Input procedure
  34. ;
  35. ; Input starts at INT_INT0. What it does there is very simple: is sets up a flag
  36. ; telling it received something and conditionally sets another flag with the
  37. ; value of the received bit.
  38. ;
  39. ; While we do that, we have the input loop eagerly checking for that flag. When
  40. ; it triggers, it records the bit in R18. The way it does so is that it inits
  41. ; R18 at 1 (not 0), then for every bit, it left shifts R18, then adds the new
  42. ; bit. When the 6th bit of R18 is set, it means we have every bit we need, we
  43. ; can flush it into Z.
  44. ; Z points directly to R3, then R2, then R1, then R0. Because display refresh
  45. ; is disabled during input, it won't result in weird displays, and because
  46. ; partial numbers result in error display, then partial result won't lead to
  47. ; weird displays, just error displays.
  48. ;
  49. ; When input mode begins, we change Z to point to R3 (the first digit we
  50. ; receive) and we decrease the Z pointer after every digit we receive. When we
  51. ; receive the last bit of the last digit and that we see that R30 is 0, we know
  52. ; that the next (and last) digit is the checksum.
  53. .inc "avr.h"
  54. .inc "tn254585.h"
  55. .inc "tn45.h"
  56. ; pins
  57. .equ RCLK 0 ; on PORTB
  58. .equ SRCLK 3 ; on PORTB
  59. .equ SER_DP 4 ; on PORTB
  60. .equ INSER 1 ; on PORTB
  61. ; Let's begin!
  62. .org 0x0000
  63. RJMP MAIN
  64. RJMP INT_INT0
  65. RETI ; PCINT0
  66. RETI ; TIMER1_COMPA
  67. RETI ; TIMER1_OVF
  68. RJMP INT_TIMER0_OVF
  69. MAIN:
  70. LDI R16, RAMEND&0xff
  71. OUT SPL, R16
  72. LDI R16, RAMEND}8
  73. OUT SPH, R16
  74. SBI DDRB, RCLK
  75. SBI DDRB, SRCLK
  76. SBI DDRB, SER_DP
  77. ; we generally keep SER_DP high to avoid lighting DP
  78. SBI PORTB, SER_DP
  79. ; target delay: 600us. At 1Mhz, that's 75 ticks with a 1/8 prescaler.
  80. LDI R16, 0x02 ; CS01, 1/8 prescaler
  81. OUT TCCR0B, R16
  82. LDI R16, 0xb5 ; TOP - 75 ticks
  83. OUT TCNT0, R16
  84. ; Enable TIMER0_OVF
  85. IN R16, TIMSK
  86. ORI R16, 0x02 ; TOIE0
  87. OUT TIMSK, R16
  88. ; Generate interrupt on rising edge of INT0
  89. IN R16, MCUCR
  90. ORI R16, 0b00000011 ; ISC00 + ISC01
  91. OUT MCUCR, R16
  92. IN R16, GIMSK
  93. ORI R16, 0b01000000 ; INT0
  94. OUT GIMSK, R16
  95. ; we never use indirect addresses above 0xff through Z and never use
  96. ; R31 in other situations. We can set it once and forget about it.
  97. CLR R31 ; high Z
  98. ; put 4321 in R2-5
  99. CLR R30 ; low Z
  100. LDI R16, 0x04
  101. ST Z+, R16 ; 4
  102. DEC R16
  103. ST Z+, R16 ; 3
  104. DEC R16
  105. ST Z+, R16 ; 2
  106. DEC R16
  107. ORI R16, 0b00010000 ; DP
  108. ST Z, R16 ; 1
  109. CLR R30 ; replace Z to 0
  110. SEI
  111. LOOP:
  112. RCALL INPT_CHK ; verify that we shouldn't enter input mode
  113. SBIC GPIOR0, 0 ; refesh flag cleared? skip next
  114. RCALL RDISP
  115. RJMP LOOP
  116. ; ***** DISPLAY *****
  117. ; refresh display with current number
  118. RDISP:
  119. ; First things first: setup the timer for the next time
  120. LDI R16, 0xb5 ; TOP - 75 ticks
  121. OUT TCNT0, R16
  122. CBI GPIOR0, 0 ; Also, clear the refresh flag
  123. ; Let's begin with the display selector. We select one display at once
  124. ; (not ready for multi-display refresh operations yet). Let's decode our
  125. ; binary value from R30 into R16.
  126. MOV R6, R30
  127. INC R6 ; we need values 1-4, not 0-3
  128. LDI R16, 0x01
  129. RDISP1:
  130. DEC R6
  131. BREQ RDISP2 ; == 0? we're finished
  132. LSL R16
  133. RJMP RDISP1
  134. ; select a digit to display
  135. ; we do so in a clever way: our registers just happen to be in SRAM
  136. ; locations 0x00, 0x01, 0x02 and 0x03. Handy eh!
  137. RDISP2:
  138. LD R18, Z+ ; Indirect load of Z into R18 then increment
  139. CPI R30, 4
  140. BRCS RDISP3 ; lower than 4 ? don't reset
  141. CLR R30 ; not lower than 4? reset
  142. ; in the next step, we're going to join R18 and R16 together, but
  143. ; before we do, we have one thing to process: R18's 5th bit. If it's
  144. ; high, it means that DP is highlighted. We have to store this
  145. ; information in R6 and use it later. Also, we have to clear the higher
  146. ; bits of R18.
  147. RDISP3:
  148. SBRC R18, 4 ; 5th bit cleared? skip next
  149. INC R6 ; if set, then set R6 as well
  150. ANDI R18, 0xf ; clear higher bits
  151. ; Now we have our display selector in R16 and our digit to display in
  152. ; R18. We want it all in R18.
  153. SWAP R18 ; digit goes in high "nibble"
  154. OR R18, R16
  155. ; While we send value to the shift register, SER_DP will change.
  156. ; Because we want to avoid falsely lighting DP, we need to disable
  157. ; output (disable OE) while that happens. This is why we set RCLK,
  158. ; which is wired to OE too, HIGH (OE disabled) at the beginning of
  159. ; the SR operation.
  160. ;
  161. ; Because RCLK was low before, this triggers a "buffer clock" on
  162. ; the SR, but it doesn't matter because the value that was there
  163. ; before has just been invalidated.
  164. SBI PORTB, RCLK ; high
  165. RCALL SENDSR
  166. ; Flush out the buffer with RCLK
  167. CBI PORTB, RCLK ; OE enabled, but SR buffer isn't flushed
  168. NOP
  169. SBI PORTB, RCLK ; SR buffer flushed, OE disabled
  170. NOP
  171. CBI PORTB, RCLK ; OE enabled
  172. ; We're finished! Oh no wait, one last thing: should we highlight DP?
  173. ; If we should, then we should keep SER_DP low rather than high for this
  174. ; SR round.
  175. SBI PORTB, SER_DP ; SER_DP generally kept high
  176. SBRC R6, 0 ; R6 is cleared? skip DP set
  177. CBI PORTB, SER_DP ; SER_DP low highlight DP
  178. RET ; finished for real this time!
  179. ; send R18 to shift register.
  180. ; We send highest bits first so that QH is the MSB and QA is the LSB
  181. ; low bits (QD - QA) control display's power
  182. ; high bits (QH - QE) select the glyph
  183. SENDSR:
  184. LDI R16, 8 ; we will loop 8 times
  185. CBI PORTB, SER_DP ; low
  186. SBRC R18, 7 ; if latest bit isn't cleared, set SER_DP high
  187. SBI PORTB, SER_DP ; high
  188. RCALL TOGCP
  189. LSL R18 ; shift our data left
  190. DEC R16
  191. BRNE SENDSR+2 ; not zero yet? loop! (+2 to avoid reset)
  192. RET
  193. ; toggle SRCLK, waiting 1us between pin changes
  194. TOGCP:
  195. CBI PORTB, SRCLK ; low
  196. NOP ; At 1Mhz, this is enough for 1us
  197. SBI PORTB, SRCLK ; high
  198. RET
  199. ; ***** INPUT MODE *****
  200. ; check whether we should enter input mode and enter it if needed
  201. INPT_CHK:
  202. SBIS GPIOR0, 1 ; did we just trigger INT_INT0?
  203. RET ; no? return
  204. ; yes? continue in input mode
  205. ; Initialize input mode and start the loop
  206. INPT_BEGIN:
  207. SBI GPIOR0, 4 ; enable input mode
  208. CBI GPIOR0, 1 ; The first trigger was an empty one
  209. ; At 1/8 prescaler, a "full" counter overflow is 2048us. That sounds
  210. ; about right for an input timeout. So we co the easy route and simply
  211. ; clear TCNT0 whenever we want to reset the timer
  212. OUT TCNT0, R5 ; R5 == 0
  213. CBI GPIOR0, 0 ; clear refresh flag in case it was just set
  214. LDI R30, 0x04 ; make Z point on R3+1 (we use pre-decrement)
  215. LDI R18, 0x01 ; initialize input buffer
  216. ; loop in input mode. When in input mode, we don't refresh the display, we use
  217. ; all our processing power to process input.
  218. INPT_LOOP:
  219. RCALL INPT_READ
  220. ; Check whether we've reached timeout
  221. SBIC GPIOR0, 0 ; refesh flag cleared? skip next
  222. RCALL INPT_TIMEOUT
  223. SBIC GPIOR0, 4 ; input mode cleared? skip next, to INPT_END
  224. RJMP INPT_LOOP ; not cleared? loop
  225. INPT_END:
  226. ; We received all our date or reached timeout. let's go back in normal
  227. ; mode.
  228. CLR R30 ; Ensure Z isn't out of bounds
  229. SBI GPIOR0, 0 ; set refresh flag so we start refreshing now
  230. RET
  231. ; Read, if needed, the last received bit
  232. INPT_READ:
  233. SBIS GPIOR0, 1
  234. RET ; flag cleared? nothing to do
  235. ; Flag is set, we have to read
  236. CBI GPIOR0, 1 ; unset flag
  237. LSL R18
  238. SBIC GPIOR0, 2 ; data flag cleared? skip next
  239. INC R18
  240. ; Now, let's check if we have our 5 digits
  241. SBRC R18, 5 ; 6th bit cleared? nothing to do
  242. RCALL INPT_PUSH
  243. OUT TCNT0, R5 ; clear timeout counter
  244. RET
  245. ; Push the digit currently in R18 in Z and reset R18.
  246. INPT_PUSH:
  247. ANDI R18, 0b00011111 ; Remove 6th bit flag
  248. TST R30 ; is R30 zero?
  249. BREQ INPT_CHECKSUM ; yes? it means we're at checksum phase.
  250. ; Otherwise, its a regular digit push
  251. ST -Z, R18
  252. LDI R18, 0x01
  253. RET
  254. INPT_CHECKSUM:
  255. CBI GPIOR0, 4 ; clear input mode, whether we error or not
  256. MOV R16, R0
  257. ADD R16, R1
  258. ADD R16, R2
  259. ADD R16, R3
  260. ; only consider the first 5 bits of the checksum since we can't receive
  261. ; more. Otherwise, we couldn't possibly validate a value like 9999
  262. ANDI R16, 0b00011111
  263. CP R16, R18
  264. BRNE INPT_ERROR
  265. RET
  266. INPT_TIMEOUT:
  267. CBI GPIOR0, 4 ; timeout reached, clear input flag
  268. ; continue to INPT_ERROR
  269. INPT_ERROR:
  270. LDI R16, 0x0c ; some weird digit
  271. MOV R0, R16
  272. MOV R1, R16
  273. MOV R2, R16
  274. MOV R3, R16
  275. RET
  276. ; ***** INTERRUPTS *****
  277. ; Record received bit
  278. ; The main loop has to be fast enough to process that bit before we receive the
  279. ; next one!
  280. ; no SREG fiddling because no SREG-modifying instruction
  281. INT_INT0:
  282. CBI GPIOR0, 2 ; clear received data
  283. SBIC PINB, INSER ; INSER clear? skip next
  284. SBI GPIOR0, 2 ; INSER set? record this
  285. SBI GPIOR0, 1 ; indicate that we've received a bit
  286. RETI
  287. ; Set refresh flag whenever timer0 overflows
  288. ; no SREG fiddling because no SREG-modifying instruction
  289. INT_TIMER0_OVF:
  290. SBI GPIOR0, 0
  291. RETI