This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Avoiding HardFault and Optimizing Code Size with TWI Transaction Manager

I recently experimented with the TWI Transaction Manager and found the following:

This function would cause HardFault Error

void _BAD_read_func(app_twi_t* param_p_app_twi, 
                      uint8_t* param_p_reg_addr, 
                      uint8_t* param_p_buffer, 
                      uint8_t byte_cnt)
{
  app_twi_transfer_t        const transfer_1[] = 
  {
    HTS221_READ(param_p_reg_addr, param_p_buffer, byte_cnt)
  };

  app_twi_transaction_t     const transaction_1 =
  {
      .callback             = custom_app_twi_callback,
      .p_user_data          = NULL,
      .p_transfers          = transfer_1,
      .number_of_transfers  = sizeof(transfer_1)/sizeof(transfer_1[0])
  };

  APP_ERROR_CHECK(app_twi_schedule(param_p_app_twi, &transaction_1));
}

But this would not

uint8_t reg_addr = HTS221_WHO_AM_I_REG_ADDR;
uint8_t m_buffer[50];

app_twi_transfer_t          transfer_2[] = 
{
  HTS221_READ(&reg_addr, m_buffer, 1)
};

app_twi_transaction_t       transaction_2 =
{
    .callback               = custom_app_twi_callback,
    .p_user_data            = NULL,
    .p_transfers            = transfer_2,
    .number_of_transfers    = sizeof(transfer_2)/sizeof(transfer_2[0])
};

void _GOOD_read_func(app_twi_t* param_p_app_twi, 
                      uint8_t* param_p_reg_addr, 
                      uint8_t* param_p_buffer, 
                      uint8_t byte_cnt)
{
  APP_ERROR_CHECK(app_twi_schedule(param_p_app_twi, &transaction_2));
}

I am guessing that the TWI peripheral does not touch the app_twi_transaction_t struct nor the app_twi_transfer_t struct before the TWI operation is actually carried out. That is why if I store the variable in the stack as in the first function, when the TWI operation look up the values, the stack variables have already been popped and that is why it caused a HardFault.

Is my guess correct?


Another question is: I need to optimize my firmware for code size. Therefore I really prefer not having to declare a global or static local app_twi_transfer_t array and a app_twi_transaction_tfor every TWI operation I have.

With the second approach I posted above, I could modify the global variable depending on what I want to read/write. But then there is the case of two consecutive transactions. When I set up the second transaction and modify the global variables, would that potentially mess up the first transaction?

Parents
  • Your guess is correct. If you want to declare the transfer or transaction structs inside a function other than main() you have to make them static, otherwise the data will be overwritten by the time the TWI driver uses them. The TWI driver runs various asserts on the data to ensure it is valid, and this is likely to fail if the data has been overwritten.

    Is it code size, RAM size or both that you need to optimize for? Having all your structures global or static will only affect your RAM consumption, not your code consumption. Even if you only define one structure at a time you still need to have that data in the code somewhere.

    If you do want to pass one transaction at a time to the TWI library you probably need some kind of state machine in the application, where you wait for TWI events and feed one transaction at a time into the library. That being said I think this will have a larger impact on code usage than simply having all the transactions global/static, so I wouldn't recommend it.

  • Keil defines the following data types: RO (read only), RW (read write) and ZI (zero initialized) The only difference between RW and ZI is that RW data is initialized to something different than zero.

    RO variables only take up flash space RW variables take up both RAM and flash (the flash is used to store the init values) ZI variables only take up RAM, and are initialized to 0 automatically at startup.

    How critical is it to use dual bank DFU? The only difference between single and dual bank is that if something fails when doing single bank update you have to retry the DFU operation until it succeeds.

Reply
  • Keil defines the following data types: RO (read only), RW (read write) and ZI (zero initialized) The only difference between RW and ZI is that RW data is initialized to something different than zero.

    RO variables only take up flash space RW variables take up both RAM and flash (the flash is used to store the init values) ZI variables only take up RAM, and are initialized to 0 automatically at startup.

    How critical is it to use dual bank DFU? The only difference between single and dual bank is that if something fails when doing single bank update you have to retry the DFU operation until it succeeds.

Children
No Data
Related