Problems setting baudrate on ATtiny202

Hi guys,

Background

I was hoping to keep a lid on this project until I had a bit more to show, but I need a hand. I’ve constructed some temperature sensors that communicate over RS485 using the ATtiny202 and excellent TMP117 as part of a larger project, and my current hurdle is setting the baudrate on the UART.

Here’s the schematic and photos of the boards.


The two bottom boards are the ones in question, the top one is a “master” designed to sit on a raspberry Pi.

The problem

The ATtiny datasheet gives the following guidelines for setting baudrate:


Full datasheet here:

Since I’m trying to implement Modbus-ASCII, I’m targeting a baudrate of 19200. 9600 must also be supported but that’s a problem for later since I haven’t even gotten 19200 to work properly yet.

My attempt

Here’s what I have so far:

My clock speed (and hence CLK_PER) is 10MHz I believe according to what I’ve set.

This means my calculation is:
BAUD = (64 x 10,000,000) / (16 x 19200)
This comes out as:
image
2083 in hex is 0x0823. My thinking now is that I need to put 0x08 in the BAUDH register, and 0x23 in the BAUDL register.

The code

Here’s my code so far. I mistakenly picked the 2K flash variant of this chip, which made it far too small to fit the excellent megaTinyCore. This means I had to do it closer to bare metal. Here’s the tutorial which got me started if you’re interested. Like most “hard ways” it’s giving me more pain, but more fun and learning.

#include <avr/io.h> // header file containing all the structs and their registers for interacting with peripherals
#include <util/delay.h>

#define PIN_TXD  6
#define PIN_RXD  7

void synchronousWrite(uint8_t byteToWrite){ // I commented out most of this while troubleshooting while I sort out baudrate
    USART0.TXDATAL = byteToWrite;
    // while (!((USART0.STATUS & USART_TXCIF_bm) && (USART0.STATUS & USART_DREIF_bm))){} // wait for transmit complete flag
    // USART0.STATUS = USART_TXCIF_bm; //
}


void main() {
    // Setting clock speed needs to use the protected write macro
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x1); // Enable prescaler, prescaler register is 0, should lead to 10MHz clock (safe for 3.3V)

    //Setup serial
    //TXD is PA6, RXD is PA7
    PORTA.OUTSET= (1 << PIN_TXD);
    PORTA.DIRSET = (1 << PIN_TXD);

    USART0.BAUDH = 0x08; // supposed to be 19200 baud
    USART0.BAUDL = 0x23;
    

    USART0.CTRLB += USART_RXEN_bm; // Enable receiver
    USART0.CTRLB += USART_TXEN_bm; // Enable transmitter
    USART0.CTRLC += USART_PMODE_EVEN_gc; // turn on even parity (part of modbus spec)

    _delay_ms(10000); // Wait 10s after programming (reset) so I can get logic analyzer probes onto the board

    while(1){
        _delay_ms(100);
        synchronousWrite(0x55); // send 0x55 10 times a second. 0x55 is a bunch of alternating 0s and 1s, so great for measuring baudrate
    }
}

But it comes out on a LA as below:


That frequency of 5.7KHz means my baudrate is twice that, 11500 baud or thereabouts.

Can anyone see where I’m going wrong? I get that this is a very deep dive into something no engineer would do, but after a few days of banging my head against this wall, I’m stumped!

Feel free to hit me with as many clarifying questions as you like

Cheers,
James

3 Likes

There is no code to set the samples per bit (USARTn.CTRLB). Have you confirmed that it has been set to the correct value?

My reading of that description suggests that ‘0x0823’ has to be put into BAUDHL[15:6], which won’t fit. So either the description is wrong or I’m reading it wrong.

3 Likes

Hi Jeff,

Good thinking! My (possibly incorrect) assumption was that since the reset value of CTRLB is 0x00, this would mean it’d default to “normal” mode, and thus 16 samples per bit:
image

I agree it’s confusing. This would mean that the highest divisor you could give is 1023, for a minimum baudrate of 39100 baud, which seems too high. Perhaps the “fractional” section just refers to finer granularity than the usual values (9600, 115200)? I’m also wondering where the “64 x” comes from in the formula given. Does this account for shifting the bits left to fit into [15:6]? The rest of the datasheet is well worded but I can’t get my head around this bit.

Cheers for the help,
James

3 Likes

The Register Summary gives a different layout for BAUD, [7:0] and [15:8], but without any explanation.
One path forward might be to recalculate for a much lower baud rate like 1200, load the results into BAUD with the left shift (ensuring the low-order bits are zero) and measure the result.

4 Likes

I don’t know if this is helpful but:
When writing to a peripheral register, I would not use += as that requires the code to read the register before writing it, and in this case you don’t care so just write the whole value and use less code space:

USART0.CTRLB = USART_RXEN_bm | USART_TXEN_bm;

assuming those are bit patterns 0x80 and 0x40

And you should set USART0.CTRLC (also with an =) before you enable the device by writing to USART0.CTRLB (See Step 3 of initialisation). The value would be 0x23 or if there’s masks construct it with usart_8_bit_bm | usart_evenp_bm or whatever the actual magic incantation is provided by the language.

That does not explain what is happening.

3 Likes

You asked for 19200. 11500 is not twice 19200. Did you mean something different? It is close to 5/8 and I wonder if writing to the parity register after enabling made it recalculate.

3 Likes

So did this get resolved?

1 Like

Hi Alan,

Sorry for the delay, I gave this project a break for a bit as the lack of progress was really frustrating me.

By this, I meant the 5.7KHz on the LA (for the 0x55 “waveform”) means a baudrate of twice that, or 11400 Baud.

Good point, I’ll give this a go and get back to you

1 Like

UPDATE

This didn’t change anything, still 5.7KHz (11400 baud)

1 Like

Next thing I thought I’d try was give my write function a plotting a few values across the range of the 16-bit baud register (0-65,536)

But I noticed that my 100ms delays were actually 167ms :thinking:
image

So I measured what a 1s and 2s delay came out as, both 1.66x longer than they should:
image

This makes me think my clockspeed is actually 6MHz instead of 10?

Just to be sure, even though I’m almost certain I did this earlier, I double-checked that the clock fuses were written correctly:

[james@james-thinkpad avr]$ pymcuprog write -m fuses -o 2 -l 0x02 -d attiny202 -t uart -u /dev/ttyUSB0
Connecting to SerialUPDI
Pinging device...
Ping response: 1E9123
Writing literal values...
Done.

And the delays returned to the right values:
image

Now to check what my baudrates do…

SUCCESS!!!
image

No framing or parity errors, just the byte I sent out.

Now I can start implementing MODBUS

Thank you @Jeff105671 and @Alan73922 for all your help. I am quite keen to get this project done so I can tell you all about it :slight_smile:

For anyone coming across this topic later, ignore what the datasheet says about a fractional and integer section of the BAUD register, and just write the what the formula in the datasheet tells you to, to the whole register. Just make sure you know your clock speed!

2 Likes

That is somewhat mysterious, as a 6MHz or thereabouts clock isn’t an option according to the pdf I downloaded. There’s a divide by 6 from 20MHz would yield 3.3Mhz, and it looks like that is the default from the factory. But it looks like you are on the right track.

1 Like

So the x64 is the 6-bit shift! Makes sense in retrospect, but that explanation could be a lot clearer.

1 Like