Attiny406 I2C communication problem

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

Hello

 

This is the first time I'm using attiny microprocessors and I have a little bit of trouble with setting up and communicating using I2C. I've read the datasheet and I think I did everything they said, but I can't get it to work. When I read data register I get the same thing that I put in it (register address). And I don't think I am even sending a device address correctly because even when I send a start condition to a I2C address that doesn't exist in my circuit, I get a OK from my Start function, which should mean that the slave acknowledged the call, but of course, there is no one to acknowledge it on that address.

 

Correction. When writing to UART the return value of the I2C_Start_W function, I get a <break> in cutecom terminal, not 0.

 

 My code is this:

 

/*
 * File:   main.c
 * Author: mario
 *
 * Created on May 8, 2022, 11:00 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <avr/io.h>

#define F_CPU_REAL 20000000UL
#define F_CPU F_CPU_REAL/6	// 3.3 MHz
#define F_SCL 100000

#include <util/delay.h>

#define BAUD_Rate 38400

/*
 *
 */
/*return 0 if ok*/
int I2C_Start_W(uint8_t addr){
    TWI0.MADDR=addr<<1;

    while(!(TWI0.MSTATUS && TWI_WIF_bm));

    if(TWI0.MSTATUS && ~(1<<TWI_RXACK_bp)){
        return 0;
    }

    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }

}

int I2C_Start_R(uint8_t addr){
    TWI0.MADDR=(addr<<1)|0b00000001;
    while(!(TWI0.MSTATUS && TWI_WIF_bm));
    if(TWI0.MSTATUS && ~(1<<TWI_RXACK_bp))
        return 0;
    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }

}

int I2C_Write(uint8_t data){
    TWI0.MDATA=data;
    while(!(TWI0.MSTATUS && TWI_WIF_bm));

    if(TWI0.MSTATUS && ~(1<<TWI_RXACK_bp))
        return 0;
    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }
}

/*no more data*/
uint8_t I2C_Read_Nack(){

    uint8_t data;
    while(!(TWI0.MSTATUS && TWI_RIF_bm));
    data=TWI0.MDATA;
    TWI0.MCTRLB|=1<<TWI_ACKACT_bp;
    TWI0.MCTRLB|=0x3<<TWI_MCMD_gp;
    return data;
}

/*want more data*/
uint8_t I2C_Read_Ack(){

    uint8_t data;
    while(!(TWI0.MSTATUS && TWI_RIF_bm));
    data=TWI0.MDATA;
    TWI0.MCTRLB|=0x2<<TWI_MCMD_gp;
    return data;
}

void I2C_Stop(){
    TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;

}

void UART_Write(uint8_t data)
{
    if(USART0.STATUS&&(1<<USART_DREIF_bp))
          USART0.TXDATAL=data;
    while(!(USART0.STATUS && (1<<USART_TXCIF_bp)));
}

int main(int argc, char** argv) {
/////////////////////////////////////////////////////setting up UART
    float tr=500*pow(10,-9);
    int baudReg= ((64UL * F_CPU)/(16UL * BAUD_Rate));
    USART0.BAUD=baudReg;
    USART0.CTRLB|=(1<<USART_RXEN_bp)|(1<<USART_TXEN_bp);
    PORTB.DIR|=1<<PIN2_bp;
/////////////////////////////////////////////////////setting up I2C
    PORTB.DIR|=(1<<PIN0_bp)|(1<<PIN1_bp);
    TWI0.CTRLA|=0x02<<TWI_SDAHOLD_gp;
    TWI0.MBAUD=F_CPU/(2*F_SCL) - (5+ ((F_CPU*tr)/2));
    TWI0.CTRLA|=0x02<<TWI_SDAHOLD_gp;
    TWI0.MCTRLA|=(1<<TWI_ENABLE_bp)|(1<<TWI_WIEN_bp)|(1<<TWI_RIEN_bp);
    TWI0.MSTATUS|=0x1<<TWI_BUSSTATE_gp;
    uint8_t rdata[6]={0};
    int status=0;
    while(1){

        status=I2C_Start_W(0b1110111);//send Start condition
        UART_Write(status);
        I2C_Write(0xac);//write register address

         //I2C_Stop(); //not sure if I need this

        I2C_Start_R(0b1110111);//read that register
        rdata[0]=I2C_Read_Ack();
        UART_Write(rdata[0]);//send over UART and I get 0Xac, exactly what I wrote
       _delay_ms(1000);

    }

    return (EXIT_SUCCESS);
}

 

