cancel
Showing results for 
Search instead for 
Did you mean: 

Using I2C Communication Protocol via LCD

Khansokhua
Associate II

STM32F407G DISC-1   STMCubeIDE

Greetings, I am trying to send data to LCD screen by using I2C Communication Protocol. I succeed in doing it by using HAL library. Also, my current work does work in debug mode.However, I could not make it work by running it normally.

I did it in HAL by using the same clock, frequency configurations. I could not find the problem. 

Here is my code:

 

 

int main(void)
{
				GPIO_Handle_t I2C1_SCL_PB6 = {  .PORTNAME = GPIOB,
												.PINCONF.PIN = GPIO_PIN_6,
												.PINCONF.MODE = GPIO_MODE_ALTARNATE,
												.PINCONF.OTYPE = GPIO_OTYPE_OD,
												.PINCONF.OSPEED = GPIO_OSPEED_VHIGH,
												.PINCONF.PUPD = GPIO_PUPD_PU,
												.PINCONF.AF = AF4

				 	 	 	 	 	 };
				GPIO_Handle_t I2C1_SDA_PB7 = {  .PORTNAME = GPIOB,
										 	    .PINCONF.PIN = GPIO_PIN_7,
												.PINCONF.MODE = GPIO_MODE_ALTARNATE,
												.PINCONF.OTYPE = GPIO_OTYPE_OD,
										        .PINCONF.OSPEED = GPIO_OSPEED_VHIGH,
											    .PINCONF.PUPD = GPIO_PUPD_PU,
												.PINCONF.AF = AF4
								 	  };
				gpioInit(&I2C1_SCL_PB6);
				gpioInit(&I2C1_SDA_PB7);
				I2C1_CLOCK_ENABLE();
				I2C1_FREQ_16MHZ();
				I2C1_FREQ_SCL_100MHZ();
				I2C1_RISE_TIME_17();

				I2C1_ENABLE();
				I2C1_ACK_ENABLE();
				I2C1_START_GENERATION();
				I2C1->I2C_DR = SLAVE_ADDRESS_LCD;
				 
			 lcd_init ();
	    		 lcd_send_string ("ABCDE");


			while(1)
			{
			}

}

 

 

 

16 REPLIES 16

OK, great! Now you can see the startup code.
The disassembly code makes sense, but the memory dump at 0x0800000 - I do not understand:

  • OK, the SP is set in VectorTable to 0x220 - I assume it means: 0x20020000 (this looks to be the top/end of an SRAM, e.g. DTCM)
  • but the Reset_Handler entry as 0xED050008 - does not make any sense - a wrong endian to display?

OK, the Reset_Handler code seems to start correctly: the SP is read from a memory location and set (not the default SP value in ResetHandler). And the code (e.g. after b LoopCopyDataInit) makes also sense (to initialize your RAMs).

And I assume it will jump (call) to main() at the end.
So, actually, the startup without debugger should work. Does it continue correctly?

The only thing which looks suspicious:

  • the vector table has entries shown as 0xED050008
  • but your correct (!) entry is 0x080005EC - correct! (address is odd, +1, for thumb code)
  • your code execution as Reset_Handler at 0x0800005EC is correct (looks more like a Big Endian as mem dump display)
  • just the display of the memory dump is wrong (wrong endian) - but potentially the correct values

You cannot still boot this FW without debugger? (on a new power cycle)

When you let the code start itself (after power cycle) - can "we" connect with debugger to the running session?
Maybe with STM32CubeProgrammer: let the board boot, start STM32CubeProgrammer, connect and stop the MCU (where it is, potentially it will stop the MCU anyway on connect).

Where is the PC?
Does it make sense? (compare with IDE Disassembly view where the MCU is)

Let's watch out if MCU with standalone boot would end up in an HardFault_Handler, looping endlessly.

OK, I have compared this startup code with one of my project (e.g. for STM32F764): it looks a bit different:

