ATTINY USART initialization gotch

Go To Last Post
26 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I figure if I spent two days finding this, others may benefit:

 

To make the USART in an ATTINY (for example ATTINY404) work, the demo code is missing setting the pin direction for the TxD pin to PORT_DIR_OUT.

In other words, you have to add this first line:

 

    PORTB_set_pin_dir(2, PORT_DIR_OUT);
    USART_0_enable();

 

I finally found this in the ATTINY404 datasheet, page 290, section:

23.3.2.3 Data Transmission - USART Transmitter
When the transmitter has been enabled, the normal port operation of the TxD pin is overridden by the
USART and given the function as the transmitter's serial output. The direction of the pin n must be
configured as output by writing the Direction register for the corresponding port (PORTx.DIR[n]).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to AVRFreaks!

 

Amazing what can be found by reading the data sheet!

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No,  I have not checked.

 

From memory it makes no difference if you make the TX pin an output before or after "enabling" with TXEN bit on Xmega or AVR8X.

 

Traditional AVR do not need to set output.  USART TXEN bit overrides the DDR bit.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
Traditional AVR do not need to set output

The thing is that these are Xmega derivatives and Xmega have always needed the dir set separately (which has always struck me as a retrograde step)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure about the USART, but for other peripherals you need to configure the ports as output. But that's for the tiny AVR0/1. In case of the mega AVR0/1 (e.g. the mega4809) it seems to be automatic.

 

edit: I'm sure this subtle difference will end up being the source of much confusion.

Last Edited: Sun. Jul 21, 2019 - 06:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(from tiny416 datasheet, assume they are the same except for any copy/paste errors they have introduced)

 

...If the USART is configured for one-wire operation, the USART will automatically override the RxD/TxD pin to output, when the transmitter is enabled.

 

24.3.2.3.2
...When the transmitter is disabled, it will no longer override the TxDn pin, and the PORT
module regains control over the pin. To protect external circuitry the pin is automatically configured as an
input by hardware. The pin can now be used as a normal I/O pin with no port override from the USART.

 

one-wire mode = USART has total control (it has to)
else = USART controls PORT but not DIR
when disabled = tx pin DIR set to input

rx = DIR has to be set to input by user
 

You can also check out the PORT block diagram to see where some of these overrides are located, and where any inverted signal is wanted it can be seen the port takes care of that before-in/after-out a digital peripheral.

Last Edited: Sun. Jul 21, 2019 - 08:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I'm not sure about the USART, but for other peripherals you need to configure the ports as output.

I've never believed it was being done for me, so I've always set up all direction bits regardless.  I need to anyhow, for all of my regular I/O signals, may as well include the TX & RX pins.

When I do it myself, I know it is being done.  I also clear all of my variables, though they supposedly are  cleared for me...but I sleep better this way.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am programming on USART on one-wire mode.

I hope one master communication with multi sensor modules through USART.

For there are double direction transferring and no synchronization mechanism , conflict can't be gotten rid of.

That's why choice one-wire : check  whether RXDATA is equal to data transferred, for TxD connected to RxD internal in one-wire mode, this is mentioned in datasheet.

But ,there is a problem for me : RXDATA is empty(0 always) ( the TXC interrupt triggered indeed).

 

I don't know what is the exact meaning of " the USART will automatically override the RxD/TxD pin to output, when the transmitter is enabled "

This is my understanding:

1) one-wire mode is only used to decrease the pins required, TxD + RxD  ->  TxD;

2) Though only 1 pin used, the TxD & RxD hardware peripherals are taken over for use;

3) In sending mode, TxD pin configured as output by hardware automatically;

        Question: what is the state of RxD peripheral?

                       Does hardware disable it automatically? or leave it as user configured?

                       In my case, I enabled RxD peripheral at the beginning, and don't change later. 

4) In receiving mode,  TxD disabled by user.

        Question: Do we need to enable RxD peripheral every time?  Do we need to set TxD pin to input?

 

ATMEL just highlights the situation  transmitter enabled, no word for my questions above.

Maybe for RxD should be treated as normal way.

 

 

 

  

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AN2658:

 

If the timing of the protocol does not allow for the overhead of reading the transmitted data, either the receive interrupt or the receiver itself can be disabled while transmitting. The receive interrupt can be disabled using the Receive Complete Interrupt Enable bit (RXCIE) in the Control A register (CTRLA). The receiver can be disabled using the Receiver Enable bit (RXEN) in the Control B register (CTRLB). When receiving, the receiver and receive interrupt must be enabled.

Those explains hint RxD peripheral can work always and will not be changed by hardware automatically.

