cancel
Showing results for 
Search instead for 
Did you mean: 

A novice question on ST Microcontroller programming with Blue Pill board

ankhola
Associate II

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. 

8 REPLIES 8
TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".

@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:

  • The startup file initializes data and the stack pointer and calls SystemInit()
  • SystemInit() enables FPU if needed (otherwise you will use software floating point in your code)
  • SystemInit() resets/inits some registers related to FLASH, memory and clocks(PLL)
  • SystemInit() moves vector table if needed 
  • SystemInit() calls main()

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.

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.
ankhola
Associate II

The attached code is meant for STM Cube IDE. Please let me know the sequence, the functions are called.

Andrew Neil
Super User

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:

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...

 

 

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
Andrew Neil
Super User

@ankhola wrote:

I am trying to step into ST Microcontrollers.. 


See: 

https://community.st.com/t5/stm32-mcus-products/for-better-learning-stm32-programming-and-debugging/m-p/719468/highlight/true#M260690

 


@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:

https://community.st.com/t5/stm32-mcus-products/for-better-learning-stm32-programming-and-debugging/m-p/719485/highlight/true#M260696

 

Also, in this day & age, a 16x2 LCD is a bit retro ...

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

@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:

https://community.st.com/t5/stm32-mcus-products/for-better-learning-stm32-programming-and-debugging/m-p/719485/highlight/true#M260696 

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

STM32CubeIDE calls main(). This is a C thing, not an IDE thing. If you don't have main, your program won't work.

If you feel a post has answered your question, please click "Accept as Solution".

@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)

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.