Any help would be appreciated.

Last Edited: Fri. May 13, 2022 - 05:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i14r10 wrote:
I can't get it to work. When I read data register I get the same thing that I put in it (register address). And I don't think I am even sending a device address correctly because even when I send a start condition to a I2C address that doesn't exist in my circuit, I get a OK from my Start function

 

You did not say what slave device your trying to talk to....

I would suggest you not use a start_wait() function, but rather just do a start() and check the return status and only if it indicates a returned ACK do you proceed, if this is nak'd, just do a stop() and report the error, or otherwise handle the error.  Do not continue with other I2C functions, that will just confuse you. 

We can only assume you have proper pull ups on your clock and data lines and the baud rate is a reasonable rate 100k or less to begin with and the address of the slave is correct...

Post a picture of your setup. 

If the above conditions are not as expected above, correct that before anything else.

 

Jim

 

 

FF = PI > S.E.T

 

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

A good start is to figure out what the difference is between && and &

 

TWI0.MSTATUS &&

 

You are using 'logical and' && everywhere, when you need to use 'bitand' &.

 

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

ok. Now I fixed && and & (I think).

I found out that my Start function never writes anything on the bus. It gets stuck just before UART_Write(0xab); WIF bit is never set in MSTATUS register.

 

The rest of the code is the same and I'm just using this function to send Start condition. But it doesn't work

I even added global interrupt enable, just in case

SREG|=1<<CPU_I_bp;

 

But it still doesn't work

 

uint8_t I2C_Start_W(uint8_t addr){
    TWI0.MADDR=addr<<1;
    UART_Write(0xaa);
    while((TWI0.MSTATUS & TWI_WIF_bm)!=1);

    UART_Write(0xab);

    //_delay_ms(1);
    if((TWI0.MSTATUS & ~(1<<TWI_RXACK_bp))==1){

        return 0;
    }

    else
    {

        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;

        return 1;
    }

}

 

Last Edited: Fri. May 13, 2022 - 08:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

i14r10 wrote:
I can't get it to work. When I read data register I get the same thing that I put in it (register address). And I don't think I am even sending a device address correctly because even when I send a start condition to a I2C address that doesn't exist in my circuit, I get a OK from my Start function

 

You did not say what slave device your trying to talk to....

I would suggest you not use a start_wait() function, but rather just do a start() and check the return status and only if it indicates a returned ACK do you proceed, if this is nak'd, just do a stop() and report the error, or otherwise handle the error.  Do not continue with other I2C functions, that will just confuse you. 

We can only assume you have proper pull ups on your clock and data lines and the baud rate is a reasonable rate 100k or less to begin with and the address of the slave is correct...

Post a picture of your setup. 

If the above conditions are not as expected above, correct that before anything else.

 

Jim

 

 

I have both SHTC3 humidity/temp sensor and BMP180 pressure sensor on the I2C bus.

 

I don't think I'm using start_wait. I think my function does what you recommend. Checks if I get back ACK. If not, then it sends STOP condition.

 

I have 4.7k resistors as pullups

 

