Thanks in advance for anyone who can help me with this. So I got a copy of some code that works pretty well just using the USI as a UART for the attiny85. At the default 8 MHz internal oscillator, divide by 8, 1 MHz clock, I can get a good serial output at 2400 baud, 8n1. If I remove the divide fuse and bring the clock up to 8 MHz, I get a good 9600 baud, 8n1 serial output. Below is that working code:
#define F_CPU 1000000UL // Setting micro to 1MHz #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> // #define BAUDRATE 9600 // if running at 8 MHz #define BAUDRATE 2400 // if running at 1 MHz #define STOPBITS 1 extern uint8_t usiserial_send_available(); extern void usiserial_send_byte(uint8_t data); // If bit width in cpu cycles is greater than 255 then divide by 8 to fit in timer // Calculate prescaler setting #define CYCLES_PER_BIT ( (F_CPU) / (BAUDRATE) ) #if (CYCLES_PER_BIT > 255) #define DIVISOR 8 #define CLOCKSELECT 2 #else #define DIVISOR 1 #define CLOCKSELECT 1 #endif #define FULL_BIT_TICKS ( (CYCLES_PER_BIT) / (DIVISOR) ) // USISerial send state variable enum USISERIAL_SEND_STATE { AVAILABLE, FIRST, SECOND }; static volatile uint8_t usiserial_send_state = AVAILABLE; static inline uint8_t usiserial_send_get_state(void) { return usiserial_send_state; } static inline void usiserial_send_set_state(enum USISERIAL_SEND_STATE state) { usiserial_send_state=state; } uint8_t usiserial_send_available() { return usiserial_send_get_state() == AVAILABLE; } // Transmit data persistent between USI OVF interrupts static volatile uint8_t usiserial_tx_data; static inline uint8_t usiserial_get_tx_data(void) { return usiserial_tx_data; } static inline void usiserial_set_tx_data(uint8_t tx_data) { usiserial_tx_data = tx_data; } static uint8_t reverse_byte (uint8_t x) { x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); return x; } void usiserial_send_byte(uint8_t data) { while (usiserial_send_get_state() != AVAILABLE) { // Spin until we finish sending previous packet }; usiserial_send_set_state(FIRST); usiserial_set_tx_data(reverse_byte(data)); // Configure Timer0 TCCR0A = 2<<WGM00; // CTC mode TCCR0B = CLOCKSELECT; // Set prescaler to clk or clk /8 GTCCR |= 1 << PSR0; // Reset prescaler OCR0A = FULL_BIT_TICKS; // Trigger every full bit width TCNT0 = 0; // Count up from 0 // Configure USI to send high, start bit and 6 bits of data // Start bit (low 0x00) // followed by first 7 bits of serialOutput USIDR = 0x00 | usiserial_get_tx_data() >> 1; // Enable USI Counter OVF interrupt (USIOIE). // Select three wire mode to ensure USI written to PB1 (USIWM0 and USIWM1) // Select Timer0 Compare match as USI Clock source. (USICS0, USICS1, and USICLK) USICR = (1<<USIOIE) | (0<<USIWM1) | (1<<USIWM0) | (0<<USICS1) | (1<<USICS0) | (0<<USICLK); DDRB |= (1<<PB1); // Configure USI_DO as output. USISR = 1<<USIOIF | (16 - 8); // Clear USI overflow interrupt flag and set USI counter to 8 } // USI overflow interrupt indicates we've sent a packet ISR (USI_OVF_vect) { if (usiserial_send_get_state() == FIRST) { usiserial_send_set_state(SECOND); // Send last 1 bit of data // and stop bits (high 0x7F) USIDR = usiserial_get_tx_data() << 7 | 0x7F; // Clear USI overflow interrupt flag (USIOIF) // and set USI counter to send last bit and stop bits USISR = 1<<USIOIF | (16 - (1 + (STOPBITS))); } else { PORTB |= 1 << PB1; // Ensure output is high DDRB |= (1<<PB1); // Configure USI_DO as output. USICR = 0; // Disable USI. USISR |= 1<<USIOIF; // clear interrupt flag usiserial_send_set_state(AVAILABLE); } } int main(void) { uint8_t i = 0; PORTB |= 1 << PB1; // Ensure serial output is high DDRB |= (1<<PB1); // Configure USI_DO as output. USICR = 0; // Start with USI disabled. sei(); // Enable global interrupts while (1) { char message1[] = "Hello World!\r\n"; uint8_t len1 = sizeof(message1)-1; for (i = 0; i<len1; i++) { while (!usiserial_send_available()) { // Wait for last send to complete } usiserial_send_byte(message1[i]); } _delay_ms(1000); } } So I wanted to try using that same code framework to do the same thing with a known good attiny84. I sanity checked myself and ran a blink type code on the attiny84 and everything worked as expected, again, using the 8 MHz clock, divide by 8, 1 MHz clock. So I copied in the exact same code as before, but I knew I had to change a couple things. I believe in the attiny84, 3-wire mode USI makes the output go to PA5 instead of the PB1 that was used on the attiny85, so I made those changes (PB1 became PA5 and DDRB became DDRA). I also changed in the resetting of the prescaler where it was GTCCR |= 1 << PSR0 on the attiny85, that it would be GTCCR |= 1 << PSR10 on the attiny84. So I made those changes and loaded the below code, but the attiny84 isn't doing anything.
#define F_CPU 1000000UL // Setting micro to 1MHz #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> // #define BAUDRATE 9600 // if running at 8 MHz #define BAUDRATE 2400 // if running at 1 MHz #define STOPBITS 1 extern uint8_t usiserial_send_available(); extern void usiserial_send_byte(uint8_t data); // If bit width in cpu cycles is greater than 255 then divide by 8 to fit in timer // Calculate prescaler setting #define CYCLES_PER_BIT ( (F_CPU) / (BAUDRATE) ) #if (CYCLES_PER_BIT > 255) #define DIVISOR 8 #define CLOCKSELECT 2 #else #define DIVISOR 1 #define CLOCKSELECT 1 #endif #define FULL_BIT_TICKS ( (CYCLES_PER_BIT) / (DIVISOR) ) // USISerial send state variable enum USISERIAL_SEND_STATE { AVAILABLE, FIRST, SECOND }; static volatile uint8_t usiserial_send_state = AVAILABLE; static inline uint8_t usiserial_send_get_state(void) { return usiserial_send_state; } static inline void usiserial_send_set_state(enum USISERIAL_SEND_STATE state) { usiserial_send_state=state; } uint8_t usiserial_send_available() { return usiserial_send_get_state() == AVAILABLE; } // Transmit data persistent between USI OVF interrupts static volatile uint8_t usiserial_tx_data; static inline uint8_t usiserial_get_tx_data(void) { return usiserial_tx_data; } static inline void usiserial_set_tx_data(uint8_t tx_data) { usiserial_tx_data = tx_data; } static uint8_t reverse_byte (uint8_t x) { x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); return x; } void usiserial_send_byte(uint8_t data) { while (usiserial_send_get_state() != AVAILABLE) { // Spin until we finish sending previous packet }; usiserial_send_set_state(FIRST); usiserial_set_tx_data(reverse_byte(data)); // Configure Timer0 TCCR0A = 2<<WGM00; // CTC mode TCCR0B = CLOCKSELECT; // Set prescaler to clk or clk /8 GTCCR |= 1 << PSR10; // Reset prescaler OCR0A = FULL_BIT_TICKS; // Trigger every full bit width TCNT0 = 0; // Count up from 0 // Configure USI to send high, start bit and 6 bits of data // Start bit (low 0x00) // followed by first 7 bits of serialOutput USIDR = 0x00 | usiserial_get_tx_data() >> 1; // Enable USI Counter OVF interrupt (USIOIE). // Select three wire mode to ensure USI written to PA5 (USIWM0 and USIWM1) // Select Timer0 Compare match as USI Clock source. (USICS0, USICS1, and USICLK) USICR = (1<<USIOIE) | (0<<USIWM1) | (1<<USIWM0) | (0<<USICS1) | (1<<USICS0) | (0<<USICLK); DDRA |= (1<<PA5); // Configure USI_DO as output. USISR = 1<<USIOIF | (16 - 8); // Clear USI overflow interrupt flag and set USI counter to 8 } // USI overflow interrupt indicates we've sent a packet ISR (USI_OVF_vect) { if (usiserial_send_get_state() == FIRST) { usiserial_send_set_state(SECOND); // Send last 1 bit of data // and stop bits (high 0x7F) USIDR = usiserial_get_tx_data() << 7 | 0x7F; // Clear USI overflow interrupt flag (USIOIF) // and set USI counter to send last bit and stop bits USISR = 1<<USIOIF | (16 - (1 + (STOPBITS))); } else { PORTA |= 1 << PA5; // Ensure output is high DDRA |= (1<<PA5); // Configure USI_DO as output. USICR = 0; // Disable USI. USISR |= 1<<USIOIF; // clear interrupt flag usiserial_send_set_state(AVAILABLE); } } int main(void) { uint8_t i = 0; PORTA |= 1 << PA5; // Ensure serial output is high DDRA |= (1<<PA5); // Configure USI_DO as output. USICR = 0; // Start with USI disabled. sei(); // Enable global interrupts while (1) { char message1[] = "Hello World!\r\n"; uint8_t len1 = sizeof(message1)-1; for (i = 0; i<len1; i++) { while (!usiserial_send_available()) { // Wait for last send to complete } usiserial_send_byte(message1[i]); } _delay_ms(1000); } } Any ideas what I did wrong?