SPI with Atmega32 and At42QT2100

193 Views Asked by At

I have a problem to communicate from an Atmega3216PU to a at42qt2100.

The datasheets are: Atmega32PU16 and AT42QT2100

This is my code:

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#define F_CPU  16000000
#define UART_BAUD_RATE  9600
#define UART_BAUD_REGISTERS  (((F_CPU / (UART_BAUD_RATE * 16UL))) - 1)

int printCHAR(char character, FILE *stream)
{
    while ((UCSRA & (1 << UDRE)) == 0) {};
    UDR = character;
    return 0;
}
FILE uart_str = FDEV_SETUP_STREAM(printCHAR, NULL, _FDEV_SETUP_RW);

void setup(){
    // Init SIO
    UCSRB |= (1 << RXEN) | (1 << TXEN);
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);

    UBRRL = UART_BAUD_REGISTERS;

    fprintf(&uart_str, "");
    stdout = &uart_str;
    printf("");

    //Init spi
    //set MOSI, SCK and SS output, all others input
    DDRB |= (1<<DDB7)|(1<<DDB5)|(1<<DDB4);
    SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)|(1<<CPHA)|(1<<CPOL);

}

void loop(){

    uint8_t data = 0b01000000; //Sending this sequence should return device id
    printf("Sending: %d \n",data);
    _delay_ms(10000);
    PORTB &= 0b11101111; // SS auf LOW!! (Pin4)
    SPDR = data;
    while(!(SPSR & (1<<SPIF)));
    PORTB |= (1<<PB4); // SS auf HIGH!! (Pin4)
    printf("Receiving: %d \n",SPDR);
    _delay_ms(10000);
}

void main(){
    setup();
    while(1){
        loop();
    }
}

I should get an answer from the at42qt2100 with the device id 108 but i always get 0. This ist the output: Sending: 64 Receiving: 0

Does anybody see a misconfiguration?

1

There are 1 best solutions below

0
On BEST ANSWER

I did a quick overview of the AT42QT2100 datasheet that you reference and I see a couple of problems right away:

  1. After SS is set low, the host must wait >2us (in Free Run mode) before starting SCK. You are immediately initiating a SPI transfer and, if you are running a 16MHz, that means you may be starting the transmission as soon as 62.5ns after setting SS low.
  2. The datasheet also says that the host must send 3 data bytes within 10ms or the transaction will be treated as an error and the communication exchange will be reset. You are sending 1 byte and then waiting 20s before sending the next byte.
  3. The datasheet says that the device settings become active after the 3 bytes have been sent. That means that 3 more bytes must be sent to get the response to your command.

You should be doing something more along the lines of this:

#define NUM_DATA_BYTES   3
void loop(){
    uint8_t sendData[NUM_DATA_BYTES] =
    {
        0b01000000, /* Device Version Command and default values. */
        0b00000000, /* Default Value for Byte 2. */
        0b11000000, /* Default Value for Byte 3. */
    };
    uint8_t receiveData[NUM_DATA_BYTES] = { 0 };
    uint8_t i;

    PORTB &= 0b11101111; // SS auf LOW!! (Pin4)

     /* Ensure that >2us delay requirement is met.  Although, the 
        for() loop below will probably introduce enough delay. */
    _delay_ms(1);

    /* Send command data. */
    for (i = 0; i < NUM_DATA_BYTES; i++)
    {
            SPDR = sendData[i];
            while(!(SPSR & (1<<SPIF)));
    }
    PORTB |= (1<<PB4); // SS auf HIGH!! (Pin4)

    /* Delay here may be unnecessary, but it ensures that timing
       issues do not occur. */
    _delay_ms(1);
    PORTB &= 0b11101111; // SS auf LOW!! (Pin4)
    _delay_ms(1);

    /* Receive device response data. */
    for (i = 0; i < NUM_DATA_BYTES; i++)
    {
            SPDR = sendData[i];
            while(!(SPSR & (1<<SPIF)));
            receiveData[i] = SPDR;

            printf("Receiving byte %d: %d \n", (i + 1), receiveData[i]);
    }
    PORTB |= (1<<PB4); // SS auf HIGH!! (Pin4)

    _delay_ms(10000);
}

I am sure this code is not perfect, but it should help to get you on the right track. Good luck!