I missed one operation:

 

As there needs to be at least one pull-up resistor connected to the bus, it may also be necessary to write to the respective Pull-Up Enable bit (PULLUPEN) for the pin used.  

Maybe that is the reason why no output on TxD pin and no data in RXDATA. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Xiao wrote:

AN2658:

 

If the timing of the protocol does not allow for the overhead of reading the transmitted data, either the receive interrupt or the receiver itself can be disabled while transmitting. The receive interrupt can be disabled using the Receive Complete Interrupt Enable bit (RXCIE) in the Control A register (CTRLA). The receiver can be disabled using the Receiver Enable bit (RXEN) in the Control B register (CTRLB). When receiving, the receiver and receive interrupt must be enabled.

Those explains hint RxD peripheral can work always and will not be changed by hardware automatically.

I missed one operation:

 

As there needs to be at least one pull-up resistor connected to the bus, it may also be necessary to write to the respective Pull-Up Enable bit (PULLUPEN) for the pin used.  

Maybe that is the reason why no output on TxD pin and no data in RXDATA. 

 

 

Here you go:

https://github.com/MicrochipTech...

 

via

 

https://github.com/MicrochipTech...

 

Please check:

https://www.avrfreaks.net/forum/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moe:you are so kind to help me, thank you very very much.

The first example is very helpful to me, I'll modify my code according to it.

 

It looks  like we should manage the pin direction as normal.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are welcome.

 

Regards,

Moe

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

>It looks  like we should manage the pin direction as normal.

 

Their examples don't always make sense.

 

From what I read, for 1-wire mode you set LBME and ODME, for loopback and open drain mode. The tx pin becomes the only pin in use, is automatically set to an output when ODME set and is then using open-drain mode so you enable the tx pin pullup (or use external pullup). The usart rx is connected internally to the tx pin, so nothing to worry about there as it will just see everything on the tx line (when enabled) and can treat is as any other rx except you are also seeing your own tx so can/should also watching for collisions. The tx line is in open drain mode, so it is never driving the pin high and has no need to be anything other than an output which can only pull the line low. The rx internal connection in loopback mode must be outside the normal port circuit as you are then free to use the rx pin as you want (datasheet says).

 

So why are they bothering with setting the pin direction in the example code when open drain mode would take care of it all. The only pin setting needed is to turn on the tx pin pullup if an external one was not used. If one is not using open drain mode in 1-wire, what happens when you have more than one transmitter outputting data at the same time? I would assume that is the only reason for ODME's existence, and should be used.

 

edit-

I would also assume with ODME enabled, they are doing what everyone else would do to simulate open drain mode- out remains set to low, and dir becomes the value to set, high for low (output on, low), low for high (output off, pullup in effect).

Last Edited: Tue. Aug 20, 2019 - 08:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moe:

I need to discuss with you further.

In the sample project :  https://github.com/MicrochipTech/TB3216_Getting_Started_with_USART/tree/master/One_Wire_Mode

 

The RXCIF  will not be set in status register after I canceled the RXCI interrupt.

Origal:

 

void USART0_oneWireSend(char* str)

{

    PORTA.DIR |= PIN0_bm; /* A0 pin must be output to transmit */

    USART0.CTRLA |= USART_RXCIE_bm; /* Receive Complete Interrupt must be enabled */

    for(size_t i = 0; i < strlen(str); i++)

    {

        charToSend = str[i];

        waitTxReady();

        USART0.TXDATAL = charToSend;

        if(waitSendConfirmation() != OK)

         {

           break;

         }

    }

    USART0.CTRLA &= ~(USART_RXCIE_bm);

    PORTA.DIR &= ~PIN0_bm;

}

 

 changed:

void USART0_oneWireSend(char* str)

{

    PORTA.DIR |= PIN0_bm; /* A0 pin must be output to transmit */

    //USART0.CTRLA |= USART_RXCIE_bm; /* Receive Complete Interrupt must be enabled */

    for(size_t i = 0; i < strlen(str); i++)

    {

        charToSend = str[i];

        waitTxReady();

        USART0.TXDATAL = charToSend;

        //if(waitSendConfirmation() != OK)

        while (!(USART0.STATUS & USART_RXCIF_bm)) {;}

         if (USART0.RXDATAL != charToSend)

         {

           break;

         }

    }

    //USART0.CTRLA &= ~(USART_RXCIE_bm);

    PORTA.DIR &= ~PIN0_bm;

}

 

The program blocked at this sentence:

       while (!(USART0.STATUS & USART_RXCIF_bm)) {;}

That means RxD does not work? Or cleard by other operation?

This happened in my code too.

 