My startup code for this project is:

Reset_Handler:  
  ldr   sp, =_estack      /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system initialization function.*/
  bl  SystemInit   
/* Call static constructors */
    bl __libc_init_array
/* Call the application's entry point.*/
  bl  main
  bx  lr    

The "big" difference I see is this:

  • your startup code calls "SystemInit" (this "bl SystemInit" right after just setting the SP (which seems to be correct)
  • but: there was not yet any SRAM initialization done! Potentially, the SystemInit needs that the SRAM was initialized (all this "CopyInitData" done before)

Where is this startup code coming from?
Do you have another project (with same MCU and board) where it works?
Could you compare the startup.S code?
Or could you find the startup.S code on the CubeHAL drivers (demo projects)?
Is it the same one?

 

SystemInit should be called AFTER all SRAM is initialized, directly before it jumps to main(). In your code example it looks to be done too early (before SRAM initialization - potentially too early).

Meanwhile I am thinking: something goes wrong with your startup.S.
Maybe a need to update the HAL drivers (this CubeHAL) for your board?
Can it be that the startup.S is old and not correct for you board?
Can you find other projects, demo code, in CubeHAL, with "same" (or similar) LinkerScripts and startup.S?
Can you create a very simple demo project (e.g. blinking LED) which boots also standalone?
Compare the differences in source code files, linker script...

Try to find the root cause with debugger (and STM32CubeProgrammer), memory dumps, checking if it makes sense (e.g. SRAM initialized and content able to display and properly written in SRAM), where is the PC (register) when booting as stand-alone...?

Potentially: if it works in IDE (with debugger) it should also work as stand-alone.
The only main reason why it might not work: debugger initializes also SRAM (load all memories, including SRAM). But on stand-alone: the startup.S code has to initialize (copy from ROM to RAM).

As I mentioned at very beginning.By using HAL library in other project (the same principle), it works very well. Maybe I misled you. I do not know  if it is releated to some debugging issue. I just did something wrong. I could not observe that BTF bit was set.It is always cleared. When I debug the program, LCD gives the output only when I go through step by step on each line of code.

 

 

 

void lcd_send_cmd (char cmd)
{
  char data_u, data_l;
  data_u = (cmd&0xf0);
  data_l = ((cmd<<4)&0xf0);

  uint8_t data_t[4];
    data_t[0] = data_u|0x0C;  //en=1, rs=0 -> bxxxx1100
    data_t[1] = data_u|0x08;  //en=0, rs=0 -> bxxxx1000
    data_t[2] = data_l|0x0C;  //en=1, rs=0 -> bxxxx1100
    data_t[3] = data_l|0x08;  //en=0, rs=0 -> bxxxx1000

    for(int i = 0; i < 4; i++)
    {
    	 while (!I2C1_SR1_TXE_READ()) 
    		 ; // null statement
    		I2C1->I2C_DR = (data_t[i]);
    		//while(!I2C1_SR1_BTF_READ());

    }
}

 

 

 

When I set a breakpoint into the LCD_Init(); due to TxE is not set, debugger stucks in the 15. line.

Also, I wonder more,  as you see in the 18. line my BTF bit is never set. That's why I got it into the comment line.

In the debugging process in the 18.line it goes infinite loop.

Also, I know it's wrong and but I do not think it directly affects my current situation but I used empty loop to give a  delay.Because I replaced Hal_delay functions with my own ugly delay functions in the working HAL project.I do not know, hope this explanation can remove the fogg from the sky @tjaekel 

 while (!I2C1_SR1_TXE_READ()) 

Code enters infinite loop in this control, TxE bit is never set. What should I do to assure it will be set?

You're not providing the full picture here. TXE won't be set until you send the address. Have you done that? Can you show a logic analyzer trace of SDA/SCL?

The reference manual is very explicit about what happens when during a transmission. In the case of a master i2c transmission, see here:

TDK_0-1736005251358.png

 

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