2014-07-23 08:22 AM
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_GPIOAvoid 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.2014-07-23 08:49 AM
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.2014-07-23 08:53 AM
Either initialize explicitly all fields of UsartInitStruct, or use
USART_StructInit(). JW2014-07-24 07:21 AM
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); }}2014-07-24 08:15 AM
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 solution2014-07-24 08:27 AM
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.2014-07-24 09:25 AM
Ok, clive1, I understood you. Thanks for consultation. And I will try to add call of SystemInit() to IAR's startup file.
2014-07-24 09:36 AM
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
2014-07-24 11:09 AM
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.