Jiunge kupata chapisho jipya kwa barua pepe!

jua'hatutumi spam!.

Jumatatu, 27 Agosti 2018

LCD LIBRARY FOR STM32F4 USING 4 BIT INTERFACING

Introduction:
Hello and welcome to the tutorial on how to interface LCD to STM32F4 in 4 bit mode. In this tutorial i will use 20x4 LCD display module. this display module has 4 lines of display with the ability of displaying 20 characters per line. But in reality it consist of only two lines which are addressable from Data Ram of the LCD.
 
How to use the display with stm32f4 MCU and ATTOLIC TRUE STUDIO FOR ST
In this tutorial STM32f4 Discovery Board DISC1 is used. this  board uses STM32F407VG  mcu. It has several pin breakout which can be used as GPIO as well as Alternate function to interface the mcu to the external world. We will use the pins as GPIO to interface our mcu to the display. Note that we want to be able to use any pin combination from any port without problems. In order to do so we need to define pins and ports in the header file:

#define D4 0
#define D4Port GPIOC
#define D5 1
#define D5Port GPIOD
#define D6 2
#define D6Port GPIOD
#define D7 3
#define D7Port GPIOB

#define RS 4 //REGISTER SELECT PIN
#define RSPort GPIOD

#define EN 5 //ENABLE PIN    #define ENPort GPIOD      

As seen from above, we have defined pin numbers as well as port to be used by the lcd. This gives us the flexibility of using any combination of pin and port as we wish, provided that we have edited the definitions above! As seen above we can use any port and any pin we like.


Now lets create our helper functions.............................
First of all we need to configure the pins as GPIO pins. Since we are using arm microcontroller we have to bear in mind that we need first to enable clock to the GPIO module of the MCU. (Module Clock is normally disabled in arm mcu families in order to save power, so you have to enable them before you can use any module). Here i will use PD0 of GPIOD as an example:

RCC->AHB1ENR|=(1<<3);  //enable clock to PORTD, see reference manual for your mcu                   

Then we need to configure the pin as GPIO (Pins has other alternative functions as well)

GPIOD->MODER|=(1<<0);    //Set pin PD0 of port D as  GPIO Output pin

Also we will set  Out put type to push-pull and with no pull up or pull-down

GPIOD->OTYPER&=~(1<<0); //Set PD0 in push-pull configuration
GPIOD->PUPDR&=~(1<<0); //NO PULL-UP OR PULL-DOWN  on PD0

Finally we set the pin speed (pin speed control slew rate on the pin)

port->OSPEEDR|=(1<<1)|(1<<0); //Set PD0 highest speed.

That's all required in order to configure the Pin as GPIO. Please consider reference manual for you mcu. Below is a function which can set any pin of any port in GPIO output mode.

void gpioInit(GPIO_TypeDef *port, uint8_t pinNumber)
{
if(port==GPIOA)
{
  RCC->AHB1ENR|=(1<<0);
}
else if(port==GPIOB)
{
RCC->AHB1ENR|=(1<<1);
}
else if(port==GPIOC)
{
RCC->AHB1ENR|=(1<<2);
}
else if(port==GPIOD)
{
RCC->AHB1ENR|=(1<<3);
}
else if(port==GPIOE)
{
RCC->AHB1ENR|=(1<<4);
}
else if(port==GPIOF)
{
RCC->AHB1ENR|=(1<<5);
}
else if(port==GPIOG)
{
RCC->AHB1ENR|=(1<<6);
}
else if(port==GPIOH)
{
RCC->AHB1ENR|=(1<<7);
}
else if(port==GPIOH)
{
RCC->AHB1ENR|=(1<<8);
}
else
{
exit(0);               //if port is not valid, terminate the program
}


port->MODER|=(1<<(pinNumber*2)); //SET IS AS GPIO
port->OTYPER=0x0000; //PUSH PULL CONFIGURATION
port->PUPDR=0X0000; //NO PULL-UP OR PULL-DOWN
port->OSPEEDR|=(3<<(pinNumber*2)); //SET IT TO HIGHEST SPEED
}


Now lets configure individual pins as GPIO output using our helper function that we have created above:

void pinInit()                //configure respective port pin
{
  gpioInit(D4Port, D4);
  gpioInit(D5Port, D5);
  gpioInit(D6Port, D6);
  gpioInit(D7Port, D7);
  gpioInit(RSPort, RS);
  gpioInit(ENPort, EN);
}

