cancel
Showing results for 
Search instead for 
Did you mean: 

Why USART transmission depends on function calls order?

orionhere
Associate II
Posted on July 23, 2014 at 17:22

Hello, please help me to solve next problem. I have application, which consists of three main modules: io_init(), lcd_init() and usart_init() â€�?  io_init() configures button inputs and some outputs used for relays driving and indication; lcd_init() initializes HD44780-based lcd; and usart_init() configures USART, which is used to communicate with external equipment using MODBUS protocol.

The problem is next: if I run this routines sequentially, for example io_init() â€�? lcd_init() â€�? usart_init(), then I can't send anything using USART. At the same time debugging shows, that everything is all right â€�? program operates in required sequience and USART settings are Ok â€�? APB clock is enabled, UE and TE bits in USART CR1 register are set to 1. Next, if I comment, for example lcd_init() call, then transmission starts and packet is sent. If I comment both lcd_init() and io_init(), then transmission fails again â€�? settings are Ok, but when application writes first byte â€�? nothing happens again. If I uncomment everything and change order of execution to io_init(), usart_init(), lcd_init() USART operates normally.  And one more. If i comment function calls from lcd_init() everything is Ok again.

I used disassmbler, but it doesn't show nothing suspicious. Please, help me to understand, what is wrong in my routines, because final version of this firmware may call this routines in random order. I'm using STM32F100C6 and IAR ARM.

Here is source:

void main(void)

{

  SystemInit();         // System clock configuration

 

  io_init();

  lcd_init();

  usart_init();

  __enable_interrupt();

  read_holding_registers(16, 5, 4);

  while(1);

}

#define USART_PORT_APB_ENABLE   RCC_APB2Periph_GPIOA

#define USART_APB_ENABLE               RCC_APB1Periph_USART2

#define RxTx_PORT_ENABLE                 RCC_APB2Periph_GPIOA

void usart_init(void)

{

USART_InitTypeDef               UsartInitStruct;

NVIC_InitTypeDef                InterruptInitStruct;

  // USART Rx and Tx pins setup

  RCC_APB2PeriphClockCmd(USART_PORT_APB_ENABLE, ENABLE);  // Turn corresponding PORT clocking

  

  RxTx_PORT->CRL&=~( (uint32_t) ( 0x0F<<(TX_PIN*4) ) );   // Clear Tx Pin configuration

  RxTx_PORT->CRL|=0x09<<(TX_PIN*4);                                // Alternate function push-pull, 10 MHz max speed

  RxTx_PORT->CRL&=~( (uint32_t) ( 0x0F<<(RX_PIN*4) ) );  // Clear Rx Pin configuration

  RxTx_PORT->CRL|=0x04<<(RX_PIN*4);                               // Floating input

  RCC_APB1PeriphClockCmd(USART_APB_ENABLE, ENABLE);  // Enable USART clocking

  UsartInitStruct.USART_Mode=USART_Mode_Tx;

  UsartInitStruct.USART_BaudRate=USART_BAUD_RATE;  

  UsartInitStruct.USART_WordLength=USART_WordLength_9b;

  UsartInitStruct.USART_Parity=USART_Parity_No;

  UsartInitStruct.USART_StopBits=USART_StopBits_2;

  

  USART_Init(USART, &UsartInitStruct);

  USART_Cmd(USART,ENABLE);                                      // Turn on USART

  

  InterruptInitStruct.NVIC_IRQChannel=USART_NVIC_IRQ;

  InterruptInitStruct.NVIC_IRQChannelSubPriority=0;

  InterruptInitStruct.NVIC_IRQChannelPreemptionPriority=0;

  InterruptInitStruct.NVIC_IRQChannelCmd=ENABLE;

  NVIC_Init(&InterruptInitStruct);                              // Enable USART interruprts

  

  /* Configuring Rx/Tx switch, because USART operates

     in half-duplex mode using RS-485 */

  RCC_APB2PeriphClockCmd(RxTx_PORT_ENABLE, ENABLE);             // Enable port clock

  RxTx_PORT->CRL&=~( (uint32_t) (0x0F<<(RX_TX_SWITCH_PIN*4)));  

  RxTx_PORT->CRL|=0x02<<(RX_TX_SWITCH_PIN*4);            // General purpose push-pull, 2 Mhz max speed

  adu=calloc(MAX_ADU_SIZE, sizeof(uint8_t));                               // Allocate memory for USART buffer

}

void lcd_init(void)

{

  set_bus_to_output();  // Configuring pins, connecte to LCD

  

  CLEAR_RS;

  CLEAR_RW;

  SET_E;

  

  // Other LCD initialization instructions are not shown

}

void io_init(void)

