; ; charlie.mic.45.asm ; ; Voice/unvoice classifier ; .include "tn45def.inc" ; ; definitions ; .equ tx_pin = PB2; transmit pin .equ led1 = PB0; .equ led2 = PB1; .equ nloop = 200 ; number of samples between framing ; ; registers ; .def bit_count = R16; bit counter .def temp = R17; temporary storage .def temp1 = R18; temporary storage .def txbyte = R19; data byte .def loop_count = R20 ; loop counter ; ; classifier registers ; .equ zc_thresh = 50 ; zero crossing threshold for unvoiced .equ mag_thresh = 800 ; magnitude threshold for voiced/unvoiced .def zc_count = R21 ; zero crossing count .def mag_l = R22 ; .def mag_h = R23 ; .def status = R24 ; .def min_sample_l = R25 ; .def min_sample_h = R26 ; .def max_sample_l = R27 ; .def max_sample_h = R28 ; .def zc_zero_l = R29; .def zc_zero_h = R30; .equ above_zero = 0 ; above zero flag position in status ; ; code segment ; .cseg .org 0 rjmp reset ; ; bit delay ; serial bit delay ; ;.equ b = 13 ; 9600 baud (clock /8) ;.equ b = 130 ; 9600 baud (clock /1) .equ b = 8 ; 115200 baud (clock /1) bit_delay: ldi temp, b bitloop: dec temp brne bitloop ret ; ; putchar ; assumes no line driver (doesn't invert bits) ; .equ sb = 1; number of stop bits putchar: ldi bit_count, 9+sb; 1+8+sb com txbyte; invert everything sec; set start bit putchar0: brcc putchar1; if carry set sbi PORTB, tx_pin; send a '0' rjmp putchar2; else putchar1: cbi PORTB, tx_pin ; send a '1' nop ; even out timing putchar2: rcall bit_delay; one bit delay rcall bit_delay lsr txbyte; get next bit dec bit_count; if not all bits sent brne putchar0; send next bit ret; ; ; char_delay ; delay between characters ; char_delay: ldi temp, 255 char_delay_loop: ldi temp1, 10 char_delay_loop1: dec temp1 brne char_delay_loop1 dec temp brne char_delay_loop ret ; ; main program ; init_mag_and_zc: ldi status, 0 ldi mag_l, 0 ldi mag_h, 0 ldi zc_count, 0 ldi min_sample_l, 255 ldi min_sample_h, 255 ldi max_sample_l, 0 ; initialize max with minimum 16-bit value ldi max_sample_h, 0 ret reset: ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) out CLKPR, temp out CLKPR, temp1 ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; init comm pin for output ; sbi PORTB, tx_pin sbi DDRB, tx_pin ; ; set up A/D ; sbi ADMUX, REFS2 ; use 2.56V reference sbi ADMUX, REFS1 ; " cbi ADMUX, REFS0 ; " cbi ADMUX, MUX3 ; 20x differential input on ADC2,3 sbi ADMUX, MUX2 ; sbi ADMUX, MUX1 ; sbi ADMUX, MUX0 ; cbi ADMUX, ADLAR ; right-adjust result sbi ADCSRA, ADEN ; enable A/D cbi ADCSRA, ADATE ; disable auto-trigger sbi ADCSRA, ADPS2 ; set prescaler for /16 cbi ADCSRA, ADPS1 ; " cbi ADCSRA, ADPS0 ; " ; ; initialize mag and zero crossing ; rcall init_mag_and_zc ldi zc_zero_l, 255 ; initialize zero at middle 16-bit value ldi zc_zero_h, 0 ; ; initialize LEDs ; sbi DDRB, led1 sbi DDRB, led2 sbi PORTB, led1 sbi PORTB, led2 ; ; main loop ; main_loop: ; ; light LEDs according to classification ; cpi mag_l, low(mag_thresh) ldi temp, high(mag_thresh) cpc mag_h, temp brlo leds_off brsh leds_on leds_off: sbi PORTB, led1 sbi PORTB, led2 rjmp leds_done leds_on: cpi zc_count, zc_thresh brlo led1_on brsh led2_on led1_on: sbi PORTB, led2 cbi PORTB, led1 rjmp leds_done led2_on: sbi PORTB, led1 cbi PORTB, led2 rjmp leds_done leds_done: ; ; send framing ; ldi txbyte, 1 rcall putchar rcall char_delay ldi txbyte, 2 rcall putchar rcall char_delay ldi txbyte, 3 rcall putchar rcall char_delay ldi txbyte, 4 rcall putchar rcall char_delay ; ; send classification from prev. frame ; ; zero crossing mov txbyte, zc_count rcall putchar ; magnitude mov txbyte, mag_l rcall putchar mov txbyte, mag_h rcall putchar ; "zero" definition mov txbyte, zc_zero_l rcall putchar mov txbyte, zc_zero_h rcall putchar ; ; recaluclate "zero" ; ldi zc_zero_h, 0 mov zc_zero_l, min_sample_l ; add min and max add zc_zero_l, max_sample_l adc zc_zero_h, min_sample_h adc zc_zero_h, max_sample_h lsr zc_zero_h ; divide by 2 ror zc_zero_l ; ; initialize zero counts and magnitude ; rcall init_mag_and_zc ; ; sample loop ; ldi loop_count, nloop sample_loop: ; ; read A/D ; sbi ADCSRA, ADSC ; start conversion adloopup: sbic ADCSRA, ADSC ; loop until complete rjmp adloopup in temp, ADCL ; load adc high byte in temp1, ADCH ; adc low byte ; ; update zero-crossings and magnitude ; ; dirty hack: skip first 20 sample values cpi loop_count, (nloop - 20) brsh end_update_zc_and_mag update_zc_and_mag: ; calculate zero crossing and conditionally add magnitude cp temp, zc_zero_l cpc temp1, zc_zero_h brlo adc_less_than_zero brsh adc_more_than_zero adc_less_than_zero: sbrc status, above_zero ; if prev was above zero rjmp inc_zc_below rjmp end_zc inc_zc_below: inc zc_count ldi status, 0 ; below zero rjmp end_zc adc_more_than_zero: ; if more than zero, add magnitude. add mag_l, temp adc mag_h, temp1 sub mag_l, zc_zero_l ; subtract zero - we only need part above zero sbc mag_h, zc_zero_h sbrs status, above_zero ; if prev was below zero rjmp inc_zc_above rjmp end_zc inc_zc_above: inc zc_count ldi status, 1 ; above zero rjmp end_zc end_zc: ; compare to min/max sample values to estimate zero cp temp, min_sample_l cpc temp1, min_sample_h brlo lower_than_min brsh compare_max lower_than_min: mov min_sample_l, temp mov min_sample_h, temp1 rjmp compare_max compare_max: cp temp, max_sample_l cpc temp1, max_sample_h brlo min_max_done brsh higher_than_max higher_than_max: mov max_sample_l, temp mov max_sample_h, temp1 rjmp min_max_done min_max_done: end_update_zc_and_mag: ; ; send conversion ; mov txbyte, temp ; low byte rcall putchar mov txbyte, temp1 ; hi byte rcall putchar ; ; loop ; dec loop_count brne sample_loop rjmp main_loop