Voila......
Now lets figure how to send a  byte to the LCD.,.. But wait, we need helper function for sending bit to the pin (Remember that our library will provide a flexible way of using pins from various Ports so we can simply write to a port at once, we need a mechanism to break byte into bits)!!! 

Here is a function that will send bits into the pin.

void writeBit(GPIO_TypeDef *port, uint8_t pinNumber, uint8_t bitValue)
{
if(bitValue)
{
port->BSRRL|=(1<<pinNumber);
}
else
{
port->BSRRH|=(1<<pinNumber);
}
}


As seen, the function needs port to write to (of Type GPIO_TypeDef), a pin and a bitValue (either 1 or 0)  .


Now we can write our byte to LCD........ Oh but wait. Do you remember that we are interfacing the lcd to our mcu using 4 Bit? Well you remember, so how about sending the byte in this scenario?  Simply we will have to break the byte into two nibbles (4 bit ) and send the high nibble first then the lower nibble next. See the function below to send Higher nibble

void sendHigherNibble(char byteToSend)
{
 char temp;
 temp=byteToSend;
 temp=temp&0xF0; //select high nibble first and send it through d4..d7

 writeBit(D4Port, D4, temp&0x10);
 writeBit(D5Port, D5, temp&0x20);
 writeBit(D6Port, D6, temp&0x40);
 writeBit(D7Port, D7, temp&0x80);
}

See the function below to send Higher nibble

void sendLowerNibble(char byteToSend){
byteToSend=byteToSend<<4; //select lower nibble first and send it through d4..d7
writeBit(D4Port, D4, byteToSend&0x10);
writeBit(D5Port, D5, byteToSend&0x20);
writeBit(D6Port, D6, byteToSend&0x40);
writeBit(D7Port, D7, byteToSend&0x80);
    
}


But in reality (in our case) we will combine all these in one function. Lets see a function which sends command to the lcd.

void LCDWriteCmd(char cmd)
{
char temp;
temp=cmd;
temp=temp&0xF0;  //select high nibble first and send it through d4..d7

writeBit(D4Port, D4, temp&0x10);
writeBit(D5Port, D5, temp&0x20);
writeBit(D6Port, D6, temp&0x40);
writeBit(D7Port, D7, temp&0x80);

writeBit(RSPort, RS, 0); //command mode selected
writeBit(ENPort, EN, 1);
delay(VAL);
writeBit(ENPort, EN, 0);      //generate enable signal

cmd=cmd<<4; //select lower nibble first and send it through d4..d7
writeBit(D4Port, D4, cmd&0x10);
writeBit(D5Port, D5, cmd&0x20);
writeBit(D6Port, D6, cmd&0x40);
writeBit(D7Port, D7, cmd&0x80);
        enableSignal();

delay(1000);

}


Function to send data to lcd:

void LCDWriteData(char data)
{
char temp;
temp=data;
temp=temp&0xF0;    //select high nibble first and send it through d4..d7

writeBit(D4Port, D4, temp&0x10);
writeBit(D5Port, D5, temp&0x20);
writeBit(D6Port, D6, temp&0x40);
writeBit(D7Port, D7, temp&0x80);

writeBit(RSPort, RS, 1);       // data mode
enableSignal(); //generate enable signal

data=data<<4; //select lower nibble first and send it through d4..d7
writeBit(D4Port, D4, data&0x10);
writeBit(D5Port, D5, data&0x20);
writeBit(D6Port, D6, data&0x40);
writeBit(D7Port, D7, data&0x80);
         enableSignal();

delay(1000);

}

Note that the delay function is dummy function that loops to generate a non precise delay to use for timing the communication between the mcu and lcd display.
Here is the dummy function for generating delay

void delay(uint32_t val)
{
    volatile uint32_t i, j=0;
for(i=0;  i<val; i++)
{
j++; //increment dummy variable
}
}


finally initialize the lcd by calling this function:

