Skip to main content
SStru.1
Associate II
October 4, 2020
Question

MX Init functions overwrite static size_t variable

  • October 4, 2020
  • 1 reply
  • 1178 views

I am working on an embedded application using the STM32F103C8T6 (black pill). I've been slowly adding the functionality I need and adding different peripherals one by one. For USART I was using the excellent code written by Tilen Majerle here. Everything was working great until I added something via CubeMX and my USART 'broke' - stopped sending. I stepped through the code and discovered the issue.

In Tilen Majerle's code

usart_send_string("READY\n");

Calls the following functions:

void
usart_send_string(const char* str) {
	usart_process_data(str, strlen(str)); /* Then try to start transfer */
}
 
void usart_process_data(const void* data, size_t len){
	lwrb_write(&usart_tx_ringbuff, data, len);
	usart_start_tx_dma_transfer();
}
 
uint8_t usart_start_tx_dma_transfer(void){
	uint32_t old_primask;
	uint8_t started = 0;
 
	old_primask = __get_PRIMASK();
	__disable_irq();
 
	if(usart_tx_dma_current_len ==0){
		usart_tx_dma_current_len = lwrb_get_linear_block_read_length(&usart_tx_ringbuff);
		if(usart_tx_dma_current_len>0){
			LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_2);
 
			/* Clear all flags */
			LL_DMA_ClearFlag_TC2(DMA1);
			LL_DMA_ClearFlag_HT2(DMA1);
			LL_DMA_ClearFlag_GI2(DMA1);
			LL_DMA_ClearFlag_TE2(DMA1);
 
			/* Start DMA transfer */
			LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, usart_tx_dma_current_len);
			LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)lwrb_get_linear_block_read_address(&usart_tx_ringbuff));
 
			/* Start new transfer */
			LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
			started = 1;
 
		}
	}
 
	__set_PRIMASK(old_primask);
	return started;
}
 

I noticed in stepping through that the condition statement wasn't getting executed.

if(usart_tx_dma_current_len ==0){
			/* Clear all flags */
			/* Start DMA transfer */
			/* Start new transfer */
		}

 Stepping through the code shows that usart_tx_dma_current_len, which I declared as:

static size_t usart_tx_dma_current_len;

Gets it's value updated after each of the following initialization calls:

 MX_GPIO_Init();
 MX_DMA_Init();
 MX_ADC1_Init();
 MX_TIM1_Init();
 MX_TIM4_Init();
 MX_TIM2_Init();
 MX_USART3_UART_Init();

For instance after MX_DMA, the value in the watch for usart_tx_dma_current_len is 1, after MX_ADC1 it is 536871292, after MX_TIM1 it is 0 and most bizarrely after MX_USART3_UART_Init it is 115200 (the baud rate I selected)...

I can solve the problem by setting usart_tx_dma_current_len = 0 after the initialisation calls but I wondered if anyone had any clue why this might be happening?

This topic has been closed for replies.

1 reply

KnarfB
Super User
October 4, 2020

Are you sure that the watched values are geniue? I would double-check by showing raw memory data at the variables address. If a global variable is indeed overwritten by function calls, it could be a stack overflow. Therefore I would compare the stack pointer register value against the variables address and check how much headroom is left on the stack.

SStru.1
SStru.1Author
Associate II
October 5, 2020

On my device I had boot 1 pulled high and boot 0 pulled low. When I keep boot 0 and boot 1 pulled low I stop getting really weird behaviour like this so I am assuming that it was something weird happening with the stack.

I'm a complete newbie to STM32 devices. Would you be able to explain to me please how I can compare the stack pointer register value against the variables address. I'm using Atollic TrueStudio - in debug I've located the disassembly which seems to show the memory addresses for each command.