According to the comment " /* Receive Complete Interrupt must be enabled */" ,

it hints that this is the only way?

 

 

Last Edited: Thu. Aug 22, 2019 - 11:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:

while (!(USART0.STATUS & USART_RXCIF_bm)) {;} //{;} ?

 

Try to substitue the {;} with just ;

I havent tested this example yet actually

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If what I said is true earlier, then this should probably work (based on the example)-

 

#define F_CPU 3333333
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdbool.h>

bool    USART0_oneWireSend   (char*);
int16_t USART0_oneWireReceive(void);
void    USART0_oneWireInit   (void);

//blocks for 1 frame
//if any collision, give up and return false
bool USART0_oneWireSend(char* str){
    for( ; *str; str++){
        while (!(USART0.STATUS & USART_DREIF_bm));
        USART0.CTRLB &= ~USART_RXEN_bm; //clear rx buffer/rxcif
        USART0.CTRLB |= USART_RXEN_bm; //re-enable rx
        USART0.TXDATAL = *str;
        int16_t rxval;
        while( rxval = USART0_oneWireReceive(), rxval == -1 );
        if( rxval != *str ) return false;
    }
    return true;
}
//non-blocking, -1 = no data available, <0 (not -1) = error, >=0 = good data
int16_t USART0_oneWireReceive(void){
    int16_t ret = USART0.RXDATAH<<8;
    if( !(ret & 0x8000) ) return -1; //rxcif=0, no data
    ret |= USART0.RXDATAL; //always get data even if errors
    if( ret & 0x4600 ) return ret; // error bits set, return will be <0 (rxcif set)
    return ret & 0xFF; //only data (could use 0x1FF if 9bit used)
}
//loopback, open drain, usart handles tx pin
void USART0_oneWireInit(void){
    USART0.BAUD = (uint16_t)USART0_BAUD_RATE(9600);
    USART0.CTRLA = USART_LBME_bm;
    USART0.CTRLB = USART_RXEN_bm | USART_TXEN_bm | USART_ODME_bm;
}

int main(void){
    USART0_oneWireInit();
    PORTA.PIN0CTRL |= PORT_PULLUPEN_bm;

    while (1){
        USART0_oneWireSend("Microchip.\r\n");
        _delay_ms(500);
    }
}

 

I haven't used 1 wire and have not thought out what the isr usage would look like or if there is any benefit (on the receive side I guess), but in the example they provide there is no point to it. The rx isr in the example only handles checking for valid rx vs tx, and the tx is blocking on it, so is of no use.

 

The above modification is a polling only receive, and a blocking tx for 1 frame. The tx should probably poll the oneWireReceive() before doing anything in case other rx data came in, but this is a simple example that does not take into account any comm protocol in effect.

Last Edited: Thu. Aug 22, 2019 - 02:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm:

 

I test your code, blocked at "while( rxval = USART0_oneWireReceive(), rxval == -1 );", stop here : 

if( !(ret & 0x8000) ) return -1; //rxcif=0, no data

that means no data received.

Same question.

So strange.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Try to substitue the {;} with just ; "

 

that does not matter.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have an attiny416 and mega4809 that I can try 1wire mode, will probably check it out and report back.

 

I see a few mistakes I made-

need to add pullup on tx-

PORTA.PIN0CTRL |= PORT_PULLUPEN_bm;

and a missed ~

USART0.CTRLB &= ~USART_RXEN_bm;

 

Simulator does not work for usart, so will try hardware now.

 

The code I posted, with the two changes noted above works. I used a mega4809, usart1, alternate pins (tx=PC4), so all USART0's changed to USART1. Seems to act as expected.

 

Initially I used USART0, and the example worked (the result of the send was true, so rx was seeing tx) but I could not see signals on my logic analyzer because they were hooked up to usart1. The simulator would not show any rx from tx, so don't go by what simulator says in this case.

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you dont mind guys, I will refer to this thread in my other thread "Getting started with Attiny 1 & 0 series":

https://www.avrfreaks.net/forum/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I also created a little example in C++ -

 

https://github.com/cv007/ATmega4809CuriosityNano/blob/master/test-1wire.cpp

 

So, bottom line- in one-wire mode you don't have to mess around with pin directions, and at most you set a pullup on the tx. So looking at their examples may be ok half the time, but may also lead you astray the other half.

 

I tend to just stick with the datasheet until that doesn't get results or is ambiguous, then I look elsewhere for clues. Atmel Start should be a good resource for some of this, but it is not. Their examples may also have some good info, or may not. Eventually you always seem to end up using the debugger to see what is actually happening, or other debugging techniques if the debugger doesn't want to play nice.

 

