Help on Code Conversion from Atmega324P to ARM Cortex M4.

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all, i know it does sound Weird that I am asking a help with this but i surely know that we just don't use AVRs. so here we go.
Anyone to please have a look at the below code conversion and give me a hint as to why it isn't quite working well?
i made the code so simple & is written a C :D

The problem is that i can't get the LCD f(x)s (i wrote about a year ago originally for Atmega32) to work on Cortex M4. (Stm32f407VG)
I made some necessary changes to suit CM4 but it still doesn't Display anything. these changes were somethings like assigning a "char" to a GPIO. for AVR each Port is 8 bits so assigning a character is straightforward But since GPIOs on ARM are 32 i tried to do some masking to still suit D0 to D7 out of D0 to D31 although i think it wasn't much necessarily needed masking anyway.

i virtually spent overnight looking for a bug but i don't seem to find it. i first though it was my LCD which suddenly broke but when plugged to Atmega32, it was displaying quite well.

Below are the code for Both Parties I would very much appreciated if anyone who is familiar with ARM doesn't mind having a look and give me a hint as to where the error might be.

Many Thanks.

AVR CODE



#include "¨
#include "¨
#include  

#define Enable 5"¨
#define RW 7"¨
#define RS 2 

void Init_LCD(void); 
void Set_LCD_up(void);"¨
void Check_if_LCD_is_Busy(void);"¨
void Command_LCD(unsigned char command);"¨
void Send_a_Character(unsigned char character);
"¨void Print_on_LCD(char *string_of_chars);"¨
void Goto_LCD_Pos(uint8_t X , uint16_t Y);
"¨void Print_on_LCD_with_Pos(uint8_t X, uint16_t Y, char *String_Of_Chars); 

int LCD_Line_Pos[2] = {0,64} ; 

int main(void) 
{
 DDRA = 0b11111111; // This is where D0 to D7 are connected"¨
 PORTA= 0b00000000;"¨"¨
 TCCR1B |= 1<<CS10 | 1<<CS11; // Timer1_B prescaling 

 Init_LCD(); 

 while(1)"¨
  {
   "¨"¨Print_on_LCD_with_Pos(7,1,"Hello World"); // On 1st Line 7th Position "¨
  } 
} 

void Init_LCD() 
{
 "¨"¨DDRD|= (1<<Enable | 1<<RW | 1<<RS); // These are connected to Port D 
 _delay_ms(15);"¨
 Command_LCD(0x01); //Clear Screen by 0x01
"¨ _delay_ms(2);"¨
 Command_LCD(0x38);"¨
 _delay_us(50);
"¨ Command_LCD(0x0F);"¨
 _delay_us(50);"¨"¨
}

void Check_if_LCD_is_Busy() 
{
 "¨DDRB = 0x00;
 "¨PORTD |= 1<<RW;
 "¨PORTD &= ~1<<RS; 
 while(PORTB >= 0x80)"¨
 { 
  "¨Set_LCD_up();"¨
  }
  "¨DDRB = 0xFF;"¨
 } 
"¨  
void Set_LCD_up() 
{
"¨ PORTD |= 1<<Enable;"¨
 asm volatile ("nop");"¨
 asm volatile ("nop");"¨
 PORTD &= ~ 1<<Enable;"¨
 } 
"¨
void Command_LCD(unsigned char command) 
{
 "¨Check_if_LCD_is_Busy();"¨
 PORTB = command;"¨
 PORTD &= ~ ((1<<RW) | (1<<RS));"¨
 Set_LCD_up();"¨PORTB = 0x00; "¨
 }
 
"¨void Send_a_Character(unsigned char character) 
{ 
 "¨Check_if_LCD_is_Busy();"¨
 PORTB = character;
 "¨PORTD &= ~ (1<<RW);"¨PORTD|= 1<<RS;"¨
 Set_LCD_up();"¨PORTB = 0x00;"¨
 }
 
