special considerations for 16-bit operations?

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

Hi all,

 

I am using an ATXMEGA16E5.

 

It is configured as a slave in SPI mode and should store some data it receives in a table. The structure looks like this:

 

typedef struct {
	uint8_t status;
	uint8_t session;
	uint8_t num;
	Struct2 list[62];
	uint16_t crc;
} TableStruct;

typedef struct {
	uint8_t arr[12];
	uint8_t counter;
} Struct2;

TableStruct scanList;

The bytes I receive from SPI should be saved in scanList.list.arr (not in counter); I use a variable initialized at 0, and increase it after each byte is received and saved:

uint8_t aux;

Of course I make this variable 0 when a new packet is received. And this is how I save data:

scanList.list[aux/12].arr[aux%12] = data; //data contains the byte received from spi
aux++;

As you can see, it is like filling a 2-dim array (62x12) so every 12 bytes I change of row.

However, since aux is an 8-bit variable, I can only receive 21 arrays of 12 bytes (252 bytes). I thought that by changing aux to be a 16-bit variable, I could store more bytes, but it did not work. It stores up to 2 arrays correctly, but after the 2nd one, it loses a byte per array. Do you have any idea why?

I am not sure what happens with those lost bytes, but I can see they are not saved in their right position because I check SRAM memory, debugging with an AVR Dragon. They are just skipped and the rest of the bytes are shifted, (like if the lost byte was not received). I also verified SPI communication and the master does send them.

 

I know I can change the way I save the bytes to avoid this issue, but I still wonder why did changing the size of my variable cause the program to fail? Are there any special considerations with 16-bit operations?

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm.

 

I think all you would need is:

uint16_t aux;

Of course, you could do:

uint8_t pArr = 0;
uint8_t pList = 0 ;

scanList.list[pList].arr[pArr] = data; //data contains the byte received from spi
pArr += 1;
if (pArr==12)
  {
      pList+= 1 ;
      pArr = 0 ;
  }

Which might actually be faster than the division.

274,207,281-1 The largest known Mersenne Prime

Measure twice, cry, go back to the hardware store

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

I thought that by changing aux to be a 16-bit variable, I could store more bytes, but it did not work.

Tell us more about "it did not work".

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

Torby,

When I did not need to store more than 255 bytes, I used that variable "aux" to store the total arrived bytes and compare it to my expected total, so that I break the routine when they were equal. That is why I found it easy to use it that way. Now, after changing its size and noticing the program did not work, I am using an 8-bit pointer and increment it after each byte is stored; and then increment it again after 12 bytes (to avoid the "counter" space).  However I was still wondering why my first solution did not work after changing the size.

 

Clawson,

esal26 wrote:

I am not sure what happens with those lost bytes, but I can see they are not saved in their right position because I check SRAM memory, debugging with an AVR Dragon. They are just skipped and the rest of the bytes are shifted, (like if the lost byte was not received). I also verified SPI communication and the master does send them.

I started a debug session in Atmel Studio with AVR Dragon and used a logic analyzer to verify SPI communication. After the master sent the data, I paused the program to verify SRAM memory. The first two arrays were stored correctly. But the others were not. I compared each byte from SPI with memory and some were skipped. For example, let's say the third and fourth arrays were:

0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xAA 0xBB 0xCC, 0x00 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xAA 0xBB

 

In memory, I saw:

0x11 0x22 0x33 0x44 0x55 0x77 0x88 0x99 0xAA 0xBB 0xCC 0x00 0x01 0x11 0x22 0x33 0x44 0x55 0x66 0x88 0x99 0xAA 0xBB...

(where 0x01 is the "counter" space in memory, I don't care about it)

 

I could see how the bytes where skipped as if they had never arrived. Do operations with 16-bit variables take that longer enough to lose an interrupt?

 

Last Edited: Wed. Sep 24, 2014 - 04:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd just put a breakpoint on:

aux++;

Are you really suggesting that when it's a uint16_t it cannot count beyond 2 ?!?

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

Clawson, thanks for your reply.

 

I don't like to put breakpoints within interrupts code. That is why I checked memory at the end of the transmission instead.

 

And if the problem were there, it would not be 2 but some number greater than 25, because "aux" does not count the arrays, it counts the bytes. As you can notice,

scanList.list[aux/12].arr[aux%12] = data; //data contains the byte received from spi

Stores the byte in row "aux/12", column "aux%12".

 

However, given that some bytes are actually stored after the lost bytes, I could never be suggesting that. I guess there was a problem with /12 and %12 operations, perhaps 16-bit divisions take longer enough to miss a byte, which now I think is the most likely answer to my question. So, I may add "avoid 16-bit divisions in interrupts" to my "best xmega programming practices" list.

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

Well, division IS pretty slow. How fast are your bytes a-commin'?

 

274,207,281-1 The largest known Mersenne Prime

Measure twice, cry, go back to the hardware store

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

The SPI frequency is 4MHz, 8 Bits per transfer. My xmega is running with internal 32MHz oscillator.

And the time between two bytes is 10us (without considering transmission time).

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

That's plenty of time. The division isn't the issue. But then, I'm a little mystified what is the issue.

274,207,281-1 The largest known Mersenne Prime

Measure twice, cry, go back to the hardware store

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't like to put breakpoints within interrupts code

Eh? f one believe some is wrong in ISR code then where else would you want to be placing the breakpoints?!?

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

Torby,

The issue should be there: I added a toggle of a pin after that line of code to count the number of interrupts. I could see how after the missing bytes arrived the pin did not change its state. So I was actually missing those bytes. I changed aux to uint8_t again and that did not happen.

 

Clawson,

I understand, I may have generalized to avoid explanations. The thing is that I cannot control how fast the master sends the bytes and therefore some interrupts will be missed. I added a hit count condition, but the AVR Dragon seemed to never met it. Then I realized I could debug by looking at the table in memory after the whole package arrived, and thought it would take less time than trying to hit the breakpoint exactly when one interrupt was failing, or send bytes more slowly from master. So, I had actually added a breakpoint as you suggested, but anyway could not see what happened.

 

Anyway, after I added the toggle, I saw that the problem is that some SPI interrupts are missed. The problem is not the operations result, but the time they take. This is supported by the fact that more bytes are received correctly before the first missed byte, given that the xmega has 2-buffers in SPI reception.

 

Thank you both for your hints!

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

I have a feeling that this could be solved by some clever data breakpoints, if you don't want to use program breakpoint in the ISR.... Try to break when some global variable or position gets a value, e.g break on the missing by in the missing position and see if you achieve a break condition, or break on a known condition that happens 'simultaneously' so that you can inspect the state?

 

Other tricks to get a feel for the issue could be to short-circuit good data in the ISR, and see if the lost data magically appears, indicating that it's a ISR blocking issue... 

 

(just thoughts and ramblings, too tired to read the whole thread)

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)