There are also different descriptions in various datasheets for the same peripheral- the mega4809 describes 1wire much better than the tiny416 for example. So another technique is to grab some other datasheets in the same family and compare to see if more info can be gained.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moe:

I have studied the docs you advised.

Based on the structure of 

https://github.com/MicrochipTech/TB3216_Getting_Started_with_USART/blob/master/One_Wire_Mode/main.c

I re-composed my code as attached files.

There is a strange problem,the received data will not be equal to RXDATA again when I add this configuration:

USART0.CTRLC =
          USART_CHSIZE_9BITH_gc        // Character size: 9 bit read high byte first
        | USART_PMODE_EVEN_gc ;         // Even Parity

Those settings should not change the data received and sent.

Parity check finished inner, and the high byte first rule was obeyed.

I debugged with atmel-ice, found that it getting different from second byte.

I'll try error this weekend.

Let me know your any advice.

Attachment(s): 

Last Edited: Fri. Aug 23, 2019 - 06:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, guys, it works right now,but I don't know why.

 

//init
void USART0_oneWireInit(void) {
    uartRcvBufferInit();
    
    PORTB.DIRCLR = PIN2_bm; //default USART.TxD = PB2
    USART0.BAUD = (uint16_t)USART0_BAUD_RATE(9600);
    
    USART0.CTRLA |= (USART_LBME_bm | USART_RXCIE_bm);
    
    USART0.CTRLB = 1 << USART_MPCM_bp       // Multi-processor Communication Mode: enabled
        | 1 << USART_ODME_bp     /// Open Drain Mode Enable: disabled
        | USART_RXMODE_CLK2X_gc  // CLK2x mode
        | 1 << USART_SFDEN_bp    // Start Frame Detection Enable: enabled
        | 1 << USART_TXEN_bp    // Transmitter Enable: enabled
        | 1 << USART_RXEN_bp;     // Reciever enable: enabled

 

    USART0.CTRLC =
          USART_CHSIZE_9BITH_gc        // Character size: 9 bit read high byte first
        | USART_PMODE_EVEN_gc ;         // Even Parity

}

 

uartSendStatus_t USART0_oneWireSend(void){
    uartStatus = uartSending;
    
    PORTB.DIR |= PIN2_bm;           // pb2 pin must be output to transmit
    
    for(uint8_t i = 0; i <= uartDataIdx; i++){
        charToSend = uartDataToSend[i].data;

        while (!(USART0.STATUS & USART_DREIF_bm)){;}  
        if(uartDataToSend[i].addressBit)
            USART0.TXDATAH = uartDataToSend[i].addressBit; //don't change other bit?
        USART0.TXDATAL = charToSend;
        
        sendingStatus = uartSendSENDING;
        while(sendingStatus == uartSendSENDING){;}
        if(sendingStatus != uartSendOK){
            break;
        }
        
        //if(i==uartDataIdx) beginFlash();//test
    }
    
    PORTB.DIR &= ~PIN2_bm;
    uartDataIdx = -1;
    uartStatus = uartReceiveIdle;
    
    return sendingStatus;
}

ISR(USART0_RXC_vect){
    if(uartStatus==uartSending){
        if (USART0.RXDATAL == charToSend){
            sendingStatus = uartSendOK;
        }else{
            sendingStatus = uartSendCONFLICT;
        }
    }else{
        uartRXDinterruptTriggered = true;
        uartRXDlastRXDATAH = USART0.RXDATAH;
        uartRXDlastRXDATAL = USART0.RXDATAL;
    }
}

 

 

Last Edited: Sat. Aug 24, 2019 - 02:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now, I have no so much time to study further, here is my experience ,for refrence.

 

1) the framework code provided by Moe works fine, and it is the very good start point;

    my code posted upper works fine too.

2) in my case, if turn to poll mode + 9bit + even check, the RXCIF bit will not set as desired,

    it maybe caused by code, but I have no time to dig it.

   ---------->update: RXCIF set as desired, but MPCM related function works wrong.

3) check the hardware first.

    I'm luckly faced problem comes from hardware twice:

      A. the PA4 hardware connect is not stable, lead to EVSYS does not work;

      B. I use 2 ATTINY814 to test the USART,  the Vin & GND & TX pin connected together each of them, 

          This will cause the interrupt doesn't work too, it will works fine if you disconnect one of the ATTINY814.

          I have to power them seperately.

          ------------>update: it does not matter now, don't know why too :)

Thank Moe & curtvm.

Last Edited: Tue. Aug 27, 2019 - 02:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perfect, happy it worked for you.