"¨void Print_on_LCD(char *String_Of_Chars) 
{ 
 "¨while(*String_Of_Chars > 0)"¨
 {
"¨  Send_a_Character(*String_Of_Chars++);"¨
 }
"¨} 
"¨ 
void Goto_LCD_Pos(uint8_t X , uint16_t Y)
"¨"¨{
 "¨Command_LCD(0x80 + (X-1) + LCD_Line_Pos[Y-1]);
 "¨}
 
"¨void Print_on_LCD_with_Pos(uint8_t X, uint16_t Y, char *String_Of_Chars) 
{"¨
 Command_LCD(0x80 + (X-1) + LCD_Line_Pos[Y-1]); "¨"¨
 while(*String_Of_Chars > 0)"¨
 {
 "¨  Send_a_Character(*String_Of_Chars++);
 "¨} 
} 

STM32F407 CODE.
Using Coocox IDE.

#include 
#include 
#include 
#include 
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "stm32f4xx_gpio.h"
#include "core_cm4.h"
 
#define RS GPIO_Pin_0
#define RW GPIO_Pin_1
#define Enable GPIO_Pin_2 // All RS, RW & Enable are @ PortB

#define LCD_D0 GPIO_Pin_0 // Lower nibble @ PortA D0 - D3
#define LCD_D1 GPIO_Pin_1
#define LCD_D2 GPIO_Pin_2
#define LCD_D3 GPIO_Pin_3

#define LCD_D4 GPIO_Pin_4 // Higher nibble @ PortA D4 to D7
#define LCD_D5 GPIO_Pin_5
#define LCD_D6 GPIO_Pin_6
#define LCD_D7 GPIO_Pin_7

//Exactly same f(x)s as Atmega32's

void Init_LCD(void);
void Set_LCD_up(void);
void Check_if_LCD_is_Busy(void);
void Command_LCD(unsigned char command);
void Send_a_Character(unsigned char character);
void Print_on_LCD(char *string_of_chars);
void Goto_LCD_Pos(uint8_t X , uint16_t Y);
void Print_on_LCD_with_Pos(uint8_t X, uint16_t Y, char *String_Of_Chars);
void _delay_ms(volatile uint32_t ms);
void _delay_us(volatile uint32_t us);

int LCD_Line_Pos[2] = {0,64} ;
 
int main(void)
{

 SystemInit(); // Running @ 84Mhz on Timers 1,2,3...
 GPIO_InitTypeDef GPIO_InitStructure;

 // Setting GPIOs & Timers Reset Clocks
 // All D0 - D7 are made as Output @ 50Mhz

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
 GPIO_InitStructure.GPIO_Pin = (LCD_D0 | LCD_D1 | LCD_D2 | LCD_D3 | LCD_D4 |LCD_D5| LCD_D6 | LCD_D7);
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 GPIO_InitStructure.GPIO_Pin = (RS | RW | Enable);
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 Init_LCD();

 while(1)
 {
  Print_on_LCD_with_Pos(7,1,"Hello World");
 }
}

// Both "_delay_us()" and "_delay_ms()" timers tested and work well 

void _delay_us(volatile uint32_t us)
{
 TIM6->PSC = 83; // PSC-1
 TIM6->ARR = (us -1); ARR -1 too
 TIM6->CNT = 0;  // Start from 0 just like TCNT(XY) in CTC mode
 TIM6->EGR |= TIM_EGR_UG; // Update Event Flag upon a match of CNT with ARR
                          // just like TIFR(XY) with OCR(XY) 
 TIM6->SR &= ~ TIM_SR_UIF; // Clear it since no ISR 
                    // or NVIC is needed in this case
 TIM6->CR1 |= (TIM_CR1_OPM | TIM_CR1_CEN);
 while(!(TIM6->SR &TIM_SR_UIF));
 }

void _delay_ms(volatile uint32_t ms)
{
  while(-- ms >0) // Call _Delay_us() 1000 times
  {
   _delay_us(1000);
  }
}

void Init_LCD()
{
 GPIOB->ODR = (1<<Enable | 1<<RW | 1<<RS);
 _delay_ms(15);
 Command_LCD(0x01); //Clear Screen 0x01 = 00000001
 _delay_ms(2);
 Command_LCD(0x38);
 _delay_us(50);
 Command_LCD(0x0F);
 _delay_us(50);
}
 
void Check_if_LCD_is_Busy()
{
 GPIOA->MODER = 0xA8000000; // set up the whole GPIOA as input mode @ 10 Mhz.
 GPIOA->OSPEEDR = 0xaaaaaaaa; //50Mhz
 GPIOA->OTYPER = 0x00;
 GPIOA->PUPDR = 0x00;
 GPIOB->ODR |= (1<<RW); // Set RW
 GPIOB->ODR &= ~ (1<<RS); // Clear RS
 while((GPIOA->IDR & 0xFF) >= 0x80) // only lower 16 bits are read!
  {
   Set_LCD_up();
  }
 GPIOA->MODER = 0x55555555; //set the whole GPIOA as output mode Push Pull 
 GPIOA->OSPEEDR = 0xaaaaaaaa;
 GPIOA->OTYPER = 0x00;
 GPIOA->PUPDR = 0x00;
}

void Set_LCD_up()
{
 GPIOB->ODR |= (1<<Enable);
 asm volatile ("NOP");
 asm volatile ("NOP");
 GPIOB->ODR &= ~ (1<<Enable);
}
 
void Command_LCD(unsigned char command)
{
 Check_if_LCD_is_Busy();
 GPIO_WriteBit(GPIOA, LCD_D0, ((command>>0) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D1, ((command>>1) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D2, ((command>>2) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D3, ((command>>3) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D4, ((command>>4) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D5, ((command>>5) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D6, ((command>>6) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D7, ((command>>7) & 0x01));
 GPIOB->ODR &= ~ ((1<<RW) | (1<<RS));
 Set_LCD_up();
 GPIOA->ODR = 0x0000;
}
 
void Send_a_Character(unsigned char character)
{
 Check_if_LCD_is_Busy();
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>0) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>1) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>2) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>3) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>4) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>5) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>6) & 0x01));
 GPIO_WriteBit(GPIOA, LCD_D0, ((character>>7) & 0x01));
 GPIOB->ODR &= ~ (1<<RW);
 GPIOB->ODR |= (1<<RS);
 Set_LCD_up();
 GPIOA->ODR = 0x0000;
}

void Print_on_LCD(char *String_Of_Chars)
{
 while(*String_Of_Chars > 0)
 {
  Send_a_Character(*String_Of_Chars++);
 }
}
 
void Goto_LCD_Pos(uint8_t X , uint16_t Y)
{
 Command_LCD(0x80 + (X-1) + LCD_Line_Pos[Y-1]);
}
 
void Print_on_LCD_with_Pos(uint8_t X, uint16_t Y, char *String_Of_Chars)
{
 Command_LCD(0x80 + (X-1) + LCD_Line_Pos[Y-1]);
 while(*String_Of_Chars > 0)
 {
  Send_a_Character(*String_Of_Chars++);
 }
}

/* Other than manipulating GPIOs and timer the rest of Syntax are pretty much the same. */

Many Thanks Again
Regards.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The LCD is a simple Hex 2x16.

Cheers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't looked at all details, but one thing

#define RS GPIO_Pin_0
#define RW GPIO_Pin_1
#define Enable GPIO_Pin_2 // All RS, RW & Enable are @ PortB

Those are bit masks (not bit numbers) so you can't for example do

GPIOB->ODR = (1<<Enable | 1<<RW | 1<<RS);

just do

GPIOB->ODR = Enable | RW | RS;

instead. Same thing everywhere you do a "1 << bitmask".

And you have bit set and bit clear registers for the IO-ports, it's better to use them instead of writing to the whole port.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh Damn.... you are right mate.
Thank you for your insight! why didn't i saw that?
Just because i used to both AVR & Cortex share the same masking criteria, i didn't quite that we can't mask ODR & IDR anything other than actual GPIO Pins :o
Thak you again for your assistance. I am going to change and hopefully i will see the burger :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "stm32f4xx_gpio.h"
#include "core_cm4.h" 

The idea of the peripheral library is that you include device specific header file and the library does the rest of the job.

Include "stm32f4xx.h" and -DUSE_STDPERIPH_DRIVER only.

 GPIOA->MODER = 0xA8000000; // set up the whole GPIOA as input mode @ 10 Mhz.
 GPIOA->OSPEEDR = 0xaaaaaaaa; //50Mhz
 GPIOA->OTYPER = 0x00;

So why did you include the peripheral library if you do not use it?

OP wrote:

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

(..)The LCD is a simple Hex 2x16


As you may have heard, STM32 chips have IOs working in a U<=3.3V in a Push-Pull(PP) standard and that could be too low for the 5V LCD. Most of the pins can be configured with Open-Drain (OD) drive and can control up to 8V loads. So please RTFM, chapter "Absolute maximum ratings", learn it by heart, before you decide to connect anything OD to the 3.3V chip.

No RSTDISBL, no fun!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right i have been thinking about that actually, Though the LCD is powered from 5V which the Board already provide. Yes again this probably is from USB supply. i will consider switching them to Open Drain since i didn't specify which pins would be a 5V tolerant.

Thx for that too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ snigelen

Other than changing

#define RS GPIO_Pin_0
#define RW GPIO_Pin_1
#define Enable GPIO_Pin_2

to

#define RS      0
#define RW      1
#define Enable  2

the code works fine because masking (ODR & IDR) totally is fine as long as we mask bits not (GPIOx_Pin_y) which was my case earlier :shock:
so

GPIOB->ODR = (1<<Enable | 1<< RW | 1<< RS);

is still as fine
provided above re-definition.

Still no Luck i might need to dig deeper :x

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you also change

GPIO_InitStructure.GPIO_Pin = (RS | RW | Enable); 

to use bit numbers?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah. i did it altogether with a change in the "#defines"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've used avr8's at 3 V and my M4 Discorvery board to I/F to a 5V LCD, so it can work. I'll look harder at the code ater work, but I'd suggest getting it to work without reading the busy bit, first and come back to that later.

Quote:
// All D0 - D7 are made as Output @ 50Mhz
Then you better RTM on your LCD, because I doubt it can operate that fast. As a human-read display, I also doubt any OEM would bother to make a char LCD that could use that speed.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The 50Mhz is not a real frequency, it's just a way to tell how "hard" the output should be driven when you change it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your ~1<<pos should read ~(1<<pos) because of operator precedence.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The LCD is a simple Hex 2x16
When you have issues with hardware...post the specific part number in your threads. If it's a a HD44700, then you need to follow the init code given in the datasheet. You're not the first to think it's ok to do minimal code for that...and won't be the last.
Quote:
The 50Mhz is not a real frequency, it's just a way to tell how "hard" the output should be driven when you change it.
Good to know, thanks.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Sun. Sep 29, 2013 - 05:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't look at the sheet to find out what your init_lcd() sets up the cursor to do, but I suggest you turn it on and have it blink as an easy visual way of knowing LCD is in the correct start state. I suggest using GPIOx_BSRR register for set/clr of your control lines. Why not just write a byte directly in 1 line for your lcd_char_write(), it's more complicated than it needs to be. In that same function, you never clr RS at the end and should.

Does the code hang in any of the functions ?
Focus on just geting the cursor to appear and printing 1 char for now. I've never used

SystemInit();

, and have you blinked an LED to make sure that the MCU timing/startup config. is right ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Sun. Sep 29, 2013 - 10:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I've never used SystemInit();

That is part of the CMSIS compliance. All CM have it.

No RSTDISBL, no fun!