void LCDInit()
{
    pinInit();
    LCDWriteCmd(0x33);          //wake up lcd for commands
    delay(1000);
    LCDWriteCmd(0x32); //wake up lcd for commands
    delay(1000); 
    LCDWriteCmd(0x20);          //init lcd for 4bit mode
    delay(1000);
    LCDWriteCmd(0x28);          //2 lines, 5x7 matrix
    delay(1000);
    LCDWriteCmd(0x0e);          //display on, cursor on
    LCDWriteCmd(0x01);          //clear lcd
    delay(20000);
    LCDWriteCmd(0x06);          //shift cursor right
    delay(1000);
    LCDWriteCmd(0x0c);
    delay(1000);
}

This is a helper function to display string in the LCD 

void LCDWriteString(char *str)
{
    while(*str!='\0')
    {
    LCDWriteData(*str++);
    }

}

But also we need a way to navigate and diplay any where in our lcd, we eill be able to do so by using the following function

void LCDSetCursor(uint8_t x, uint8_t y)
{
  char firstArr[4]={0x00, 0x40, 0x14, 0x54};  //array of  addresses for 4x20 lcd 
  LCDWriteCmd(0x80+firstArr[y]+x);
}

Note that y ranges from 0 to 3 (rows) and x ranges from 0 to 19.  

This is the complete library for  lcd in 4 bit mode


/*
 * LiquidCrystal4BIT.h
 *
 *      Created on: Aug 18, 2018
 *      Author: DIDAS ERENE  (rombian_coder)
 *      follow instagram: rombian_abc, embedded_tz
 *      phone call: 255 629052463
 *
 *
 *      THIS LIBRARY SERVES HELPER FUNCTION FOR USING 20x4 LCD DISPLAY WITH
 *      STM32F4 MICROCONTROLLER FAMILLIES, IT USES 4 BIT MODE MEANS IT USES D4     *      TO D7 PINS
 *      OF LCD DISPLAY, SERVING MCU PINS. IT GIVES FLEXIBILITY OF USING ANY PIN     *       ON ANY PORT
 *      WITH LCD BY ONLY CHANGING PIN AND PORT DEFINITION.
 *
 *      ENJOY USING IT
 */


#ifndef LIQUIDCRYSTAL4BIT_H_
#define LIQUIDCRYSTAL4BIT_H_
#define _SVID_SOURCE
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include<stdio.h>
#include<stdlib.h>
#define VAL   10000


#define D4 0
#define D4Port GPIOD
#define D5 1
#define D5Port GPIOD
#define D6 2
#define D6Port GPIOD
#define D7 3
#define D7Port GPIOD

#define RS 4 //REGISTER SELECT PIN
#define RSPort GPIOD

#define EN 5 //ENABLE PIN    (TIE RW TO GROUND AS WE WILL NOT READ ANYTHING FROM LCD IN THIS LIBRARY)
#define ENPort GPIOD
/* Private variables */
/* Private function prototypes */
/* Private functions */
void gpioInit(GPIO_TypeDef *port, uint8_t pinNumber);
void pinInit(void);
void delay(uint32_t val);
void writeBit(GPIO_TypeDef *port, uint8_t pinNumber, uint8_t bitValue);
void enableSignal(void);
void LCDWriteCmd(char cmd);
void LCDWriteData(char data);
void LCDInit();
void LCDSetCursor(uint8_t x, uint8_t y);
void LCDWriteString(char *str);
void LCDWriteInteger(uint16_t val);
void LCDWriteFloat(float fval);


void gpioInit(GPIO_TypeDef *port, uint8_t pinNumber)
{
if(port==GPIOA)
{
  RCC->AHB1ENR|=(1<<0);
}
else if(port==GPIOB)
{
RCC->AHB1ENR|=(1<<1);
}
else if(port==GPIOC)
{
RCC->AHB1ENR|=(1<<2);
}
else if(port==GPIOD)
{
RCC->AHB1ENR|=(1<<3);
}
else if(port==GPIOE)
{
RCC->AHB1ENR|=(1<<4);
}
else if(port==GPIOF)
{
RCC->AHB1ENR|=(1<<5);
}
else if(port==GPIOG)
{
RCC->AHB1ENR|=(1<<6);
}
else if(port==GPIOH)
{
RCC->AHB1ENR|=(1<<7);
}
else if(port==GPIOH)
{
RCC->AHB1ENR|=(1<<8);
}
else
{
return;
}


port->MODER|=(1<<(pinNumber*2)); //SET IS AS GPIO
port->OTYPER=0x0000; //PUSH PULL CONFIGURATION
port->PUPDR=0X0000; //NO PULLUP OR PULLDOWN
port->OSPEEDR|=(3<<(pinNumber*2)); //SET IT TO HIGHEST SPEED
}

