AVR ATMeaga328 and MCP3201 with SPI connection

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

Now I am working with ADC with MCP3201 by reading the value from SPI connection. I understand the concept of SPI, but I still confuse how to keep the data from SPDR after SPIF is set and the result of mcp3201 is need to shift the bit, I don't know how to do it. Do you have any c language code example or tutorial to woking with MCP3201?

 

void SPI_init(void){

  DDRB = ((1<<DDB2)|(1<<DDB1)|(1<<DDB0)); // MOSI SCK,SS outputs
  DDRB |= (1 << PORTB2);
  SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR0);  // SPI enable, Master, f/16

}

uint16_t SPI_READ(){
   PORTB |= (1 << PORTB2)
   SPDR = 0xFF;
   while(!(SPSR & (1<<SPIF)));
   return SPDR
}

 

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

without looking... I bet there is arduino code for it......

 

but I am more wondering if you understand how embedded programming works at all.....

 

what is your intent here:

 DDRB = ((1<<DDB2)|(1<<DDB1)|(1<<DDB0)); // MOSI SCK,SS outputs
  DDRB |= (1 << PORTB2);

I think you made a small mistake here and the second line needs to adress the PORTB register and not the DDRB register....

Luckily for you DDRB2 and PORTB2 are the same value so you have 2 times set the PORTB pin2 pin to output low(keep in mind that default registers are 99.99% of the times '0' and thus for the port pin it means low when set to output.

 

then second this part:

uint16_t SPI_READ(){
   PORTB |= (1 << PORTB2)
   SPDR = 0xFF;
   while(!(SPSR & (1<<SPIF)));
   return SPDR
}

this actually is a garbage read... always.....

I assume PORTB2 pin is the SS or #CS pin. Normal SPI operation is that the line is low level for a chip to be 'activated' or selected...... You start with making the line high, so not selecting anything, and then do a SPI transfer.

 

I would suggest you check the data sheet of the device you are going to talk to how much delay there has to be between selecting the chip and actually starting to write data to it. You might need a bit of dead time between the two.

 

then when the write is done you return the SPDR  ( which is 8 bit ) into a 16bit variable. I wonder if you did not get any compiler warnings on that. This might cause the upper 8 bits to be of the "garbage"  kind.

 

If you need to get 16 bits from the chip you need to do atleast 2x 8 bit fetches again see the data sheet on the chip you use on how to do that, it will be described in there.

 

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


Euler01 wrote:
I still confuse how to keep the data from SPDR after SPIF is set

What, exactly is the confusion?

 

The received data is in the SPDR register - so you just read that into a suitable program variable.

 

The code you posted does exactly that:

uint16_t SPI_READ(){
   PORTB |= (1 << PORTB2)
   SPDR = 0xFF;
   while(!(SPSR & (1<<SPIF)));
   return SPDR
}

The 'return' statement reads the SPDR register, and uses the value read as the return value of the SPI_READ function.

 

As  meslomp said, SPDR is just an 8-bit register - so it doesn't make sense for SPI_READ() to return a uint16_t

 

The code as shown won't build - you are missing a couple of semicolons.

 

Note also that, by convention, names in ALL CAPITALS are reserved for preprocessor macros.

 

EDIT

 

See also:

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 11, 2021 - 08:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, for make you confuse. I followed your suggestion and got this.

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

#define CS PORTB2
#define CS_DDR DDB2

void USART_Init(unsigned int ubrr) {
    UBRR0 = ubrr;
    UCSR0B |= (1 << RXEN0) | (1 << TXEN0);
    UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
}

void USART_Transmit( unsigned char data ) {
    while ( !( UCSR0A & (1 << UDRE0)) );
    UDR0 = data;
}

void SPI_Init()
{
    DDRB |= (1 << CS_DDR)
    /* Enable SPI, Master mode, clk/16 */
    SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

uint16_t SPI_READ()
{
    uint8_t data1;
    uint8_t data2;
    uint16_t d_out;
    PORTB &= ~(1 << CS)                         // Chip select low

    SPDR = 0xFF;                                // put dummy byte in SPDR

    while(!(SPSR & (1<<SPIF)));                 // wait for SPIF high 

    data1 = SPDR;                               // copy SPDR out

    SPDR = 0xFF;                                // put dummy byte in SPDR

    while(!(SPSR & (1<<SPIF)));                 // wait for SPIF high     

    data2 = SPDR;                               // copy SPDR out

    PORTB &= ~(1 << CS);                        // Chip select high

    d_out = ((uint16_t)data2 << 8) | data1;     // Concat bit

    return d_out
}

int main(void) {

    USART_Init(103);
    SPI_Init();

    uint16_t sensor;
    unsigned char buffer[10];

    while (1) {
        sensor = SPI_READ()             // Read data from sensor

        sprintf(buffer,"%d",sensor)     // convert to string

        for(int i=0; buffer[i] != 0; i++){
            USART_Transmit(buffer[i])
        }

        _delay_ms(1000);
    }
}

,but I still don't know how to remove the unused bit. Form the data-sheet I need to remove 1 bit to got 7 bit for first fetch and remove 3 bit to get 5 bit for  second fetch. Then concatenate it to get 12 bits. Do you have any suggestions ?

 

Last Edited: Thu. Feb 11, 2021 - 09:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

 

Euler01 wrote:
I still don't know how to remove the unused bit.

But that has nothing to do with the SPI.

 

You do that once you've got the data stored into your own program variable(s) - so it's just a matter of standard 'C' data manipulation.

 

You need to "mask-out" the unwanted bits - take a look at this tutorial:

 

https://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101

 

EDIT

 

Euler01 wrote:
I need to remove 1 bit to got 7 bit for first fetch and remove 3 bit to get 5 bit for  second fetch

No - look again at your diagram:

  • You need to "get rid of" the top two bits of the first byte;
  • You want all of the bits of the second byte.

 

Then concatenate it to get 12 bits

You could concatenate first and then do the masking, or mask just the 1st byte and then do the concatenation - up to you.

 

EDIT 2

 

Oh, wait - there is a confusing bit:

 

You get the 12 useful bits (green), plus B1 repeated!

 

surprise

 

So you are going to have to extract that from your 2 bytes.

 

That probably will be easier done by concatenating first...

 

So the process is something like:

receive 1st byte;

receive 2nd byte;

concatenate;

mask-out top 3 bits of the concatenated value;

shift the masked concatenated value down by 1 bit.

 

You probably also need to keep that repeated B1 for the next transfer?

 

EDIT 3

 

This is all explained in the datasheet:

 

 

https://ww1.microchip.com/downlo...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 11, 2021 - 09:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you, for your suggestion I wrote the code from merlomp suggestion. Can you look on comment 3, I have more question ?

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

See #5 - our replies have crossed-over

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From Andy's diagrams it looks like t will be something like:

uint8_t high_byte, low_byte;
uint16_t result;

high_byte = SPI_READ();
low_byte= SPI_READ();

// ???BA987 65432101
// 00011111 11111110 - mask to isolate required bits
high_byte &= 0b00011111;
low_byte  &= 0b11111110;

// result = BA98 76543210
// first get 65432101 to 6543210 by a simple >>1
low_byte >>= 1;

// the "tricky bit" is getting the 7 bit from the bottom
// of the upper byte into the top of the low byte
// there's many ways you could do this but I think I'd
// just go for...
if (high_byte & 1) {
    low_byte |= 0b10000000;
}
// but now bit 7 is in both high and low so:
high_byte >>= 1; // 000BA987 becomes 0000BA98
// then finally combine the bytes:
result = (high_byte << 8) | low_byte;

 

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

there being many ways to skin a cat ...

uint8_t   rx_byte;    // A single byte read from SPI
uint16_t  rx_12bits;  // The 12-bit ADC value we require

rx_byte   =   SPI_READ();   // read the 1st byte

rx_byte   &=  0b00111111;   // mask-out the top 2 bits

rx_12bits =   rx_byte << 7; // put the masked byte into bits 15..7 of the result

rx_byte   =   SPI_READ();   // read the 2nd byte

rx_byte   >>= 1;            // shift it down 1 bit - to get rid of the repeated B1
                            // (this leaves a 0 in the top bit)

rx_12bits |=  rx_byte       // put the shifted byte into the bottom bits of the result
                            // (because the top bit of rx_byte is zero, it won't affect the B7 value already there)

@ Euler0: note that the source code could be shortened, by doing more than 1 step at a time; eg

uint8_t   rx_byte;    // A single byte read from SPI
uint16_t  rx_12bits;  // The 12-bit ADC value we require

rx_byte   =   SPI_READ() & 0b00111111; // read the 1st byte; ignore the top 2 bits

rx_12bits =   rx_byte << 7;            // put the masked byte into bits 15..7 of the result

rx_byte   =   SPI_READ() >>= 1;        // read the 2nd byte and shift it down 1 bit - to get rid of the repeated B1
                                       // (this leaves a 0 in the top bit)

rx_12bits |=  rx_byte                  // put the shifted byte into the bottom bits of the result
                                       // (because the top bit of rx_byte is zero, it won't affect the B7 value already there)

But this is unlikely to make any difference at all to the generated executable code - the compiler is smart enough to spot sequences that it can do "all in one go".

 

 

EDIT

 

How It Works:

 

1st byte

  7    6    5    4    3    2    1    0
+----+----+----+----+----+----+----+----+
| ?? | ?? |  0 | B11| B10|  B9|  B8|  B7|   1st byte, read from SPI
+----+----+----+----+----+----+----+----+

+----+----+----+----+----+----+----+----+
|  0 |  0 |  0 | B11| B10|  B9|  B8|  B7|   masked
+----+----+----+----+----+----+----+----+


     +----+----+----+----+----+----+----+----+
     |  0 |  0 |  0 | B11| B10|  B9|  B8|  B7|
     +----+----+----+----+----+----+----+----+
        |    |    |    |    |    |    |    |    
        |    |    |    |    |    |    |    |    shift-up 7 - into bits 14-7 of result
        V    V    V    V    V    V    V    V

  15   14   13   12   11   10    9    8 : 7    6    5    4    3    2    1    0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|  0 |  0 |  0 |  0 | B11| B10|  B9|  B8|  B7|  0 |  0 |  0 |  0 |  0 |  0 |  0 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+


                                        +----+----+----+----+----+----+----+----+
2nd byte, read from SPI                 |  B6|  B5|  B4|  B3|  B2|  B1|  B0|  B1|
                                        +----+----+----+----+----+----+----+----+

                                        +----+----+----+----+----+----+----+----+
shifted down                            |  0 |  B6|  B5|  B4|  B3|  B2|  B1|  B0|
                                        +----+----+----+----+----+----+----+----+
                                           |    |    |    |    |    |    |    |    
copy into low byte of result               |    |    |    |    |    |    |    |
                                           V    V    V    V    V    V    V    V
  15   14   13   12   11   10    9    8 :  7    6    5    4    3    2    1    0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|  0 |  0 |  0 |  0 | B11| B10|  B9|  B8|  B7|  B6|  B5|  B4|  B3|  B2|  B1|  B0|
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
                                           ^
                                           ^ OR-ing in a zero here leaves B7 intact 
;

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 11, 2021 - 11:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@awneil , @clawson Thank you, both of you. Your explanation is very good that help me a lot. smiley

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

you're welcome.

 

If that's resolved the issue, please mark the solution - see Tip #5 in my signature, below:

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

in #9, I wrote:
How It Works

 

Just to note, when you're stuck with something like this - drawing it out using pencil and paper  is a great way to help figure it out.

 

Also when you're checking some code - step it through manually, noting what's happening

 

#PencilAndPaper #DrawADiagram #ManualSimulator

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 11, 2021 - 11:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
drawing it out using pencil and paper 
yeah I do that too. But sometimes (as above) I put my "bit shuffling thoughts" into the comments to work out what to do so it's then an aide to the maintainer when they come back to this in a few years.

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

clawson wrote:
I put my "bit shuffling thoughts" into the comments

Absolutely!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...