I2C frequency is 100kHz

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while((TWI0.MSTATUS & TWI_WIF_bm)!=1);
if((TWI0.MSTATUS & ~(1<<TWI_RXACK_bp))==1){

Now you need to figure how to test bits. Most of the time you want to get 0, or not 0.

 

while((TWI0.STATUS & 0x40) !=1); //this will always be true since 'anything & 0x40' will never be 1 (is always !=1).

 

while((TWI0.STATUS & 0xEF))==1){ //this is only testing that all bits, excluding bit4, are all 0 except bit0 (the 1 value you use)

 

better-

while( ! (TWI0.MSTATUS & TWI_WIF_bm) ){} //the resulting value will either be 0 (false), or 0x40 (true), so wait while result is not true

while( (TWI0.MSTATUS & TWI_WIF_bm) == 0 ){} //the resulting value will either be 0, or 0x40, so wait while result == 0

 

if( ! (TWI0.MSTATUS & TWI_RXACK_bm) ){

if( (TWI0.MSTATUS & TWI_RXACK_bm) == 0){

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

Wait a minute, I think you helped me realize something that I've been doing wrong for years.

 

0b1111 & 0b0010 equals "true"? And is it

 

0b1111 & 0b0010 = 0b0010 = 2 ?

 

If I got it correct this time, I thought "true" and "1" are the same. That's why in my code I test it if it's 1 or 0, and I should have tested if it's true or false.

 

 

 

curtvm wrote:

while((TWI0.MSTATUS & TWI_WIF_bm)!=1);
if((TWI0.MSTATUS & ~(1<<TWI_RXACK_bp))==1){

Now you need to figure how to test bits. Most of the time you want to get 0, or not 0.

 

while((TWI0.STATUS & 0x40) !=1); //this will always be true since 'anything & 0x40' will never be 1 (is always !=1).

 

while((TWI0.STATUS & 0xEF))==1){ //this is only testing that all bits, excluding bit4, are all 0 except bit0 (the 1 value you use)

 

better-

while( ! (TWI0.MSTATUS & TWI_WIF_bm) ){} //the resulting value will either be 0 (false), or 0x40 (true), so wait while result is not true

while( (TWI0.MSTATUS & TWI_WIF_bm) == 0 ){} //the resulting value will either be 0, or 0x40, so wait while result == 0

 

if( ! (TWI0.MSTATUS & TWI_RXACK_bm) ){

if( (TWI0.MSTATUS & TWI_RXACK_bm) == 0){

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

0b1111 & 0b0010 equals "true"? 

Its true if it gets converted to a bool-

 

if( 0b1111 & 0b0010 ){ /* if is just a bool test, 2 is not 0, so is true, so we are here */ }

 

but if you are comparing it to an int, it is still a 2 to be compared with a 1-

 

if( (0b1111 & 0b0010) == 1 ){ /* an int comparison (that ultimately returns a bool from the if statement), 2 is never equal to 1, so the if always returns false, and we are never here */ }

 

 

edit-

What you are really missing is when the result of the if is promoted to a bool. If you compare an int to a bool, the bool gets promoted to an int and the comparison is done (the int vs 1/0), and the result of that is converted to a bool for the if statement.

 

This would work, but would be awkward to do with no reason to do it-

while( (bool)(TWI0.MSTATUS & TWI_WIF_bm) != 1 ); //or != true, or == false

since this first converts the bitand test to a bool (0 is false, 0x40 is true), you are now comparing bool's.

 

But then why not let the if statement convert for you, or let the ! logic inversion do the conversion-

while( ! (TWI0.MSTATUS & TWI_WIF_bm) ); //int result (0, 0x40), ! works on bools, so result is converted to bool first, then logic inverted

or since a 0/false is a single value as opposed to true which is many values (not 0)

while( (TWI0.MSTATUS & TWI_WIF_bm) == 0 ); //int result (0, 0x40), we only care about the 0 state, anything else will be true but we limited the test to only the bit we wanted

could also do this but is not normally done except when testing multiple bits-

while( (TWI0.MSTATUS & TWI_WIF_bm) != TWI_WIF_bm );

Last Edited: Sat. May 14, 2022 - 08:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i14r10 wrote:

If I got it correct this time, I thought "true" and "1" are the same. That's why in my code I test it if it's 1 or 0, and I should have tested if it's true or false.

 

They are, sort of. An expression is true if it is non-zero.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

I should have tested if it's true or false.

Just use an if statement.  if (dog & 0x03)  call billy ...   when  either (or both) of the 2 LSB bits of dog are 1, billy will be called.  You don't (but can) say equals true.

I have both SHTC3 humidity/temp sensor and BMP180 pressure sensor on the I2C bus.

Which one do you have hooked up right now for debugging of communications? 

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

ok so, my new functions are

 

uint8_t I2C_Start_W(uint8_t addr){

    TWI0.MADDR=addr<<1;

    while((TWI0.MSTATUS & TWI_WIF_bm)==0){ //wait until WIF flag is set

    }

    if((TWI0.MSTATUS & TWI_RXACK_bm)==0){ //check if ACK flag is 0

        return 0;
    }

    else
    {

        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp; //if NACK send STOP
        return 1;
    }

}

 

I hope this is better

Last Edited: Sun. May 15, 2022 - 08:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

Which one do you have hooked up right now for debugging of communications? 

 

BMP180

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

I've made some progress. I have valid I2C communication. I can get data, at least I'm 90% sure I'm getting good data, but I have one problem. If I read only one byte from the sensor and respond with NACK, I can do that all day repeatedly. But if I try to read two bytes one after the another, first one I respond with ACK and after second I respond with NACK. On the first try I get valid data from both bytes, but on the second way through the while(1) loop it gets stuck in the I2C_Read_Nack() function, in the 

 

while((TWI0.MSTATUS & TWI_RIF_bm)==0);

I never get read interrupt flag after first round of I2C communication is done (after stop condition), and MSTATUS register reads 0x22 meaning clock stretching and bus owner flags are set.

 

So, in the first iteration of the loop I get byte1(ACK) and byte2(NACK)

In the second iteration I get byte1(ACK) and then it gets stuck.

 

After first iteration of the loop Bus status is Idle.

 

 

!!!!!!!!!__________________________EDIT________________EDIT____________________________!!!!!!!!!!!!

 

Crazy how 10 minutes after I post something I find the error. The error was in Ack function, line

TWI0.MCTRLB&=(1<<TWI_ACKACT_bp);

when it should be

 

 

TWI0.MCTRLB&=~(1<<TWI_ACKACT_bp);

 

My code is this (corrected version), if anybody wants to play with it. I should add some more stuff like checking if there's error on the bus etc... I'll do that tomorrow.

 


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <avr/io.h>
#include <util/delay.h>

#define F_CPU_REAL 20000000UL
#define F_CPU F_CPU_REAL/6	// 3.3 MHz
#define F_SCL 100000
#define BAUD_Rate 57600

uint8_t rdata[6]={0};

/*return 0 if ok*/
uint8_t I2C_Start_W(uint8_t addr){

    TWI0.MADDR=addr<<1|0x0;
    while((TWI0.MSTATUS & TWI_WIF_bm)==0);
	TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);						// clear Read and Write interrupt flags

    if((TWI0.MSTATUS & TWI_RXACK_bm)==0){
        return 0;
    }

    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }

}

int I2C_Start_R(uint8_t addr){

    TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);						// clear Read and Write interrupt flags
    TWI0.MADDR=(addr<<1)|0b00000001;

    if((TWI0.MSTATUS & TWI_RXACK_bm)==0){
        return 0;
    }

    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }
}

int I2C_Write(uint8_t data){

    TWI0.MDATA=data;
    while((TWI0.MSTATUS & TWI_WIF_bm)==0);
	TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);						// clear Read and Write interrupt flags
    if((TWI0.MSTATUS & (1<<TWI_RXACK_bp))==0){
        return 0;
    }

    else
    {
        TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;
        return 1;
    }
}

/*no more data*/
uint8_t I2C_Read_Nack(){
    uint8_t data;

    while((TWI0.MSTATUS & TWI_RIF_bm)==0);

    data=TWI0.MDATA;
    TWI0.MCTRLB|=1<<TWI_ACKACT_bp;
    TWI0.MCTRLB|=0x03<<TWI_MCMD_gp;

    return data;
}

/*want more data*/
uint8_t I2C_Read_Ack(){

    uint8_t data;

    while((TWI0.MSTATUS & TWI_RIF_bm)==0);
    data=TWI0.MDATA;

    TWI0.MCTRLB&=~(1<<TWI_ACKACT_bp);
    TWI0.MCTRLB|=0x02<<TWI_MCMD_gp;

    return data;
}

double tr=0;

void I2C_Init(){
    tr=1000/1000000000;
    PORTB.DIR|=(1<<PIN0_bp)|(1<<PIN1_bp);
    TWI0.CTRLA|=0x02<<TWI_SDAHOLD_gp;
    SREG|=1<<CPU_I_bp;
    uint8_t baud=F_CPU/(2*F_SCL) - (5+ ((F_CPU*tr)/2));

    TWI0.MBAUD=baud;
    float tlow=0;
    tlow=(baud+5)/F_CPU - 150*pow(10,-9);
    TWI0.MCTRLA|=(1<<TWI_ENABLE_bp)|(1<<TWI_WIEN_bp)|(1<<TWI_RIEN_bp);
    TWI0.MSTATUS|=1<<TWI_BUSSTATE_gp;

}

void UART_Write(uint8_t data)
{
    while(!(USART0.STATUS&(1<<USART_DREIF_bp)));
    USART0.TXDATAL=data;
    while(!(USART0.STATUS & (1<<USART_TXCIF_bp)));
}

int main(int argc, char** argv) {

    int baudReg= ((64UL * F_CPU)/(16UL * BAUD_Rate));
    USART0.BAUD=baudReg;
    USART0.CTRLB|=(1<<USART_RXEN_bp)|(1<<USART_TXEN_bp);
    PORTB.DIR|=1<<PIN2_bp;

    I2C_Init();

    int status=0;
    while(1){

        status=I2C_Start_W(0b1110111);//sensor address
        if(status!=0)
            return(0);

        status=I2C_Write(0xb4);//register address
        if(status!=0)
            return(1);

        status=I2C_Start_R(0b1110111);
         if(status!=0)
            return(2);

        rdata[0]=I2C_Read_Ack();
        rdata[1]=I2C_Read_Nack();

       _delay_ms(5000);

    }

    return (EXIT_SUCCESS);
}

 

Last Edited: Mon. May 16, 2022 - 08:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a twi driver for avr0/1/dx-

https://github.com/cv007/Avr01Dx...

its interrupt driven, but can also poll.

 

On an mcu I would avoid double,float,pow unless you know the compiler will take care of the calculations at compile time. If they get moved to runtime then you are wasting flash space, especially on a tiny406 where you are already limited.

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

curtvm wrote:

Here is a twi driver for avr0/1/dx-

https://github.com/cv007/Avr01Dx...

its interrupt driven, but can also poll.

 

On an mcu I would avoid double,float,pow unless you know the compiler will take care of the calculations at compile time. If they get moved to runtime then you are wasting flash space, especially on a tiny406 where you are already limited.

 

Thanks. But I wonder, is it just me not being a real programmer, or most of the code I come across is very hard to understand and is hmmm... abstract? Functions that call other functions that call other functions that use enumerators, defines that define already defined stuff etc... XD, ok, rant is over.

 

Sure, I'll get rid of double as soon as I check out if I can since datasheet said to use those.

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

Functions that call other functions that call other functions that use enumerators

Well, that is good and bad....good if the pieces you are using are well-proven, working 100% and you know how to properly interface to them (often a pothole there).  When using many layers, lots of the details get hidden away, which is good, since you typically don't need to worry about all of them.  Also, external access is limited to their defined interfaces (hopefully!) to  prevent you from messing things up. On the other hand, if you do have some detail issue, then having them buried 3 layers down can be a real challenge, especially when you were not the original coder.  It's similar to buying a replacement car engine and hooking it up.  You don't care about the spring inside the EGR valve..until the engine doesn't work well because that spring has a problem.

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

But I wonder, is it just me not being a real programmer

Yes, its you. That code is about as simple as it gets for twi. Learn to use functions, enums, static, etc., and when to use them. These kind of things are your friends, with functions being your closest friends.

 

that use enumerators, defines that define already defined

The only (self created) defines in use are in twiPins.h, and F_CPU. I should change the twiPins.h to get rid of the defines. I rarely use defines, but revert to old habits now and then. The enums in a source file work nice, and do not 'leak' outside the source file (no one else can see them). They are constants without storage, and are useful. In this case, 9 lines of enums take care of all needs and combinations, can be seen in the same file as the code so do not have to hunt down other headers, and is easier to create/read than trying to use all the existing header defines. These are created reading the datasheet, so don't even look at the existing headers when creating them.

 

static void toStateIdle     () { TWI0.MSTATUS = ALLFLAGS|IDLE; } //clear flags, set to IDLE

vs

static void toStateIdle     () { TWI0.MSTATUS = TWI_BUSERR_bm|TWI_ARBLOST_bm|TWI_CLKHOLD_bm|TWI_WIF_bm|TWI_RIF_bm|TWI_BUSSTATE_IDLE_gc; } //clear flags, set to IDLE

 

int main(int argc, char** argv) 
return (EXIT_SUCCESS);

Looks like you are probably a pc programmer. Not a bad thing, just some indications like the above that are used in pc programming but not mcu's.

 

Sure, I'll get rid of double as soon as I check out if I can since datasheet said to use those.

I doubt the datasheet says anything about double/float/pow. If you simply discard the Trise time, it becomes simpler-

https://github.com/cv007/Avr01Dx...

Without Trise included in the calculations, you are a fraction slower than you could be if you knew what the Trise time actually was (like maybe 97khz vs 100khz, for example). But being slower means you are erring on the safe side (even though most devices are not going to magically quit working at 100,001Hz or 400,001Hz). I don't think it is worth the trouble, and to get the real Trise time you would have to measure it, otherwise its a guess anyway.

Last Edited: Tue. May 17, 2022 - 09:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just re-reading this thread. I wonder what the attraction of (1 <<) and XXXX_bp actually is?

 

AFAIK in all these new Xmega derived AVRs Atmel go to all the trouble of giving you both _bp (bit position = 0..7) and _bm (bit mask 0x01, 0x02, 0x04 .. 0x40, 0x80) symbols so why would you ever choose to use _bp and (1<<).

 

You either have:

#define BITNAME_A_bp 0
#define BITNAME_B_bp 1
#define BITNAME_C_bp 2
#define BITNAME_D_bp 3
#define BITNAME_E_bp 4
#define BITNAME_F_bp 5
#define BITNAME_G_bp 6
#define BITNAME_H_bp 7

which will then force you to use:

dest = (1 << BITNAME_x_bp);

if (SOMEREG & (1 << BITNAME_x_bp)) {...

or you can use:

#define BITNAME_A_bm 0x01
#define BITNAME_B_bm 0x02
#define BITNAME_C_bm 0x04
#define BITNAME_D_bm 0x08
#define BITNAME_E_bm 0x10
#define BITNAME_F_bm 0x20
#define BITNAME_G_bm 0x40
#define BITNAME_H_bm 0x80

then use:

dest = BITNAME_x_bm;

if (SOMEREG & BITNAME_x_bm) {...

Although some of this was corrected part way through the thread by #13 there was still stuff like:

    TWI0.MCTRLA|=(1<<TWI_ENABLE_bp)|(1<<TWI_WIEN_bp)|(1<<TWI_RIEN_bp);
    TWI0.MSTATUS|=1<<TWI_BUSSTATE_gp;

}

void UART_Write(uint8_t data)
{
    while(!(USART0.STATUS&(1<<USART_DREIF_bp)));
    USART0.TXDATAL=data;
    while(!(USART0.STATUS & (1<<USART_TXCIF_bp)));
}

which could be:

    TWI0.MCTRLA |= TWI_ENABLE_bm |  TWI_WIEN_bm | TWI_RIEN_bm;
    TWI0.MSTATUS |= TWI_BUSSTATE0_bm;

}

void UART_Write(uint8_t data)
{
    while(!(USART0.STATUS & USART_DREIF_bm));
    USART0.TXDATAL = data;
    while(!(USART0.STATUS & USART_TXCIF_bm));
}

The _bp stuff is only really there for the Asm programmer now when he wants to SBI or something.

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

clawson wrote:

Just re-reading this thread. I wonder what the attraction of (1 <<) and XXXX_bp actually is?

 

As AVR1000 says...

 

Quote:

4.2 Bit Positions

...

The bit position definitions are included for compatibility reasons. They are also needed when programming in assembly for instructions that use a bit number.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Cliff,

 

If you use field numbers rather than individual bit masks.

e.g. I tend to write (2 << COM1A0) rather than (1 << COM1A1)|(0 << COM1A0)

i.e.  I think in terms of mode number

 

The Xmega include files have many descriptive MACROs but not always.

 

Yes,   of course you use XXXXX_bm most of the time.

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

Oh yeah << has it's place. My classic example for a Sharp ARM7 with LCD controller is:

LCDCTRL = (640 << LCDXRES) | (480 << LCDYRES) ;

I simply don't think there's a more explanatory line of C for that 

 

(and if anyone can't see the LCD was being configured for 640x480 use you may want to reconsider a career in engineering!) 

 

But (1 << whatever) should never be needed. (in fact I wrote a whole Python header generator system for AVR to avoid the need) 

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

clawson wrote:

Just re-reading this thread. I wonder what the attraction of (1 <<) and XXXX_bp actually is?

 

 

It's an old habit. This is the first time I'm programming attiny. In the past I only programmer Atmegas, which as far as I know don't have bit mask already defined. I'm switching from bit positions to bit masks but as I said, it's a habit.

Last Edited: Wed. May 18, 2022 - 12:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

 

int main(int argc, char** argv) 
return (EXIT_SUCCESS);

Looks like you are probably a pc programmer. Not a bad thing, just some indications like the above that are used in pc programming but not mcu's.

 

 

 

Actually those two lines are what MPLAB IDE wrote in an empty project and I didn't bother changing it. 

I'm just a hobbyist programmer. Sometimes I do MCUs and sometimes I do computer programs.

Last Edited: Thu. May 19, 2022 - 06:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually those two lines are what MPLAB IDE wrote in an empty project

I guess that was the other option. I have used mplabx from the start and at some point I changed the project templates to make more sense for an mcu. In Tools-Templates you can change it to what you want.