1. This forum section is a read-only archive which contains old newsgroup posts. If you wish to post a query, please do so in one of our main forum sections (here). This way you will get a faster, better response from the members on Motherboard Point.

SPI slave on ATmega162: transferring data

Discussion in 'Embedded' started by Roman Mashak, Dec 9, 2004.

  1. Roman Mashak

    Roman Mashak Guest

    Hello, All!

    I keep fighting with atmega162 and its SPI :)

    So now I configured slave mode. The purpose is to send 8 bytes one after
    another. I tested and debugged all code in simulator hundreds of times, I
    watched MCU registers, SPI registers and etc., everything seems to be fine,
    but after burning in chip - some weird problem.

    After receiving data from me, master is logging them onto the screen, so I
    can watch what's going on. Indeed it happenes, data got by master are
    strange and not what I sent.

    My chip is ATmega162, frequency 8MHz. Here is code:

    char *data = "123456780";
    ....

    /*
    SPI init
    Mode: slave
    MISO: output
    MOSI: input
    SS: input
    SCK: input
    */
    void SPI_SlaveInit(void)
    {
    DDRB = (1 << PB6);
    SPCR = (1 << SPIE) | (1 << SPE); // slave and enable SPI

    PORTB |= (1 << PB6);
    }

    void SPI_SendByte(char *data)
    {
    SPDR = *data;
    while ( !(SPSR & (1 << SPIF)) )
    ;
    }

    SIGNAL(SIG_SPI)
    {
    char *t;

    t = data;
    while ( *data != 0 ) {
    SPI_SendByte((char *)data);
    data++;
    }

    data = t;

    LED0_blink(); // for debug: flush LED
    }


    int main(void)
    {
    sei();
    SPI_SlaveInit();
    .........
    return 1;
    }

    And now I don't know, where is my problem has hidden.

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #1
    1. Advertisements

  2. What data is received, what do you see on the screen?

    Meindert
     
    Meindert Sprang, Dec 9, 2004
    #2
    1. Advertisements

  3. Roman Mashak

    Gary Kato Guest

    So now I configured slave mode.

    I have a feeling that the problem is that you aren't thinking how a slave
    should react. You are still thinking as if it is a master. A slave only
    responds to a request from a master. A slave does not initiate transfers.

    If you really want to see just your data come across, you need to setup the
    first data byte on initialization. When the master starts clocking, so will the
    slave. I don't know what triggers your SIG_SPI, maybe the toggling of SS? If
    so, then you have only the amount of time from the changing of SS to the Master
    starting to clock in order to get your data into the data register if it wasn't
    put there previously. I'm not sure what happens if you change the data register
    while the shifting is happening. Maybe the 162 will save it away in a buffer to
    be written after the transfer or maybe it won't allow the change, or maybe it
    will allow the change and the master will get some bits from the old data and
    some from the new data.

    And remember that the data register gets overwritten by watever the master
    sent.

    What you probably want to do in this specific case is:

    on slave init: setup for SPI Slave, then write first byte of data into SPDR.

    after transmission complete: get next data byte and write into SPDR for next
    transmission.
    Quite frankly, I also don't like this. You change the global variable "data"
    and then restore it. Why not just change the temporary "t" and do away with the
    restore? Only change a global if that side effect is required.
     
    Gary Kato, Dec 9, 2004
    #3
  4. Roman Mashak

    Roman Mashak Guest

    Hello, Gary!
    You wrote on 09 Dec 2004 11:40:57 GMT:

    GK> If you really want to see just your data come across, you need to setup
    GK> the first data byte on initialization. When the master starts clocking,
    Do you mean, I should write byte into SPDR at my SPI_SlaveInit function? If
    so, should I also wait for transfer complete or at that stage it's quite
    enought just store value in the SPDR register?
    But if slave doesn't initiate a transfer it can silently wait for a moment
    when master starts transfer. I don't quite understand why should I write
    byte at slave init?
    GK> so will the slave. I don't know what triggers your SIG_SPI, maybe the
    GK> toggling of SS? If so, then you have only the amount of time from the
    As I understood datasheet, interrupt will be invoked as soon as transfer of
    byte will be completed.
    GK> changing of SS to the Master starting to clock in order to get your
    GK> data into the data register if it wasn't put there previously. I'm not

    [skip]

    GK> What you probably want to do in this specific case is:

    GK> on slave init: setup for SPI Slave, then write first byte of data into
    GK> SPDR.

    GK> after transmission complete: get next data byte and write into SPDR for
    GK> next transmission.
    ok, I'll follow your advice
    ??>> char *t;
    ??>>
    ??>> t = data;
    ??>> while ( *data != 0 ) {
    ??>> SPI_SendByte((char *)data);
    ??>> data++;
    ??>> }
    ??>> data = t;

    GK> Quite frankly, I also don't like this. You change the global variable
    "data"
    GK> and then restore it. Why not just change the temporary "t" and do away
    with the
    GK> restore? Only change a global if that side effect is required.
    In this case, may be it's better to declare 'char *t' as global volatile
    variable and use it, or it's rather useless ?

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #4
  5. Roman Mashak

    Roman Mashak Guest

    Hello, Meindert!
    You wrote on Thu, 9 Dec 2004 11:55:22 +0100:

    ??>> After receiving data from me, master is logging them onto the screen,
    ??>> so I can watch what's going on. Indeed it happenes, data got by master
    ??>> are strange and not what I sent.

    MS> What data is received, what do you see on the screen?
    on the screen I see like this:
    0x01 0x02 0x01 0x00 0x04 0x03 0x02 0x01

    ans so on

    and from time to time the order of data is changed.

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #5
  6. Roman Mashak

    Roman Mashak Guest

    Hello, Gary!
    You wrote on 09 Dec 2004 11:40:57 GMT:


    GK> And remember that the data register gets overwritten by watever the
    GK> master sent.

    GK> What you probably want to do in this specific case is:

    GK> on slave init: setup for SPI Slave, then write first byte of data into
    GK> SPDR.

    GK> after transmission complete: get next data byte and write into SPDR for
    GK> next transmission.
    I don't quite understand if I should check SPIF flag in case of interrupt
    driven code?

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #6
  7. Below, you pass a pointer to a character:
    And here, you pass a character to SPI_SendByte, while it expects a
    pointer...
    Can't be right.

    Meindert
     
    Meindert Sprang, Dec 9, 2004
    #7
  8. Roman Mashak

    Roman Mashak Guest

    Hello, Meindert!
    You wrote on Thu, 9 Dec 2004 13:58:24 +0100:

    MS> Below, you pass a pointer to a character:
    I changed the code and simplified a little, now I don't use pointers and
    send only one byte at all:

    ....
    char *t_data = "1";

    void SPI_SendByte(char data)
    {
    SPDR = data;
    while ( !(SPSR & (1 << SPIF)) )
    ;
    }

    SIGNAL(SIG_SPI)
    {
    SPI_SendByte(*t_data);
    ......
    }

    nt main(void)
    {
    Timer1_Init();
    sei(); // enable global interrupt
    SPI_SlaveInit();

    // write first byte outside ISR to trigger interrupt
    SPI_SendByte(*t_data);

    while (1)
    {
    ...
    }
    }

    But still the same issue...

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #8
  9. The problem with the code above (I assume it is the code for the master) is
    that you call spi_sendbyte from within the interrupt handler. Spi_sendbyte
    waits for the SPIF flag. The moment this flag is set, another interrupt is
    issued while you are still inside spi_sendbyte. You can safely remove the
    while loop in spi_sendbyte because you only call it once at the start of the
    communication. After that it is called from the interrupt handler only when
    the shifter is empty. So, no need to wait for the SPIF flag. And when the
    while loop is removed, you'll that the only line left in spi_sendbyte can be
    put in the interrupt handler to save a pair of unnecessary call/return.

    Meindert
     
    Meindert Sprang, Dec 9, 2004
    #9
  10. Roman Mashak

    Roman Mashak Guest

    Hello, Meindert!
    You wrote on Thu, 9 Dec 2004 14:40:44 +0100:

    MS> The problem with the code above (I assume it is the code for the
    MS> master) is that you call spi_sendbyte from within the interrupt
    this is code for slave
    MS> handler. Spi_sendbyte waits for the SPIF flag. The moment this flag is
    MS> set, another interrupt is issued while you are still inside
    MS> spi_sendbyte. You can safely remove the while loop in spi_sendbyte
    Alright, I understand you. Here is changed version, I hope is correct :)
    MS> because you only call it once at the start of the communication. After
    MS> that it is called from the interrupt handler only when the shifter is
    MS> empty. So, no need to wait for the SPIF flag. And when the while loop
    MS> is removed, you'll that the only line left in spi_sendbyte can be put
    MS> in the interrupt handler to save a pair of unnecessary call/return.

    char *t_data = "1";
    ....

    SIGNAL(SIG_SPI)
    {
    SPDR = *t_data;
    LED0_blink(); // debugging feature
    }

    void SPI_SlaveInit(void)
    {
    DDRB = (1 << PB6);
    SPCR = (1 << SPIE) | (1 << SPE); // select slave and enable SPI

    PORTB |= (1 << PB6);
    }

    int main(void)
    {
    sei();
    SPI_SlaveInit();
    SPDR = *t_data; //write byte to trigger interrupt

    while (1)
    {
    .....
    }

    return 1;
    }

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 9, 2004
    #10
  11. Almost. You do not detect whether you are at the end of the array or ready
    sending data.

    Meindert
     
    Meindert Sprang, Dec 9, 2004
    #11
  12. What is the frequency of the SCK signal from the master? Sometimes
    simulators will respond to clock signals too fast for the actual
    hardware.
    Most of the SPI systems I've worked with use a separate signal as
    a chip select. An SPI slave device will generally require a
    low-going chip select from the master device to inform it that
    a transfer is pending. In your case, you could use that
    incoming low-going CS signal to start the transfers. However,
    you will need to ensure that there is enough time between the
    CS signal and the start of the transfer. As noted in another
    post, you may have to 'pre load' the SPI data register
    so that the hardware can send the byte as soon as the
    master starts SCK. The CS interrupt service routine then simply
    loads the NEXT byte when the first transfer is done and
    polls the SPIF bit to determine when to load subsequent
    bytes.

    If you rely on an SPIF interrupt, you need VERY fast
    interrupt service to get the next byte into the output
    register before clocking starts, if the master is
    requesting bytes one after the other in continuous
    fashion.
    Mark Borgerson
     
    Mark Borgerson, Dec 9, 2004
    #12
  13. And if the LED0_blink() takes longer than 8 SCK cycles to execute,
    you're going to have problems with continuous data. If LED0_Blink()
    simply sets a flag so that the main routine can blink the LED,
    you may be OK.

    You haven't said what SCK rate is being used by the master. If
    the slave is being implemented on an 8MHz machine, I suspect it
    may not like SCK signals in excess of 2MHz. If the SCK is
    2Mhz, You don't have a lot of machine cycles to respond
    to the interrupts.


    I would suggest starting with a clock rate in the 100KHz region,
    with perhaps a millisecond between transfers at the host end.
    When that works, you can start increasing the SCK rate.
    One of the nice things about being an SPI slave is that you
    don't really have to worry about slow clock rates.


    Mark Borgerson
     
    Mark Borgerson, Dec 9, 2004
    #13
  14. Roman Mashak

    CBFalconer Guest

    It is more efficient to do other things while the transmitter
    becomes ready, i.e. use:

    while (!(SPSR % (1 << SPIF))) continue;
    SPDR = data;
     
    CBFalconer, Dec 9, 2004
    #14
  15. Roman Mashak

    Roman Mashak Guest

    Hello, Meindert!
    You wrote on Thu, 9 Dec 2004 15:27:20 +0100:

    ??>>
    MS> Almost. You do not detect whether you are at the end of the array or
    MS> ready sending data.
    I don't have any array, I'm sending just 1 byte.

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 10, 2004
    #15
  16. Roman Mashak

    Roman Mashak Guest

    Hello, Mark!
    You wrote on Thu, 09 Dec 2004 15:35:51 GMT:

    MB> And if the LED0_blink() takes longer than 8 SCK cycles to execute,
    MB> you're going to have problems with continuous data. If LED0_Blink()
    MB> simply sets a flag so that the main routine can blink the LED,
    MB> you may be OK.
    Yeah,, may be that's the main issue. My LED0_blink() function is pretty
    dumb and I'm not sure it's fast enought to jump out of ISR:

    void LED0_blink(void)
    {
    uint8_t i=0;
    DDRC = 0x01;

    // flash LED (LED is inverse)
    PORTC &= ~(1 << PC0);
    for (i = 0; i < 50; i++) {
    _delay_loop_2(100);
    }

    // down LED
    PORTC |= (1 << PC0);

    for (i = 0; i < 50; i++) {
    _delay_loop_2(100);
    }

    }

    MB> You haven't said what SCK rate is being used by the master. If
    MB> the slave is being implemented on an 8MHz machine, I suspect it
    MB> may not like SCK signals in excess of 2MHz. If the SCK is
    MB> 2Mhz, You don't have a lot of machine cycles to respond
    MB> to the interrupts.
    SCK rate of master is 500KHz
    MB> I would suggest starting with a clock rate in the 100KHz region,
    MB> with perhaps a millisecond between transfers at the host end.
    MB> When that works, you can start increasing the SCK rate.
    MB> One of the nice things about being an SPI slave is that you
    MB> don't really have to worry about slow clock rates.

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 10, 2004
    #16
  17. Roman Mashak

    Gary Kato Guest

    My LED0_blink() function is pretty
    Always do as little as possible in your interrupt service routines.
     
    Gary Kato, Dec 10, 2004
    #17
  18. Yes, but you're sending it over and over again until you hit the reset
    button.

    Meindert
     
    Meindert Sprang, Dec 10, 2004
    #18
  19. Roman Mashak

    Roman Mashak Guest

    Hello, Meindert!
    You wrote on Fri, 10 Dec 2004 07:02:42 +0100:

    MS>>> Almost. You do not detect whether you are at the end of the array or
    MS>>> ready sending data.
    ??>> I don't have any array, I'm sending just 1 byte.

    MS> Yes, but you're sending it over and over again until you hit the reset
    MS> button.
    Do you mean, some flag indicating the status of process should be included
    in code?

    With best regards, Roman Mashak. E-mail:
     
    Roman Mashak, Dec 10, 2004
    #19
  20. Well, it depends on what you want to achieve. Your original code sent out a
    string, there you have to test for the end of the string. Later, you changed
    your code to send only one byte, to see what went wrong. But that code keeps
    sending indefinately because of the interrupt routine that gets called after
    you send the first and only byte 'manually'. Oh, and as other already
    pointed out: in an interrupt routine that runs very frequently (do you
    actually know how often it is executed? You should!), you cannot blink a LED
    and then wait in a nested loop. During that loop, the interrupt routine
    might get called another few 10-100 thousand times but not executed because
    the processor inhibits interrupts unless you enable them explicitly within
    the interrupt handler. If all this sounds like chinese to you, with all
    respect try something more easy to achive until you thoroughly understand
    what the processor is doing during interrupts.

    Meindert
     
    Meindert Sprang, Dec 10, 2004
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.