void pinInit()
{
gpioInit(D4Port, D4); //configure respective port pin
gpioInit(D5Port, D5);
gpioInit(D6Port, D6);
gpioInit(D7Port, D7);

gpioInit(RSPort, RS);
gpioInit(ENPort, EN);
}
void delay(uint32_t val)
{
volatile uint32_t i, j=0;
for(i=0;  i<val; i++)
{
j++; //increment dummy variable
}
}

void writeBit(GPIO_TypeDef *port, uint8_t pinNumber, uint8_t bitValue)
{
if(bitValue)
{
port->BSRRL|=(1<<pinNumber);
}
else
{
port->BSRRH|=(1<<pinNumber);
}
}

void enableSignal()
{
writeBit(ENPort, EN, 1);
delay(VAL);
writeBit(ENPort, EN, 0);
}
void LCDWriteCmd(char cmd)
{
char temp;
temp=cmd;
temp=temp&0xF0; //select high nibble first and send it through d4..d7

writeBit(D4Port, D4, temp&0x10);
writeBit(D5Port, D5, temp&0x20);
writeBit(D6Port, D6, temp&0x40);
writeBit(D7Port, D7, temp&0x80);

writeBit(RSPort, RS, 0); //command mode selected
enableSignal(); //generate enable signal

cmd=cmd<<4; //select lower nibble first and send it through d4..d7
writeBit(D4Port, D4, cmd&0x10);
writeBit(D5Port, D5, cmd&0x20);
writeBit(D6Port, D6, cmd&0x40);
writeBit(D7Port, D7, cmd&0x80);
    enableSignal();

delay(1000);

}

void LCDWriteData(char data)
{
char temp;
temp=data;
temp=temp&0xF0;      //select high nibble first and send it through d4..d7

writeBit(D4Port, D4, temp&0x10);
writeBit(D5Port, D5, temp&0x20);
writeBit(D6Port, D6, temp&0x40);
writeBit(D7Port, D7, temp&0x80);

writeBit(RSPort, RS, 1);            // data mode
enableSignal(); //generate enable signal

data=data<<4; //select lower nibble first and send it through d4..d7
writeBit(D4Port, D4, data&0x10);
writeBit(D5Port, D5, data&0x20);
writeBit(D6Port, D6, data&0x40);
writeBit(D7Port, D7, data&0x80);
    enableSignal();

delay(1000);

}

void LCDInit()
{
pinInit();
LCDWriteCmd(0x33);      //wake up lcd for commands
delay(1000);
LCDWriteCmd(0x32); //wake up lcd for commands
delay(1000);
LCDWriteCmd(0x20);    //init lcd for 4bit mode
delay(1000);
     LCDWriteCmd(0x28);     //2 lines, 5x7 matrix
     delay(1000);
     LCDWriteCmd(0x0e);     //display on, cursor on
LCDWriteCmd(0x01);     //clear lcd
delay(20000);
LCDWriteCmd(0x06);     //shift cursor right
delay(1000);
LCDWriteCmd(0x0c);
delay(1000);
}

void LCDSetCursor(uint8_t x, uint8_t y)
{
char firstArr[4]={0x00, 0x40, 0x14, 0x54};    //Starting addresses for 4x20 lcd display

LCDWriteCmd(0x80+firstArr[y]+x);

}

void LCDWriteString(char *str)
{
    while(*str!='\0')
    {
    LCDWriteData(*str++);
    }

}

void LCDWriteInteger(uint16_t val)
{
char buf[7];
sprintf(buf, "%i", val);
LCDWriteString(buf);
}

void LCDWriteFloat(float fval)
{
char fbuf[8];
gcvt(fval, 6, fbuf);
LCDWriteString(fbuf);
}

#endif /* LIQUIDCRYSTAL4BIT_H_ */




Example : Using Attolic true studio for st


/*
******************************************************************************
File:     main.c
Info:     Generated by Atollic TrueSTUDIO(R) 9.0.1   2018-08-11

The MIT License (MIT)
Copyright (c) 2018 STMicroelectronics

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

******************************************************************************
*/

/* Includes */

#include "LiquidCrystal4BIT.h"
#include "ADC1Lib.h"

#define TIM2_SR_UIF 0x0001
#define TIM3_SR_UIF 0x0001
#define TIM4_SR_UIF 0x0001
#define TIM5_SR_UIF 0x0001

volatile uint32_t cnt=0;
volatile uint32_t cnt1=0;
volatile uint32_t cnt2=0;
volatile uint32_t cnt3=0;
void timerConfig(TIM_TypeDef *timer, uint16_t psc, uint32_t arr);

void TIM2_IRQHandler()
{
   if(TIM2->SR&TIM2_SR_UIF)
   {
   cnt++;
       TIM2->SR&=~(1<<0); //clear pending UIF bit
   }
}
void TIM3_IRQHandler()
{
   if(TIM3->SR&TIM3_SR_UIF)
   {
   cnt1++;
       TIM3->SR&=~(1<<0); //clear pending UIF bit
   }
}

void TIM4_IRQHandler()
{
   if(TIM4->SR&TIM4_SR_UIF)
   {
   cnt2++;
       TIM4->SR&=~(1<<0); //clear pending UIF bit
   }
}
void TIM5_IRQHandler()
{
   if(TIM5->SR&TIM5_SR_UIF)
   {
   cnt3++;
       TIM5->SR&=~(1<<0); //clear pending UIF bit
   }
}

int main(void)
{
    pinInit();
    LCDInit();
    ADCPinConfig(ADCPort, ADCPin);
    ADCInit(ADC1, ADCChannel);
    LCDSetCursor(0,0);
    LCDWriteString("TIMER 2:");
    LCDSetCursor(0,1);
    LCDWriteString("TIMER 3:");
     LCDSetCursor(0,2); 
    LCDWriteString("TIMER 4:");
    LCDSetCursor(0,3);   
    LCDWriteString("TIMER 5:");
    timerConfig(TIM2, 42000, 499);
    timerConfig(TIM3, 42000, 999);
    timerConfig(TIM4, 42000, 1999);
    timerConfig(TIM5, 42000, 3999);
    NVIC_EnableIRQ(TIM2_IRQn);
    NVIC_EnableIRQ(TIM3_IRQn);
    NVIC_EnableIRQ(TIM4_IRQn);
    NVIC_EnableIRQ(TIM5_IRQn);
while(1)
{

LCDSetCursor(10,0);
LCDWriteInteger(cnt);
LCDSetCursor(10,1);
LCDWriteInteger(cnt1);
LCDSetCursor(10,2);
LCDWriteInteger(cnt2);
LCDSetCursor(10,3);
LCDWriteInteger(cnt3);
LCDSetCursor(10,0);

}
}

void timerConfig(TIM_TypeDef *timer, uint16_t psc, uint32_t arr)
{
if(timer==TIM2)
       {
   RCC->APB1ENR|=(1<<0); //CLOCK FOR TIMER 2
       }
      else if(timer==TIM3)
      {
   RCC->APB1ENR|=(1<<1); //CLOCK FOR TIMER 3
      }
      else if(timer==TIM4)
      {
   RCC->APB1ENR|=(1<<2); //CLOCK FOR TIMER 4
      }
      else if(timer==TIM5)
      {
   RCC->APB1ENR|=(1<<3); //CLOCK FOR TIMER 5
      }
      else
      {
      LCDWriteCmd(0x80);
      LCDSetCursor(0,0);
      LCDWriteString("INVALID PARAMETER");
      LCDSetCursor(0,1);
      LCDWriteString("DURING INIT....");
      exit(0);
      }
   timer->CR1&=~(1<<1);        //UPDATE ENABLE
   timer->CR1&=~(1<<4);       //UP COUNTER
   timer->CNT=0;
   timer->DIER|=(1<<0);    //uie interrupt
   timer->PSC=(uint16_t)psc;
   if(timer==TIM2||timer==TIM5)
   {
      timer->ARR=(uint32_t)arr;
   }
   else
   {
   timer->ARR=(uint16_t)arr;
   }
   timer->CR1|=(1<<0);  //TIMER ENABLE
}



THAN YOU, IF YOU HAVE ANY ISSUE PLEASE LEAVE YOU COMMENT BELOW!!!

Socializer Widget By Blogger Yard
SOCIALIZE IT →
FOLLOW US →
SHARE IT →

2 comments: