Pages: [1]   Go Down
Author Topic: USI Serial, Hardware Serial for ATtiny  (Read 1190 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
God Member
*****
Karma: 0
Posts: 594
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Download from my github: https://github.com/frank26080115/Arduino-UsiSerial-Library/

UsiSerial is a simple wrapper around the code from AVR307 so that Arduino can use USI to implement a hardware serial port

The USI class works almost exactly like HardwareSerial
So you can use print and println and other functions like that

PORTB0 is DI, thus it is RX
PORTB1 is DO, thus it is TX
default baud rate is 19200, which can only be changed in USI_UART_config.h
only some baud rates will work, depending on CPU frequency
buffers are small to save memory, since this library is designed for ATtiny
testing was done using a 16 MHz Trinket

I don't know if this has been done before but I needed it and wrote it just a few minutes ago
Logged

Freelance engineer, consultant, contractor. Graduated from UW in 2013.

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13469
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for sharing,
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

UK
Offline Offline
Faraday Member
**
Karma: 100
Posts: 6109
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

From a quick look at the usi-uart-config.h I can't immediately see how to know if it would work on an Attiny45 at 1MHz?

...R
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 594
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

From a quick look at the usi-uart-config.h I can't immediately see how to know if it would work on an Attiny45 at 1MHz?

...R

Right, this is a complicated calculation, you can't see it immediately. So if you really want to know, you need to go through the #define preprocessor calculations by hand.

If the final calculated timer value is too low, then the ISR may not execute fast enough

If the timer value is large enough, then the second problem is accuracy. There are two values, one for half of a bit and one for one full bit width. You need to compare what the full bit width should be with the one calculated by the preprocessor.

To be fair, 1 MHz is not exactly a UART friendly clock speed to begin with, and it is already super slow. On an ATmega (I know we are using ATtiny), you can probably achieve 2400, 4800, and 9600 baud (I'm looking at the table inside the datasheet for an ATmega328P, 9600 requires the 2X speed bit to be set as well). I'm not exactly hopeful.

Going through step by step for 9600 baud (I'm kind of interested myself so I'll go through it with you)

Code:
#define TIMERn_SEED               (256 - ( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ))

Code:
#define TIMERn_SEED               (256 - ( (1000000/ 9600) / 1))

This calculates to 152

Code:
#if ( (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) > (256 - INTERRUPT_STARTUP_DELAY) )

Code:
#define INTERRUPT_STARTUP_DELAY   (0x11 / TIMER_PRESCALER)

Code:
#define TIMER_PRESCALER           1

Code:
#if ( (( (1000000 / 9600) / 1) * 3/2) > (256 - 0x11) )

Code:
#if ((156) > (239))

This is false, so we go to the #else

Code:
#else
    #define INITIAL_TIMERn_SEED       ( 256 - (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) )
    #define USI_COUNTER_SEED_RECEIVE  (USI_COUNTER_MAX_COUNT - DATA_BITS)
#endif

Code:
   #define INITIAL_TIMERn_SEED       ( 256 - (( (1000000/ 9600) / 1) * 3/2) )
    #define USI_COUNTER_SEED_RECEIVE  (16 - 8)

INITIAL_TIMERn_SEED calculates to 100

Both of these should be large enough for the ISR to execute completely before the next ISR

Now to calculate the accuracy

9600 baud means a bit width of 104.17us, while TIMERn_SEED is 152 which is 152us, which is way too bad.

sidenote: how do I know my calculations match what the compiler is actually doing? I'm using http://codepad.org/g05zb8qb
Code:
#define SYSTEM_CLOCK 1000000UL
#define BAUDRATE 9600
#define TIMER_PRESCALER 1
#define USI_COUNTER_MAX_COUNT 16
#define DATA_BITS 8
#define TIMERn_SEED               (256 - ( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ))
#if ( (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) > (256 - INTERRUPT_STARTUP_DELAY) )
    #define INITIAL_TIMERn_SEED       ( 256 - (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 1/2) )
    #define USI_COUNTER_SEED_RECEIVE  ( USI_COUNTER_MAX_COUNT - (START_BIT + DATA_BITS) )
#else
    #define INITIAL_TIMERn_SEED       ( 256 - (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) )
    #define USI_COUNTER_SEED_RECEIVE  (USI_COUNTER_MAX_COUNT - DATA_BITS)
#endif

int main(void)
{
    printf("INITIAL_TIMERn_SEED = %d\r\nTIMERn_SEED = %d\r\n", INITIAL_TIMERn_SEED, TIMERn_SEED);
    return 0;
}
« Last Edit: September 28, 2013, 04:13:02 pm by frank26080115 » Logged

Freelance engineer, consultant, contractor. Graduated from UW in 2013.

UK
Offline Offline
Faraday Member
**
Karma: 100
Posts: 6109
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for that - I was hoping it might be simpler than my own solution http://forum.arduino.cc/index.php?topic=190285.0 - but appears not to be.

...R
Logged

Pages: [1]   Go Up
Jump to: