1

I am currently writing a code to write on an LCD screen pixel by pixel. The code works fine, however the speed at which the code is processed is incredibly slow. The goal is simply to write number on the LCD screen so I am using the "switch" function with a "for loop" to read each of the bit I will activate. I am wondering if someone could tell me a way to speed up my code...

int* switch_library_number_1(int num, int octet) {

switch(num)
{

case 0 : ;
    int number_0 [] = {0x80, 0x08,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, ...};

        int * pNumber_0 = &number_0[octet];

        return pNumber_0;
          break;

case 1 : ;
    int number_1 [] = {0x80, 0x08,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, ...};

    int * pNumber_1 = &number_1[octet];

    return pNumber_1;
      break;
}

Then it goes up to nine like that, I don't think you need to seem all the cases. Plus even if I deleted most of them, I have 522 bytes by number. The rest of the code goes as fallow :

int main(void)
{
ADC_Initialization();
SPI_Initialization();
int nombre_octet = 522;
int premier_nombre;
int deuxieme_nombre;

while(1)
{
    GPIOA->BSRRL = CS;
    for(int i = 0; i < nombre_octet; i++)
    {
        write_spi(*switch_library_number_1(0, i));
    }
    GPIOA -> BSRRH = CS;

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

    GPIOA->BSRRL = CS;
    for(int i = 0; i < nombre_octet; i++)
    {
        write_spi(*switch_library_number_2(1, i));
    }
    GPIOA -> BSRRH = CS;

    }
}

Finally, here is the write_SPI function, but due to it's simplicity, I don't think that it is the problem.

void write_spi(char data)
{
    SPI1->DR = data;

    while (!(SPI1->SR & SPI_I2S_FLAG_TXE));
    while (!(SPI1->SR & SPI_I2S_FLAG_RXNE));
    while (SPI1->SR & SPI_I2S_FLAG_BSY);
}

Thanks in advance!

glts
  • 21,808
  • 12
  • 73
  • 94
  • #1 here is that we don't use `int` in embedded systems, because it is signed, which we typically never want, and of unknown size, which makes it non-portable. Instead we use `stdint.h` types. `char` is even worse, since we don't even know if it is signed or not. – Lundin Mar 13 '19 at 10:01
  • #2 is reading up about how to use local variables in functions: [Can a local variable's memory be accessed outside its scope?](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope). (No, it cannot...) – Lundin Mar 13 '19 at 10:08

2 Answers2

1

I quite like the way you split your code into three snippets. I can suggest improvements for each of them:

switch_library_number_1():

  • This could be just a 2D array, number[][], or if number_0, number_1... are not of the same length, it could be an array of pointers to these. There would need to be checks for valid num and offset. This might be a minor speed improvement.
  • Your number_0... arrays are currently on stack, and read-write. Make them const, so they won't use RAM.
  • Currently you are returning a pointer to memory location on stack - this doesn't normally work, if it does it's by luck and accident. You should not access stack data when you're out of scope (function) where it's been defined. static const would make this safe, as it wouldn't be on stack anymore.

main loop:

  • It's a bit odd to call switch_library_number_1/2 on each loop iteration. You know your data will just be in array. This could probably be replaced by write_spi(number[0][i]); if number array is properly set up. This should get you some speed improvement, as it very much simplifies data fetching.
  • You appear to have a busy loop. That's a tricky practice (I bet 100 is a guess, and note that compiler could optimise this loop away). If possibly use some library provided delay function or a timer to get precise delays. Is this an actual requirement of SPI slave?

write_spi(char data):

  • char should be unsigned char here. chars might be signed or unsigned, so when you're using them as bytes (not actual string characters), you should specify signedness.
  • You seem to wait for every byte transmission to finish, which is safe, but a bit slow. Normally this can be rewritten into a faster alternative of wait_for_SPI_ready_for_TX; SPI_TX, where you only wait before sending next byte. Note that you will also need to wait for byte to be transmitted fully before pulling CS back high again. This could be a big speed improvement.

Some other things to consider:

  • What's the actual SPI clock? There may be huge speed improvements if clock is increased.
  • How did you measure this to be "slow"? Does it point to slow parts of code (what are those then? If not obvious from C, what are they assembled to?)
  • Have you got an oscilloscope/logic analyser to look at actual signals on wire? This may provide useful data.
domen
  • 1,819
  • 12
  • 19
  • 1
    There shouldn't be any busy loops at all, neither broken ones as in the OP nor library ones. Just check SPI status flags. – Lundin Mar 13 '19 at 10:03
  • The busy loop is between two SPI transfers. It might be needed, as some slaves have quite strange requirements. – domen Mar 13 '19 at 10:34
  • That ought to be clarified with comments then. – Lundin Mar 13 '19 at 10:35
  • I agree, I've added a bit to the answer. – domen Mar 13 '19 at 10:36
  • 1
    Thank you very much for these advices! I have implemented all of your recommandations, and indeed the speed increased to a very acceptable level. Also great explanation on each tips, it really helped. For the other questions, my SPI clock is set at 2 MHz (system constraint). I measured this to be "slow" because it was taking around 15 secs to print 4 number on the LCD screen. It was indeed the way that I used the switch function that slowed the data. – Nathaniel Brochu Mar 14 '19 at 15:22
0

I had a similar problem with STM32F207 Series Cortex-M3 controller, when I observed the TX line through Oscillator, I saw that CHIP_SELECT disable was taking too much time to set in, after all the data has sent.I figured out it is related to flag controls So ı play with the control flags a little bit, Here how it worked out just fine for me;

static void SPI_Send(uint16_t len,uint8_t* data)
{
   uint16_t i;

   for(i = 0;i<len;i++)
   {        
     SPI_I2S_SendData(SPI1,*(data+i));
     while(!(SPI1->SR & SPI_SR_TXE));   
   }    
   while(SPI1->SR & SPI_SR_BSY);
   CHIP_SEL_DISABLE;
}

I believe it is slow because you are also checking the 'Receive Buffer Not Empty' where you don't need to.

İlkerK
  • 76
  • 4
  • We don't even know if the code is using manual or automatic /SS, so you are just guessing here. Also, the problem you describe is typically an issue at the receiver-side hardware rather than on the MCU side. SPI is very poorly standardized and all manner of bastard hardware exists. – Lundin Mar 13 '19 at 10:06
  • I don't understand what you ment by manual/automatic, did you mean, ıf the code is using kernel drivers etc ? Anyways, In my case when I observe the TX line going out from SPI Peripheral of the MCU, I observed that it was taking too much time to set CS pin to high after all the data has sent. And configuration above fixed it.Though still you might be right, I am not such an expert. But I would be glad If you could explain how the receiver side could effect the transmission side. Is it like the RX capacitance effecting the corresponding flag register resulting in more time to flag to set in ? – İlkerK Mar 14 '19 at 10:43
  • 2
    Pretty much all SPI hardware has two options: the hardware can handle /SS automatically or otherwise you can do it manually with GPIO. The receiver-side hardware has timing specs, not necessarily following any standard. – Lundin Mar 14 '19 at 11:47
  • I do control the ss manually in my code. Thanks for the solution! – Nathaniel Brochu Mar 14 '19 at 15:24