.device ATmega88 .equ CLOCKSPEED = 8000000 ; R0 ; used for memory i/o ; R1 ; used for memory i/o .def ZERO = R2 ; static zero .def mem_3 = R3 ; active register used for addressing .def mem_2 = R4 ; memory .def mem_1 = R5 ; = R6 .def temp2 = R7 .def temp3 = R8 .def temp4 = R9 ; = R10 ; = R11 .def buffer = R12 .def packet_count_hi = R13 ; number of packets received/created .def packet_count_lo = R14 .def return = R15 ; used to return a value from select routines ;--- .def temp = R16 ; general purpose temporary .def temp1 = R17 ; general purpose temporary2 .def serial_byte = R18 ; serial send/receive byte .def spi_byte = R19 ; radio/flash send/receive byte .def radio_state = R20 ; 1 = transmiting, 0 = receiving .def packet_byte_count = R21 ; counter for packet i/o .def sum_lo = R22 ; used for checksums and other math tallies .def sum_hi = R23 .def announce_count = R24 ; = R25 ; XL = R26 (16-bit pairs) ; XH = R27 ; YL = R28 ; YH = R29 ; ZL = R30 ; ZH = R31 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; pins ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; button ; .equ button = PD3 .equ button_port = PORTD .equ button_pin = PIND .equ button_ddr = DDRD .macro init_button cbi button_ddr, button ; in, sbi button_port, button ; with pull-up .endmacro .macro skip_if_button_down sbic button_pin, button .endmacro .macro skip_if_button_up sbis button_pin, button .endmacro ; ; Speaker cutoff ; .equ speaker_cutoff = PC1 .equ speaker_cutoff_port = PORTC .equ speaker_cutoff_ddr = DDRC .macro speaker_off ; output 0 (ground) for off sbi speaker_cutoff_ddr, speaker_cutoff ; disable PWM for power savings ldi temp, 0b00000001 sts TCCR1A, temp .endmacro .macro speaker_on ; enable PWM: ; TCCR1A - Timer/Counter 1 Control Register A ; COM1A1, COM1A0, COM1B1, COM1B0, ---, ---, WGM11, WGM10 ; OC1A set on Compare Match (clear on top), Fast PWM 10-bit ldi temp, 0b11000001 sts TCCR1A, temp ; set hi-Z (input) for on cbi speaker_cutoff_ddr, speaker_cutoff .endmacro .macro init_speaker cbi speaker_cutoff_port, speaker_cutoff speaker_off .endmacro ; ; LEDs ; .equ ext_led = PD4 .equ ext_led_port = PORTD .equ ext_led_ddr = DDRD .macro ext_led_on cbi ext_led_port, ext_led .endmacro .macro ext_led_off sbi ext_led_port, ext_led .endmacro .equ int_led = PC2 .equ int_led_port = PORTC .equ int_led_ddr = DDRC .macro int_led_on cbi int_led_port, int_led .endmacro .macro int_led_off sbi int_led_port, int_led .endmacro .macro init_leds sbi int_led_ddr, int_led int_led_off sbi ext_led_ddr, ext_led ; source to light ext_led_off .endmacro ; ; Serial ; .equ serial_tx = PD1 ; .equ serial_tx_port = PORTD ; .equ serial_tx_ddr = DDRD ; .equ serial_rx = PD0 .equ serial_rx_port = PORTD .equ serial_rx_ddr = DDRD .equ serial_rx_pin = PIND .macro init_serial cbi serial_rx_ddr, serial_rx ; in cbi serial_rx_port, serial_rx sbi serial_tx_ddr, serial_tx ; out cbi serial_tx_port, serial_tx .endmacro ; ; SPI port (memory) ; .equ spi_port = PORTB .equ spi_mosi = PB3 .equ spi_miso = PB4 .equ spi_sck = PB5 .equ spi_cs = PB2 .macro init_spi sbi DDRB, spi_mosi ; out sbi DDRB, spi_sck ; out sbi DDRB, spi_cs ; out cbi DDRB, spi_miso ; in sbi spi_port, spi_sck cbi spi_port, spi_miso sbi spi_port, spi_mosi sbi spi_port, spi_cs .endmacro ; ; Radio ; ; IA4421_code.asm defs .equ IA_MOSI = PD5 .equ IA_MOSI_PORT = PORTD .equ IA_MOSI_DDR = DDRD .equ IA_SCK = PD6 .equ IA_SCK_PORT = PORTD .equ IA_SCK_DDR = DDRD .equ IA_NSEL = PD7 .equ IA_NSEL_PORT = PORTD .equ IA_NSEL_DDR = DDRD .equ IA_MISO = PB0 .equ IA_MISO_PORT = PORTB .equ IA_MISO_DDR = DDRB .equ IA_MISO_PIN = PINB ; Additional defs .equ IA_NIRQ = PD2 .equ IA_NIRQ_PORT = PORTD .equ IA_NIRQ_DDR = DDRD .equ IA_NIRQ_PIN = PIND .equ IA_NFFS = PC5 .equ IA_NFFS_PORT = PORTC .equ IA_NFFS_DDR = DDRC .macro IA_NFFS_ACTIVE cbi IA_NFFS_PORT, IA_NFFS .endmacro .macro IA_NFFS_INACTIVE sbi IA_NFFS_PORT, IA_NFFS .endmacro .macro skip_if_ia_nirq_active sbic IA_NIRQ_PIN, IA_NIRQ .endmacro .macro init_extra_radio_pins cbi IA_NIRQ_PORT, IA_NIRQ cbi IA_NIRQ_DDR, IA_NIRQ IA_NFFS_INACTIVE sbi IA_NFFS_DDR, IA_NFFS .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Addresses & opcodes ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Flash memory addresses: .equ flash_data_start = 0 .equ flash_data_length = 40000 .equ num_data_packets = flash_data_length / UDP_GENERAL_DATA_LENGTH .equ acceptable_bad_packets = num_data_packets / 10 .equ flash_prompt_start = 0x20000 .equ flash_prompt_length = 44999 .equ flash_confirm_start = 0x30000 .equ flash_confirm_length = 36678 .equ flash_cancelled_start = 0x40000 .equ flash_cancelled_length = 50000 .equ flash_sending_start = 0x50000 .equ flash_sending_length = 31788 ; eeprom addresses .equ eeprom_unique_id = 0x0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; code segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .cseg ; ; vector table ; rjmp reset ; Reset Handler .dw 0 ; IRQ0 Handler rcall button_handler ; IRQ1 Handler - button .dw 0 ; PCINT0 Handler .dw 0 ; PCINT1 Handler .dw 0 ; PCINT2 Handler .dw 0 ; Watchdog Timer Handler .dw 0 ; Timer2 Compare A Handler .dw 0 ; Timer2 Compare B Handler .dw 0 ; Timer2 Overflow Handler .dw 0 ; Timer1 Capture Handler .dw 0 ; Timer1 Compare A Handler .dw 0 ; Timer1 Compare B Handler .dw 0 ; Timer1 Overflow Handler .dw 0 ; Timer0 Compare A Handler .dw 0 ; Timer0 Compare B Handler .dw 0 ; Timer0 Overflow Handler .dw 0 ; SPI Transfer Complete Handler .dw 0 ; USART, RX Complete Handler .dw 0 ; USART, UDR Empty Handler .dw 0 ; USART, TX Complete Handler .dw 0 ; ADC Conversion Complete Handler .dw 0 ; EEPROM Ready Handler .dw 0 ; Analog Comparator Handler .dw 0 ; 2-wire Serial Interface Handler .dw 0 ; Store Program Memory Ready Handler .include "delays_8MHz.asm" .include "MEGA88_code.asm" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Serial ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "RS232_bauds.asm" .set RS232_BAUD_RATE = RS232_115200 .include "RS232.asm" putchar: rs232_putchar serial_tx_port, serial_tx, serial_byte ret getchar: rs232_getchar serial_rx_pin, serial_rx, serial_byte ret .macro putchar_i ldi serial_byte, @0 rcall putchar .endmacro .macro putchar_r mov serial_byte, @0 rcall putchar .endmacro .macro putchar_16 ldi serial_byte, high(@0) rcall putchar ldi serial_byte, low(@0) rcall putchar .endmacro .macro putchar_s lds serial_byte, @0 rcall putchar .endmacro ;;;;;;;;;;;;;;;;;;;;;;; ; memory ;;;;;;;;;;;;;;;;;;;;;;; .macro init_mem_pointer ldi temp, byte3(@0) mov mem_3, temp ldi temp, byte2(@0) mov mem_2, temp ldi temp, byte1(@0) mov mem_1, temp .endmacro .macro AT45DB_cs_hi sbi spi_port, spi_cs .endmacro .macro AT45DB_cs_lo cbi spi_port, spi_cs .endmacro .macro AT45DB_write_spi rcall hw_write_spi .endmacro .macro AT45DB_read_spi rcall hw_read_spi .endmacro .include "AT45DB.asm" ; ; Memory macros to abstract the AT45DB-specific calls. ; ; writing .macro open_memory_write rcall AT45DB_open_continuous_write .endmacro .macro pause_memory_write rcall AT45DB_pause_continuous_write rcall AT45DB_wait_for_ready .endmacro .macro memory_write rcall AT45DB_cont_write .endmacro .macro close_memory_write rcall AT45DB_close_continuous_write rcall AT45DB_wait_for_ready .endmacro ; reading .macro open_memory_read rcall AT45DB_wait_for_ready AT45DB_open_continuous_read mem_3, mem_2, mem_1 .endmacro .macro memory_read AT45DB_read_spi .endmacro .macro close_memory_read AT45DB_close_continuous_read .endmacro .macro open_memory_rewrite rcall AT45DB_open_rewrite .endmacro .macro memory_rewrite rcall AT45DB_rewrite .endmacro .macro close_memory_rewrite rcall AT45DB_close_rewrite .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Radio ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "IA4421_config.asm" .include "IA4421_code.asm" .include "IA4421_txreg.asm" .macro my_transmitter_startup_sequence ldi radio_state, 1 transmitter_startup_sequence .endmacro .macro my_transmitter_shutdown_sequence clr radio_state transmitter_shutdown_sequence .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SRAM and Flash addressing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "DPA_UDP.asm" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Radio program logic ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Receive a single packet to SRAM, calculating checksum ; as you go. Does not check for FFIT first; you must ; check prior to calling. ; radio_to_sram: ldi XL, low(UDP_RX_START) ldi XH, high(UDP_RX_START) mov sum_lo, ZERO mov sum_hi, ZERO ldi packet_byte_count, (UDP_LENGTH / 2) radio_to_sram_loop: rcall radio_receive_byte st X+, spi_byte mov R1, spi_byte ; high first rcall radio_receive_byte st X+, spi_byte mov R0, spi_byte ; then low add sum_lo, R0 adc sum_hi, R1 dec packet_byte_count brne radio_to_sram_loop ; packet done. reset FIFO buffer. rcall radio_reset_fifo ldi temp, 0xFF cp sum_lo, temp cpc sum_hi, temp brne radio_to_sram_bad ; good packet. sts UDP_RX_STATUS, ZERO ret radio_to_sram_bad: ; bad packet. ldi temp, 1 sts UDP_RX_STATUS, temp ret ; ; Sense - look for radio traffic. ; If none is seen, return. ; Otherwise, block and process any valid data we receive until ; the traffic is done. ; sense: skip_if_rssi_active ret ; if rssi is low, wait a bit, check again, exit. sense_data: skip_if_radio_has_data rjmp sense ; just check rssi again if there's no fifo data. ; radio has data! process it. rcall process_packet rjmp sense ; loop back ; ret ; never reached ; ; sense repeatedly X times (ensuring minimum amount of time for ; a sender to have initiated a packet). ; long_sense: ldi XL, 255 lds XH, 10 lds temp, unique_id add XH, temp sense_delay_loop: rcall sense sbiw XL, 1 brne sense_delay_loop ret very_long_sense: ldi XL, 255 ldi XH, 230 lds temp, unique_id add XH, temp very_long_sense_delay_loop: rcall sense sbiw XL, 1 brne very_long_sense_delay_loop ret process_packet: rcall radio_to_sram ; if checksum is invalid, ignore. lds R0, UDP_RX_STATUS cp R0, ZERO brne process_packet_return ; if it originated with ourself, ignore. lds temp, UNIQUE_ID lds R0, UDP_RX_SOURCE_ID cp temp, R0 breq process_packet_return ; check packet type. lds temp, UDP_RX_PACKET_TYPE ; announce packet? cpi temp, DATA_ANNOUNCE breq process_announce ; other than "announce", all packet types require ; matching current source/data ID. ; If source ID doesn't match, ignore lds R0, UDP_RX_SOURCE_ID lds R1, CURRENT_SOURCE_ID cp R0, R1 brne process_packet_return ; If data ID doesn't match, ignore. lds R0, UDP_RX_DATA_ID lds R1, CURRENT_DATA_ID cp R0, R1 brne process_packet_return ; data packet? cpi temp, DATA_PACKET breq process_data_packet ; execute packet? cpi temp, EXECUTE breq process_execute ; packet not recognized or invalid. process_packet_return: ret process_announce: ; Check that we haven't seen this before. lds R0, CURRENT_SOURCE_ID lds R1, CURRENT_DATA_ID lds temp, CURRENT_REQUEST_ID lds temp1, UDP_RX_SOURCE_ID lds temp2, UDP_RX_DATA_ID lds temp3, UDP_RX_REQUEST_ID ; if source, data, and request ID's match, ignore. cp R0, temp1 cpc R1, temp2 cpc temp, temp3 breq process_packet_return ; ignore ; if the source and data match but request ID's differ, ; skip clearing flash data. cp R0, temp1 cpc R1, temp2 breq process_announce_rebroadcast ; clear old recording and packet table rcall clear_flash_data rcall clear_packet_table process_announce_rebroadcast: ; make sure our pointers match. lds temp, UDP_RX_SOURCE_ID sts CURRENT_SOURCE_ID, temp lds temp, UDP_RX_DATA_ID sts CURRENT_DATA_ID, temp lds temp, UDP_RX_REQUEST_ID sts CURRENT_REQUEST_ID, temp ; rebroadcast rcall send_announcement ; wait for data packets rjmp clear_stack_and_receive ; will not return. process_data_packet: ; ignore if we have no bad packets left. lds R0, PACKET_TABLE_BAD_LO lds R1, PACKET_TABLE_BAD_HI cp R0, ZERO cpc R1, ZERO breq process_packet_return ; mark this packet as valid rcall mark_packet_valid ; put data in memory rcall sram_udp_rx_data_to_flash ret process_execute: ; ignore if we already executed this request. lds R0, LAST_REQUEST_EXECUTED lds R1, UDP_RX_REQUEST_ID cp R0, R1 breq process_packet_return ; execute! sts LAST_REQUEST_EXECUTED, R1 rcall playback rcall send_execute ; forward execute request! ; consider bad packet count lds temp, PACKET_TABLE_BAD_LO lds temp1, PACKET_TABLE_BAD_HI ; if more than acceptable_bad_packets are bad, don't forward data yet. cpi temp, acceptable_bad_packets cpc temp1, ZERO brsh process_execute_count ; num bad packets OK - forward data rcall send_data ret ; too many bad packets. recount. process_execute_count: rcall count_bad_packets ret ; whoa there! Calling this clears the stack; you will not return. clear_stack_and_receive: init_stack_pointer ; N.B.: fall-through receive: rcall process_packet rjmp receive ; never reached ret ; ; Transmit the packet stored in UDP_TX in sram. ; transmit_packet: my_transmitter_startup_sequence rcall continuous_transmit my_transmitter_shutdown_sequence ret ; ; Transmit without turning transmitter on/off. ; continuous_transmit: send_preamble ldi packet_byte_count, UDP_LENGTH ldi XL, low(UDP_TX_START) ldi XH, high(UDP_TX_START) continuous_transmit_loop: ld R0, X+ ia_send_tx_reg_r R0 dec packet_byte_count brne continuous_transmit_loop ret ; ; Clear stack, enable interrupts, transmit message. ; transmit: init_stack_pointer ; inform that we are sending ext_led_off rcall play_sending sei ; reenable interrupts for new recordings. ; take over data ID and source ID lds temp, UNIQUE_ID sts CURRENT_SOURCE_ID, temp lds temp, MY_DATA_ID inc temp sts MY_DATA_ID, temp sts CURRENT_DATA_ID, temp ; transmit message ; start with a powerful anouncement propagation. ; This is to prevent an old message from taking over a new one. ldi announce_count, 3 transmit_initial_announcement: rcall increment_execute_id rcall send_announcement rcall delay_1s rcall delay_1s rcall delay_1s dec announce_count brne transmit_initial_announcement rcall delay_1s rcall delay_1s rcall delay_1s rcall delay_1s rcall delay_1s ; Next, loop forever retransmitting the whole message. transmit_loop: rcall increment_execute_id rcall send_announcement rcall very_long_sense rcall very_long_sense rcall very_long_sense rcall send_data ; execute!! rcall send_execute rcall playback rcall very_long_sense rcall very_long_sense rcall very_long_sense rcall very_long_sense rcall very_long_sense rjmp transmit_loop send_announcement: build_packet_header DATA_ANNOUNCE rcall calculate_checksum ext_led_on rcall long_sense ext_led_off rcall transmit_packet ret send_data: rcall long_sense clr packet_count_hi clr packet_count_lo ldi ZL, low(num_data_packets) ldi ZH, high(num_data_packets) init_mem_pointer flash_data_start my_transmitter_startup_sequence ; for continuous transmission. send_data_loop: rcall build_data_packet ldi temp, UDP_GENERAL_DATA_LENGTH add mem_1, temp adc mem_2, ZERO adc mem_3, ZERO sbrc mem_2, 2 ext_led_on sbrs mem_2, 2 ext_led_off ldi temp, 1 add packet_count_lo, temp adc packet_count_hi, ZERO rcall continuous_transmit ;rcall transmit_packet rcall delay_5ms ; enough time for sender to process packet. rcall delay_5ms ; enough time for sender to process packet. rcall delay_5ms ; enough time for sender to process packet. sbiw ZL, 1 brne send_data_loop my_transmitter_shutdown_sequence ext_led_off ret increment_execute_id: lds R0, CURRENT_REQUEST_ID inc R0 sts CURRENT_REQUEST_ID, R0 ret send_execute: int_led_on build_packet_header EXECUTE rcall calculate_checksum rcall long_sense rcall transmit_packet int_led_off ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Memory management ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Clear the packet table (a table of data packet sequence numbers ; and received validity). ; clear_packet_table: stsi PACKET_TABLE_BAD_HI, high(num_data_packets) stsi PACKET_TABLE_BAD_LO, low(num_data_packets) ldi XL, low(PACKET_TABLE_PACKETS) ldi XH, high(PACKET_TABLE_PACKETS) ldi YL, low(num_data_packets) ldi YH, high(num_data_packets) ldi temp, PACKET_INVALID clear_packet_table_loop: st X+, temp sbiw YL, 1 brne clear_packet_table_loop ret ; ; mark RX udp packet as valid in packet table ; mark_packet_valid: ldi XL, low(PACKET_TABLE_PACKETS) ldi XH, high(PACKET_TABLE_PACKETS) lds R0, UDP_RX_DATA_SEQ_LO lds R1, UDP_RX_DATA_SEQ_HI add XL, R0 adc XH, R1 ldi temp, PACKET_VALID st X, temp ret ; ; Count number of positions marked "bad" in packet table. ; count_bad_packets: ldi XL, low(PACKET_TABLE_PACKETS) ldi XH, high(PACKET_TABLE_PACKETS) ldi YL, low(num_data_packets) ldi YH, high(num_data_packets) mov sum_lo, ZERO mov sum_hi, ZERO ldi temp1, 1 count_bad_packets_loop: sbiw YL, 1 ; return if we cross zero. brcs count_bad_packets_return ld temp, X+ cp temp, ZERO ; don't add if address is marked valid. breq count_bad_packets_loop ; increment otherwise. add sum_lo, temp1 adc sum_hi, ZERO rjmp count_bad_packets_loop count_bad_packets_return: sts PACKET_TABLE_BAD_HI, sum_hi sts PACKET_TABLE_BAD_LO, sum_lo ret debug_packet_table: ldi XL, low(PACKET_TABLE_PACKETS) ldi XH, high(PACKET_TABLE_PACKETS) ldi YL, low(num_data_packets) ldi YH, high(num_data_packets) debug_packet_table_loop: ld serial_byte, X+ rcall putchar sbiw YL, 1 brne debug_packet_table_loop ret ; ; Build a packet in SRAM representing data ; in flash memory location mem_3, mem_2, mem_1 ; with packet ID pointed to by packet_count_hi, packet_count_lo ; build_data_packet: ; build header lds temp, CURRENT_REQUEST_ID build_packet_header DATA_PACKET ; build data ; first two data bytes are sequence number. sts UDP_TX_DATA_SEQ_HI, packet_count_hi sts UDP_TX_DATA_SEQ_LO, packet_count_lo ; "general" data ldi packet_byte_count, UDP_GENERAL_DATA_LENGTH open_memory_read ldi YH, high(UDP_TX_GENERAL_DATA) ; reset SRAM data location ldi YL, low(UDP_TX_GENERAL_DATA) build_data_packet_loop: memory_read st Y+, spi_byte dec packet_byte_count brne build_data_packet_loop close_memory_read rcall calculate_checksum ret ; ; build the checksum for the TX packet in SRAM. ; ; Clobbers XL, XH, YL, YH, R0, packet_byte_count calculate_checksum: ; zero out total (to be stored in sum_hi:sum_lo) mov sum_hi, ZERO mov sum_lo, ZERO ; zero out checksum in packet, so it doesn't affect calculation sts UDP_TX_CHECKSUM_LO, ZERO sts UDP_TX_CHECKSUM_HI, ZERO ; initialize SRAM location ldi XH, high(UDP_TX_START) ldi XL, low(UDP_TX_START) ; add all the bytes in ram as 16-bit values (msb first). ldi packet_byte_count, UDP_LENGTH / 2 calc_checksum_loop: ; read ld R1, X+ ; high byte first ld R0, X+ ; then low byte ; add add sum_lo, R0 ; low byte first adc sum_hi, R1 ; then high byte ; loop dec packet_byte_count brne calc_checksum_loop ; complement to finish com sum_lo com sum_hi sts UDP_TX_CHECKSUM_LO, sum_lo sts UDP_TX_CHECKSUM_HI, sum_hi ret ; fill the mem_3,mem_2,mem_1 pointer with the memory location ; corresponding to the packet number of the packet in SRAM ; (based on UDP_DATA_LENGTH) sram_rx_to_data_pos: init_mem_pointer flash_data_start lds sum_lo, UDP_RX_DATA_SEQ_LO ; low byte of packet count lds sum_hi, UDP_RX_DATA_SEQ_HI ; high byte of packet count ; multiply packet count by data length for memory position. ; do 16x16 -> 16 algorithm, but with 8-bit operand (see avr apnote 201) ldi temp, UDP_GENERAL_DATA_LENGTH ; multiply low bytes mul temp, sum_lo ; al * bl ; add result to data pointer add mem_1, R0 adc mem_2, R1 adc mem_3, ZERO ; multiply high byte mul temp, sum_hi ; bh * al ; add result to high byte of pointer adc mem_2, R0 adc mem_3, ZERO ret ; write the data portion of the udp data packet to the appropriate ; location given by its . sram_udp_rx_data_to_flash: ; put data in place rcall sram_rx_to_data_pos open_memory_rewrite ldi XL, low(UDP_RX_GENERAL_DATA) ldi XH, high(UDP_RX_GENERAL_DATA) ldi packet_byte_count, UDP_GENERAL_DATA_LENGTH sram_udp_rx_data_to_flash_loop: ld spi_byte, X+ memory_rewrite dec packet_byte_count brne sram_udp_rx_data_to_flash_loop close_memory_rewrite ret clear_flash_data: init_mem_pointer flash_data_start ldi XH, high(flash_data_length) ldi XL, low(flash_data_length) open_memory_write clear_flash_data_loop: ldi spi_byte, 128 memory_write sbiw XL, 1 brne clear_flash_data_loop close_memory_write ret copy_rx_sram_to_tx_sram: ldi XL, low(UDP_RX_START) ldi XH, high(UDP_RX_START) ldi YL, low(UDP_TX_START) ldi YH, high(UDP_TX_START) ldi packet_byte_count, UDP_LENGTH copy_rx_sram_to_tx_sram_loop: ld R0, X+ st Y+, R0 dec packet_byte_count brne copy_rx_sram_to_tx_sram_loop ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Audio playback and recording ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Record ADC to memory. ; record: speaker_off ext_led_on init_mem_pointer flash_data_start open_memory_write ldi YH, high(flash_data_length) ldi YL, low(flash_data_length) mov sum_lo, ZERO mov sum_hi, ZERO record_loop: start_adc memory_write rcall wait_for_ADC lds spi_byte, ADCH ; store for next loop sbiw YL, 1 brne record_loop close_memory_write ext_led_off ret playback: init_mem_pointer flash_data_start ldi YL, low(flash_data_length) ldi YH, high(flash_data_length) ext_led_on rcall memory_to_speaker ext_led_off ret play_sending: init_mem_pointer flash_sending_start ldi YL, low(flash_sending_length) ldi YH, high(flash_sending_length) rcall memory_to_speaker ret play_cancelled: init_mem_pointer flash_cancelled_start ldi YL, low(flash_cancelled_length) ldi YH, high(flash_cancelled_length) rcall memory_to_speaker ret play_confirm: init_mem_pointer flash_confirm_start ldi YL, low(flash_confirm_length) ldi YH, high(flash_confirm_length) rcall memory_to_speaker ret play_prompt: init_mem_pointer flash_prompt_start ldi YL, low(flash_prompt_length) ldi YH, high(flash_prompt_length) rcall memory_to_speaker ret ; ; Expects mem_3, mem_2, mem_1 to be loaded with starting address, ; and YL-YH to be loaded with length. ; memory_to_speaker: speaker_on open_memory_read playback_loop: memory_read mov temp, ZERO sts OCR1AH, temp sts OCR1AL, spi_byte rcall delay_97us sbiw YL, 1 brne playback_loop speaker_off close_memory_read ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; UI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; button_handler: cli ; disable interupts sbrc radio_state, 0 rjmp button_handler_shut_down_transmitter rcall play_prompt rcall record rcall playback rcall play_confirm ldi ZL, 87 ; approximately ldi YH, 255 ; 5 seconds delay ldi YL, 255 ; button_handler_confirm_loop: skip_if_button_up rjmp transmit ; clears stack, doesn't return. sbrs ZL, 0 ext_led_on ; light on if ZL odd sbrc ZL, 0 ext_led_off ; light off if ZL even subi YL, 1 ; low byte sbc YH, ZERO; high byte sbc ZL, ZERO; byte 3 brne button_handler_confirm_loop ext_led_off rcall play_cancelled reti button_handler_shut_down_transmitter: my_transmitter_shutdown_sequence ; shut it down if it's on rjmp button_handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; main & init ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "trunk_test.asm" ; hardware tests and hardware one-time inits ;.include "trunk_debug.asm" ; debugging ; ; Send data directly from mic to speaker. ; mic_to_speaker: speaker_on mic_to_speaker_loop: start_adc mic_to_speaker_adloopup: lds temp, ADCSRA sbrc temp, ADSC ; loop until ADC complete rjmp mic_to_speaker_adloopup lds temp, 0 sts OCR1AH, temp lds temp, ADCH sts OCR1AL, temp rjmp mic_to_speaker_loop ; never reached speaker_off ret reset: init_stack_pointer init_clockspeed ; ; init pins ; init_leds init_button init_serial int_led_off init_speaker init_spi ; ; setup hardware SPI functionality ; init_hw_spi ; ; init INT1 interrupt - button ; ; External Interrupt Control Register A ; ---, ---, ---, ---, ISC11, ICS10, ISC01, ISC00 ldi temp, 0b00000000 ; trigger on low sts EICRA, temp ; ; External Interrupt Mask Register ; ---, ---, ---, ---, ---, ---, INT1, INT0 ldi temp, 0b00000010 ; int0 on out EIMSK, temp ; ; init ADC (microphone on ADC6) ; ; ADMUX - ADC Multiplexer Selection Register ; REFS1, REFS0, ADLAR, ---, MUX3, MUX2, MUX1, MUX0 ; 1.1v reference, left adjust, ADC6 ldi temp, 0b11100110 sts ADMUX, temp ; ADCSRA - ADC Control/Status Register A ; ADEN, ADSC, ADATE, ADIF, ADIE, ADPS1, ADPS2, ADPS0 ; enable ADC. Set prescaler to /64 ~ 125kHz (9kHz s.r) at 8MHz CPU ldi temp, 0b10000110 sts ADCSRA, temp ; ADCSRB - ADC Control/Status Register B (compare match settings) ; ---, ACME, ---, ---, ---, ADTS2, ADTS1, ADTS0 sts ADCSRB, ZERO ; ; init PWM (Speaker on OC1A - PB1) ; ; TCCR1A called by speaker_on macro ; TCCR1B - Timer/Counter 1 Control Register B ; ICNC1, ICES1, ---, WGM13, WGM12, CS12, CS11, CS10 ; set prescaler to 1 (no division) ldi temp, 0b00001001 sts TCCR1B, temp ; TCCR1C - Timer/Counter 1 Control Register C ; FOC1A, FOC1B, ---, ---, ---, ---, ---, --- ; not using; in PWM mode. ldi temp, 0b00000000 sts TCCR1C, temp ; PWM pin sbi DDRB, PB1 cbi PORTB, PB1 ; ; initialize registers ; clr ZERO clr buffer clr radio_state ; ; init playback timer - Timer0 ; ; TCCR0A - Timer/Counter 0 Control Register A ; COM0A1, COM0A0, COM0B1, COM0B0, ---, ---, WGM01, WGM00 ; Disconnect from pins, clear timer on match. ldi temp, 0b00000010 out TCCR0A, temp ; ; init SRAM ; ; clear SRAM ldi ZL, low(RAMSTART) ; start of memory ldi ZH, high(RAMSTART) ldi YL, low(RAMEND - RAMSTART) ; length of memory ldi YH, high(RAMEND - RAMSTART) init_sram_loop: st Z+, ZERO ; clear! sbiw YL, 1 brne init_sram_loop ; put eeprom unique ID in sram for more convenient access ldi ZH, high(eeprom_unique_id) ldi ZL, low(eeprom_unique_id) out EEARH, ZH out EEARL, ZL sbi EECR, EERE in temp, EEDR sts UNIQUE_ID, temp ; set starting data ID sts MY_DATA_ID, ZERO ; ; init radio ; init_extra_radio_pins initialize_IA4421 rcall ia_clear_it_latches ; clear POR interrupt ; ; External memory voodoo. ; ; Without this, the memory chip seems to not write consistently. AT45DB_deep_power_down AT45DB_resume_from_power_down ; ; enable interrupts ; ; ; hardware init tests ; ;rcall test_sequence ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Ye olde program! ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;rcall mic_to_speaker sei rcall receive main_loop: rjmp main_loop