cancel
Showing results for 
Search instead for 
Did you mean: 

Unit testing and system initialisation code

CTapp.1
Senior

I am currently working on testing of some code using a testing tool. As nearly all of the code interacts with the hardware, it is easiest for me to do this on the target. The tool automatically builds the test environment and allows files and/or functions to be tested as a whole or in isolation. In my case, I include all of the project's files in the test builds (so the functionality is available), selecting one of them at a time for white box testing.

The only real problem I have is the startup code that exists within main() - this needs to be run to set up the clock tree, initialise the HAL, etc. The testing environment renames main() and adds its own main() so that it can control how the test cases I specify are executed and to allow the results to be captured.

At the moment, I have to duplicate the code in the supplied main() so that I can initialise the system before any tests are executed. This is a manual process, which means that the behaviour of this "clone" can diverge from what is provided by the IDE (when code is regenerated after editing the .ioc, for example).

I cannot simply call the renamed main() function to perform initialisation as it makes calls into the project code and does not return.

It would be really helpful if all of the calls within the IDE-provided main() that are related to system startup (everything between USER CODE END 1 and USER CODE BEGIN 2) could be encapsulated within a single function (that can be made extern) so that test environments could include a call to it to perform system initialisation. I do not think it is possible to get the IDE to do this at the moment. Would this be a feature that would be useful to the community? If so, how do I submit a feature request?

1 ACCEPTED SOLUTION

Accepted Solutions

Yeah, and that doesn't need a script. Just needs a prototype for mcuInit() adding into main.h and (obviously) to remove/relocate anything within the /* USER CODE BEGIN WHILE */ and /* USER CODE BEGIN 3 */ blocks (especially the while (1)).

Thanks :)

View solution in original post

8 REPLIES 8
Andrew Neil
Evangelist III

Have you configured your project so that the peripheral code goes into separate .c/.h file pairs - rather than into main() ?

CTapp.1
Senior

Thanks, but that only moves the functions out of main.c; it still leaves the calls in main() (though it does at least mean the prototypes are available, which helps a bit). This is an example of the code from main() that I have to duplicate:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  /* System interrupt init*/
  NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* SysTick_IRQn interrupt configuration */
  NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),15, 0));

  /** Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral
  */
  LL_PWR_DisableUCPDDeadBattery();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_CRC_Init();
  MX_RNG_Init();
  MX_TIM6_Init();
  MX_TIM4_Init();
  MX_TIM2_Init();
  MX_SPI1_Init();
  MX_SPI2_Init();
  MX_IWDG_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */

 If this was encapsulated in a single (generated) function that was called from main(), then I would just be able to call it and not worry about (and it could be static inline in it's own .h):

void mcuInit( void )
{
  // All initialisation calls in here
}

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/
  mcuInit(); // Call initialisation code
  /* USER CODE BEGIN 2 */

 

Andrew Neil
Evangelist III

A quick & dirty solution: put a goto in USER CODE 1, and its target label in USER CODE 2 ... ?!

CTapp.1
Senior

I can move some of the calls out by settings "Do Not Generate Function Call" (Advanced Settings) - but that only moves out the MX_ calls.

SystemClock_Config is also different - it's connected to the RCC, but setting "Generate a pair of .c/.h" does not result in rcc.c, rcc.h files being produced.

I think I might need to use an "After Code Generation" user action to modify main() each time the code is regenerated if I want something that can be automated.

Pavel A.
Evangelist III

Like this?

int main(void)
{
  /* USER CODE BEGIN 1 */
  mcuInit();
  mainloop();
}
void mcuInit()
{
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/
..........
}

 

Yeah, and that doesn't need a script. Just needs a prototype for mcuInit() adding into main.h and (obviously) to remove/relocate anything within the /* USER CODE BEGIN WHILE */ and /* USER CODE BEGIN 3 */ blocks (especially the while (1)).

Thanks :)

Pavel A.
Evangelist III

Instead of removing code within /* USER CODE BEGIN WHILE */ insert there #if 0 ...#endif

 

CTapp.1
Senior

Yes, that could also be done - but some coding standards would then consider the code to be "commented out", resulting in a standards violation that has to then be managed ;) It's in a user-provided code section, so it's easy enough to remove.