2025-12-18 5:59 AM - last edited on 2025-12-18 6:03 AM by mƎALLEm
I am trying to step into ST Microcontrollers. I have been able to blink LED of Blue Pill. My next attempt to interface 16x2 LCD with Blue Pill. An example code is being studied. In MikroC PIC Compiler control first goes to main() function from where other functions are called. In STM Cube IDE, I cannot understand where control goes first. Also what is the significance of "void delay (unit16_t us)" and "HAL_Delay()". The code is attached. Please help this
#include "LCD.h"
#include "stm32f1xx_hal.h"
/*********** Define the LCD PINS below ****************/
#define RS_Pin GPIO_PIN_1
#define RS_GPIO_Port GPIOA
#define RW_Pin GPIO_PIN_2
#define RW_GPIO_Port GPIOA
#define EN_Pin GPIO_PIN_3
#define EN_GPIO_Port GPIOA
#define D4_Pin GPIO_PIN_4
#define D4_GPIO_Port GPIOA
#define D5_Pin GPIO_PIN_5
#define D5_GPIO_Port GPIOA
#define D6_Pin GPIO_PIN_6
#define D6_GPIO_Port GPIOA
#define D7_Pin GPIO_PIN_7
#define D7_GPIO_Port GPIOA
void delay (uint16_t us)
{
int oo = 0;
while (oo < us) {
oo++;
}
}
/****************************************************************************************************************************************************************/
void send_to_lcd (char data, int rs)
{
HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, rs); // rs = 1 for data, rs=0 for command
/* write the data to the respective pin */
HAL_GPIO_WritePin(D7_GPIO_Port, D7_Pin, ((data>>3)&0x01));
HAL_GPIO_WritePin(D6_GPIO_Port, D6_Pin, ((data>>2)&0x01));
HAL_GPIO_WritePin(D5_GPIO_Port, D5_Pin, ((data>>1)&0x01));
HAL_GPIO_WritePin(D4_GPIO_Port, D4_Pin, ((data>>0)&0x01));
HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 1);
HAL_Delay(10);
HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, 0);
HAL_Delay(10);
}
void lcd_send_cmd (char cmd)
{
char datatosend;
/* send upper nibble first */
datatosend = ((cmd>>4)&0x0f);
send_to_lcd(datatosend,0); // RS must be 0 while sending command
/* send Lower Nibble */
datatosend = ((cmd)&0x0f);
send_to_lcd(datatosend, 0);
}
void lcd_send_data (char data)
{
char datatosend;
/* send higher nibble */
datatosend = ((data>>4)&0x0f);
send_to_lcd(datatosend, 1); // rs =1 for sending data
/* send Lower nibble */
datatosend = ((data)&0x0f);
send_to_lcd(datatosend, 1);
}
void lcd_clear (void)
{
lcd_send_cmd(0x01);
HAL_Delay(2);
}
void lcd_put_cur(int row, int col)
{
switch (row)
{
case 0:
col |= 0x80;
break;
case 1:
col |= 0xC0;
break;
}
lcd_send_cmd (col);
}
void lcd_init (void)
{
HAL_Delay(10);
lcd_send_cmd (0x02);
lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters)
lcd_send_cmd (0x08); //Display on/off control --> D=0,C=0, B=0 ---> display off
lcd_send_cmd (0x01); // clear display
lcd_send_cmd (0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift)
lcd_send_cmd (0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits)
}
void lcd_send_string (char *str)
{
while (*str) lcd_send_data (*str++);
}
newbie.
2025-12-18 6:20 AM
The code starts initially at Reset_Handler which is in a startup file. Eventually, it makes it to main() which is typically the start of user code. There is no main() in the code that you posted.
2025-12-18 6:28 AM - edited 2025-12-18 6:30 AM
@ankhola wrote:In MikroC PIC Compiler control first goes to main() function from where other functions are called. In STM Cube IDE, I cannot understand where control goes first.
In C there's always some magic going on before main(). There is setting up the C runtime (CRT). Which is not much for C on an MCU as there is no host OS. But some data needs to be initialized with a value (zero or non-zero).
But MCUs often have special things you might want to setup before main. Vector table might need to be initialized if it is not at beginning of flash or if it is in RAM. Stackpointer needs to be initialized before first function call.
In case of STM32:
main() still needs to initialize peripherals, but many things already work. The MCU is now running on internal clock. Some peripherals are clocked by default, while others are still off. Floating point operators use hardware floating point if that is what you configured.
Some MCU manufacturers/frameworks do the things SystemInit does in main() and then call app_main() or user_main() from that.
@ankhola wrote:Also what is the significance of "void delay (unit16_t us)" and "HAL_Delay()".
HAL_Delay() does a delay of N to N+1 tick periods by waiting for N+1 ticks. If the systick time is set to a period of 1 millisecond then HAL_Delay(10) does a delay from 10.0 to 10.9 millisecond depending on when you call the function (if a tick just happened it will take longer).
delay() is a software delay by wasting CPU cycles. This is usually poorly defined as it depends on compiler, MCU type, clock, etc. And in case of optimization it might be optimized out as it doesn't do anything.
2025-12-18 6:31 AM
The attached code is meant for STM Cube IDE. Please let me know the sequence, the functions are called.
2025-12-18 6:43 AM - edited 2025-12-18 6:44 AM
Note that Blue Pill is not an ST product, and most likely does not contain a genuine STM32.
This forum is littered with posts by people who have been burned by Blue Pill and fake STM32s.
Therefore, I would strongly recommend that you start with a Nucleo board - as that is genuine, well-known, supported with ready-to-use examples, comes complete with a genuine ST-Link, etc, etc.
eg, with an STM32F103: https://www.st.com/en/evaluation-tools/nucleo-f103rb.html
@ankhola wrote:In MikroC PIC Compiler control first goes to main() function .
Yes, that is standard for any C program - nothing special there.
@ankhola wrote:In STM Cube IDE, I cannot understand where control goes first. .
It will be exactly the same - it will go to main().
The code you've posted is not a complete program - it lacks a main().
@ankhola wrote:what is the significance of "void delay (unit16_t us)" and "HAL_Delay()". .
The clue is in the names - they provide inline delays:
HAL_Delay() comes from the ST HAL firmware pack; it provides a delay in milliseconds.
It is documented in the User Manual for the firmware pack:
https://www.st.com/resource/en/user_manual/um1850-description-of-stm32f1-hal-and-lowlayer-drivers-stmicroelectronics.pdf#page=50
via: https://www.st.com/en/embedded-software/stm32cubef1.html#documentation
Note that all the HAL functions are also documented in comments in the source code:
/**
* @brief This function provides accurate delay (in milliseconds) based
* on variable incremented.
* @note In the default implementation , SysTick timer is the source of time base.
* It is used to generate interrupts at regular time intervals where uwTick
* is incremented.
* @note ThiS function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @PAram Delay specifies the delay time length, in milliseconds.
* @retval None
*/
__weak void HAL_Delay(uint32_t Delay)
@ankhola wrote:An example code is being studied. .
The fact that it contains no comments describing its functions suggests that it may not be a great example to follow...
2025-12-18 6:47 AM
@ankhola wrote:I am trying to step into ST Microcontrollers..
See:
@ankhola wrote:I have been able to blink LED of Blue Pill. My next attempt to interface 16x2 LCD
That's rather a big leap!
I suggest a better approach:
Also, in this day & age, a 16x2 LCD is a bit retro ...
2025-12-18 6:58 AM
@ankhola wrote:The attached code is meant for STM Cube IDE. Please let me know the sequence, the functions are called.
You said it came from an example?
That example should include details of how to use it.
If it doesn't, then it can't be a great example.
Again, I think going straight from just blinking an LED to a parallel LCD interface is too much of a jump.
I would strongly recommend that you cover some more basics first; eg, as described here:
2025-12-18 7:03 AM
STM32CubeIDE calls main(). This is a C thing, not an IDE thing. If you don't have main, your program won't work.
2025-12-18 7:35 AM
@TDK wrote:If you don't have main, your program won't work.
@ankhola - in fact, it won't even build.
I tried it - this is the error you get:
arm-none-eabi/bin/ld.exe: ./Core/Startup/startup_stm32f030c8tx.o: in function `LoopFillZerobss':
Core/Startup/startup_stm32f030c8tx.s:91: undefined reference to `main'
(LoopFillZerobss is part of the startup code - that's where the call to main() happens)