Part 3: More than blinking
Adding MIDI
Adding the configuration of USART is straightforward.
...
; =========================================================
; Main routine
; =========================================================
main:
...
; ---------------------------------
; Configuration of USART for MIDI
; ---------------------------------
ldi r16, (1<<UDRE)
STORE UCSRA, r16 ; Asynchronous Normal mode, buffer empty
ldi r16, (1<<TXEN)
STORE UCSRB, r16 ; Enable the transmitter, but not the receiver
ldi r16, (1<<UCSZ1|1<<UCSZ0)
STORE UCSRC, r16 ; 8 bits, no parity, 1 stop bit
; Setting the speed to 31250 bauds
; UBRR = (fosc / 16 * BAUD) - 1
; = (8000000 / 16 * 31250) - 1
; = 15 = 0x0f
ldi r16, 0x00
STORE UBRRH, r16
ldi r16, 0x0f
STORE UBRRL, r16
After the loop, we check the LOOP LED; if the LED is off, we send a MIDI NOTE ON and light on the LED. Otherwise, we send a NOTE OFF and light off the LED.
...
oloop:
...
brne oloop
sbic PORTD, LOOP_LED ; Is LOOP_LED = 0 ?
rjmp load_note ; Yes: load a new note
rcall note_off ; Otherwise, send NOTE OFF
rjmp oloop
load_note:
ldi r20, 0x25 ; The note number
rcall note_on ; Call NOTE ON
rjmp oloop ; Restart the loop
This is the code send the 3 bytes of a note on MIDI message: NOTE ON, note number and velocity.
note_on: ; Send a note on
ldi r23, NOTE_ON ; Note on
rcall midi_send
mov r23, r20
rcall midi_send
ldi r23, 0x60 ; Mid velocity
rcall midi_send
ret
This is the code send the 3 bytes of a note off MIDI message: NOTE OFF, note number and null velocity.
note_off: ; Send a note off
ldi r23, NOTE_OFF ; Note off
rcall midi_send
mov r23, r20
rcall midi_send
ldi r23, 0x00 ; 0 velocity
rcall midi_send
ret
The midi_send function waits for buffer to be empty.
midi_send: ; Send the MIDI byte in r23, start the timer 1 and light up the MIDI activity LED
cli ; Disable interrupts
; ---- send the MIDI byte
wait_usart:
sbis UCSRA, UDRE ; Wait USART Data Register empty
rjmp wait_usart
STORE UDR, r23 ; Store the byte in r23 to the USART Data Register
; ---- Reset the TCNT1 counter
clr r16
clr r17
STORE TCNT1H, r16
STORE TCNT1L, r17
; ---- Start the timer by setting prescaler = 1024
ldi r16, (1<<WGM12)|(1<<CS10)|(1<<CS12)
STORE TCCR1B, r16
; ---- Light the MIDI LED on
LOAD r16, PORTD
sbr r16, (1<<MIDI_LED)
STORE PORTD, r16
sei ; Enable interrupts
ret
MIDI Codes
Channel messages
Hex | Binary | Description | Param #1 | Param #2 |
0x8x | 1000 cccc | Note-off | key # | velocity |
0x9x | 1001 cccc | Note-on | key # | velocity |
0xAx | 1010 cccc | Aftertouch | key # | value |
0xBx | 1011 cccc | Continuous Controller | Controller # | value |
0xCx | 1100 cccc | Patch change | Patch # |
0xDx | 1101 cccc | Channel Pressure | Value |
0xEx | 1110 cccc | Pitch bend | Value LSB | Value MSB |
System messages
Hex | Binary | Description | Param #1 | Param #2 |
0xF0 | 1111 0000 | Start of System exclusive message | Vendor ID | Variable length |
0xF1 | 1111 0001 | MIDI Time Code Quarter Frame |
Binary | Description |
0000 ffff | Piece 0: Frame number lsbits |
0001 000f | Piece 1: Frame number msbit |
0010 ssss | Piece 2: Second lsbits |
0011 00ss | Piece 3: Second msbits |
0100 mmmm | Piece 4: Minute lsbits |
0101 00mm | Piece 5: Minute msbits |
0110 hhhh | Piece 6: Hour lsbits |
0111 0rrh | Piece 7: Rate and hour msbit |
| |
0xF2 | 1111 0010 | Song Position Pointer | Value LSB | Value MSB |
0xF3 | 1111 0011 | Song Select | Song # | |
0xF4 | 1111 0100 | Reserved | | |
0xF5 | 1111 0101 | Reserved | | |
0xF6 | 1111 0110 | Tune Request | | |
0xF7 | 1111 0111 | End of System exclusive message | | |
Realtime messages
Hex | Binary | Description | Param #1 | Param #2 |
0xF8 | 1111 1000 | Timing Clock | | |
0xF9 | 1111 1001 | Reserved | | |
0xFA | 1111 1010 | Start | | |
0xFB | 1111 1011 | Continue | | |
0xFC | 1111 1100 | Stop | | |
0xFD | 1111 1101 | Reserved | | |
0xFE | 1111 1110 | Active Sensing | | |
0xFF | 1111 1111 | System Reset | | |
The issue
During the development of this project, a potential issue arose: the reset signal sent the Atari. In short, before sending data, the Atari pulses the /STROBE signal high to low 4 times; this is a signal for the CPLD of the original MO4 to reset. These pulses are sent at the speed of CPU (8 MHz); since my ATTiny runs also at 8MHz, I simply don't have enough time to process them, hence to detect a request to reset. I have to find an external way to process the pulses.
I came with 4 ranges of solution, but so far, no real solution. This will have to be developped.
- Using a faster processor with 4 USART such as the ATXmega range (ex: ATxmega128A4U)
- Using a Raspberry Pi Pico to dialog with the Atari, the ATTiny will be used only for the USART
- Using an external counter, such as SN74ALS576BN (quadruple D flip-flops, negative edge, with preset and clear). This solution is simple and cheap.
- Using an analog processing such as quickly discharging a capacitor on each pulse down, but slowly charging. The 4 successive discharges will drain the capacitor, which can be used to block a transistor.
Follow up:
Part 1
Part 2
Part 3
Part 4
Part 5
Part 6
.
Comments