{

GPIO_InitTypeDef        PortInitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);

  // PA0 â€�? discrete input; PA9-PA12 â€�? buttons;

  PortInitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;

  PortInitStruct.GPIO_Speed=GPIO_Speed_2MHz;

  PortInitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;

  GPIO_Init(GPIOA, &PortInitStruct);

  // PA8 â€�? discrete output;  

  PortInitStruct.GPIO_Pin=GPIO_Pin_8;

  PortInitStruct.GPIO_Speed=GPIO_Speed_2MHz;

  PortInitStruct.GPIO_Mode=GPIO_Mode_Out_PP;

  GPIO_Init(GPIOA, &PortInitStruct);

  // PC14 �?¸ PC15 â€�? relays

  PortInitStruct.GPIO_Pin=GPIO_Pin_14|GPIO_Pin_15;

  PortInitStruct.GPIO_Speed=GPIO_Speed_2MHz;

  PortInitStruct.GPIO_Mode=GPIO_Mode_Out_PP;

  GPIO_Init(GPIOC, &PortInitStruct);

}

void read_holding_registers(uint8_t slave_address, uint16_t start_address, uint16_t quantity)

{

  if (!(modbus_status&REQUEST_IN_PROCESS))

  {

    adu[0]=slave_address;

    adu[1]=0x03;

    adu[2]=start_address>>8;

    adu[3]=start_address;

    adu[4]=quantity>>8;

    adu[5]=quantity;

    send_adu(6);

  }

}

void send_adu(uint8_t bytes_num)

{

uint16_t        temp;

  TRANSMITTER_ON;

  temp=modbus_crc(bytes_num);

  *(adu+bytes_num)=temp;

  *(adu+(bytes_num+1))=temp>>8;

  bytes_num+=2;

  bytes_to_send=bytes_num;

  USART->DR=adu[0];

  USART->CR1|=USART_CR1_TXEIE; // Other bytes must be sent in ISR

  

  usart_byte=1;

}

Sorry for poor English, and thanks for help.
8 REPLIES 8
Posted on July 23, 2014 at 17:49

Doesn't IAR's startup code call SystemInit() prior to main()?

Not reviewing register level settings.

I would definitely check the USART TXE state before sending any data.

Missing the interrupt code here.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on July 23, 2014 at 17:53

Either initialize explicitly all fields of UsartInitStruct, or use

USART_StructInit().

JW
orionhere
Associate II
Posted on July 24, 2014 at 16:21

That's true. IAR's code doesn't call SystemInit(). As I know it must call it from startup_stm32f10x_ld.s, but there is no such call there. May IAR just forgot add it to file, because startup file for stm32f4 contains call to SystemInit(). Anyway, I think, it doesn't matter, that it's called from main. Or you think different?

About TXE flag. You mean, that it can be set to one in some way and first byte transmission fails? Maybe it's an idea, i will check it.

ISR source is next:

void USART_ISR(void)

{

  if ((USART->SR&USART_SR_TXE)&&(USART->CR1&USART_CR1_TXEIE))

  {

if (usart_byte<bytes_to_send)

     {

       USART->DR=adu[usart_byte];        // Buffer is empty, move next byte from packet to USART data       register

       ++usart_byte;

     }

     else

     {

       USART->CR1&=~USART_CR1_TXEIE; // Last byte was transmitted, enable transmission complete

      USART->CR1|=USART_CR1_TCIE; // interrupt

       usart_byte=0;

       bytes_to_send=0;

     }

  }

  // Transmission completed, receiver can be enabled

  if ((USART->SR&USART_SR_TC)&&(USART->CR1&USART_CR1_TCIE))

  {

     RxTx_PORT->BSRR|=1<<(16+RX_TX_SWITCH_PIN);

    USART->CR1&=~(USART_CR1_TXEIE|USART_CR1_TCIE);

        USART->CR1|=(USART_CR1_RE|USART_CR1_RXNEIE);  

  }

}

orionhere
Associate II
Posted on July 24, 2014 at 17:15

To waclawek.jan

I'm a bit surprised, but your solution works. After USART_StructInit() call all the functions fall into their places. Now result doesn't depend on call order. Maybe later I will try to find exact reason of such behaviour, because USART registers settings seemed as required in debugger...

Anyway, thank you for solution
Posted on July 24, 2014 at 17:27

Uninitialized local variables contain random junk, functions using this junk will do odd things. This inexplicable behaviour will change depending on the call order, or whatever was on the stack prior.

CMSIS expects you to call SystemInit() prior to calling main(), one reason would be if main() uses floating point code, and FPU instructions, and you don't enable the FPU early enough. The system will Hard Fault. The other reason would be to initialize external memories, pins and buses prior to the C runtime code trying to copy statics there.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
orionhere
Associate II
Posted on July 24, 2014 at 18:25

Ok, clive1, I understood you. Thanks for consultation. And I will try to add call of SystemInit() to IAR's startup file.

Posted on July 24, 2014 at 18:36

STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar\startup_stm32f10x_ld.s

EXTERN __iar_program_start
EXTERN SystemInit
...
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
orionhere
Associate II
Posted on July 24, 2014 at 20:09

One more thanks. I've changed my startup file to the correct one. I have made this mistake, because I take them in IAR's example directory, and there are couple of them and some contains call to SystemInit() and some not, probably because that sample application supposed to operate at default clock settings.