NRF51822——LCD128X64驱动

版权声明:原创文章转载请注明出处。 https://blog.csdn.net/wwt18811707971/article/details/81702970

1.概述

128X64的LCD可以显示 128 列*64 行点阵单色图片,或显示 8 个/行*4 行 16*16 点阵的汉字,或显示 16 个/行*8 行 8*8 点阵的英文、数字、符号。驱动IC为 ST7565R。在 LCD 上排列着 128×64 点阵,128 个列信号与驱动 IC 相连,64 个行信号也与驱动 IC 相连,IC 邦定在 LCD 玻璃上( COG工艺)。

2.硬件设计

2.1 连接原理图

这里写图片描述

硬件连接:

这里写图片描述

2.2 模块的引脚功能

模块通过 SPI 进行通信,需要的引脚如下图:

这里写图片描述

2.3 点阵与 DD RAM 地址的对应

**页定义:**PAGE,与平时所讲的“页”并不是一个意思,在此表示 8 个行就是一个“页”,一个 128*32 点阵的屏分为 8 个“页”,从第 0“页”到第 7“页”。

这里写图片描述

3.软件实现

这里写图片描述

3.1 模拟SPI时序

//头文件
#ifndef _SPI_H_
#define _SPI_H_

#include "nrf51.h"
#include "nrf_gpio.h"

#define SPI_CLK       2
#define SPI_CLK_LOW         nrf_gpio_pin_clear(SPI_CLK)
#define SPI_CLK_HIGH        nrf_gpio_pin_set(SPI_CLK)

#define SPI_MOSI       1
#define SPI_MOSI_LOW        nrf_gpio_pin_clear(SPI_MOSI )
#define SPI_MOSI_HIGH       nrf_gpio_pin_set(SPI_MOSI)

#define SPI_MISO       0
#define SPI_MISO_LOW        nrf_gpio_pin_clear(SPI_MISO )
#define SPI_MISO_HIGH       nrf_gpio_pin_set(SPI_MISO)
#define SPI_MISO_READ       nrf_gpio_pin_read(SPI_MISO)

void Spi_Gpio_Init(void);
void Spi_Write_Byte(uint8_t dat);
uint8_t Spi_Read_Byte(void);

#endif
//源程序
#include "spi.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include <stdint.h>
#include "lcd128x64.h"

//定义SPI四钟方式的0和3
#define _CPOL   0
#define _CPHA   0

void Spi_Gpio_Init(void)
{
    nrf_gpio_cfg_output(SPI_CLK);
    nrf_gpio_cfg_output(SPI_MOSI);
    nrf_gpio_cfg_input(SPI_MISO,NRF_GPIO_PIN_NOPULL);
#if _CPOL==0
    SPI_CLK_LOW;
#else
    SPI_CLK_HIGH;
#endif
}

//模式0:CPOL=0;CPAH=0
//模拟SPI写数据
#if _CPOL==0&&_CPHA==0  
void Spi_Write_Byte(uint8_t dat)
{
    uint8_t len;

    for(len=0;len<8;len++)
    {
        SPI_CLK_LOW;//时钟上升沿采样传输数据
                //nrf_delay_ms(1);
        if(dat&0x80)    //发送数据
                {
                    SPI_MOSI_HIGH;
                } 
        else
        {
                    SPI_MOSI_LOW;
                }
        SPI_CLK_HIGH;
            //  nrf_delay_ms(1);
        dat <<= 1;
    }
}

//模拟SPI读数据
uint8_t Spi_Read_Byte(void)
{
    uint8_t cnt;
    uint8_t dat=0;

    for(cnt=0;cnt<8;cnt++)  
    {
        SPI_CLK_LOW;    //下降沿后读取数据
        nrf_delay_ms(1);
        dat <<= 1;  //最高位已经在数据线上了
        if(SPI_MISO_READ)   //
        {
            dat |= 0x01;
        }
        else
        {
            dat &= 0xfe;
        }
        //dat <<= 1;注:此处最高位被移除了
        SPI_CLK_HIGH;
        nrf_delay_ms(1);
    }

    return dat;
}
#endif


//模式3:CPOL=1;CPAH=1
//模拟SPI写数据
#if _CPOL==1&&_CPHA==1            //MODE  1  1
void Spi_Write_Byte(uint8_t dat)
{
    uint8_t len;

    SPI_CLK_HIGH;
    for(len=0;len<8;len++)
    {
        SPI_CLK_LOW;//时钟上升沿输出数据
                //nrf_delay_ms(1);
        if(dat&0x80)    //发送数据
                {
                    SPI_MOSI_HIGH;
                } 
        else
        {
                    SPI_MOSI_LOW;
                }
        SPI_CLK_HIGH;
            //  nrf_delay_ms(1);
        dat <<= 1;
    }
}

//模拟SPI读数据
uint8_t Spi_Read_Byte(void)
{
    uint8_t cnt;
    uint8_t dat=0;

    SPI_CLK_LOW;
    for(cnt=0;cnt<8;cnt++)  
    {
        SPI_CLK_LOW;
        nrf_delay_ms(1);
        dat <<= 1;
        if(SPI_MISO_READ)   
        {
            dat |= 0x01;
        }
        else
        {
            dat &= 0xfe;
        }
        SPI_CLK_HIGH;
        nrf_delay_ms(1);
    }
    SPI_CLK_HIGH;

    return dat;
}
#endif

模拟SPI的介绍请移步SPI专题(一)——基础知识

3.2 LCD驱动

//头文件
#ifndef __LCD128X64__H__
#define __LCD128X64__H__

#include "nrf51.h"
#include "nrf_gpio.h"

#define Max_Column 128
#define Max_Row     64 
#define SIZE 16

#define LCD_CMD  0  //写命令
#define LCD_DATA 1  //写数据

#define LED_LIGHT1  28

#define LCD_CS         3
#define LCD_CS_LOW          nrf_gpio_pin_clear(LCD_CS)
#define LCD_CS_HIGH         nrf_gpio_pin_set(LCD_CS)

#define LCD_RST       4
#define LCD_RST_LOW         nrf_gpio_pin_clear(LCD_RST)
#define LCD_RST_HIGH        nrf_gpio_pin_set(LCD_RST)

#define LCD_A0         5
#define LCD_A0_LOW          nrf_gpio_pin_clear(LCD_A0)
#define LCD_A0_HIGH         nrf_gpio_pin_set(LCD_A0)


typedef enum
{
    FONT_5X8 = 1,
    FONT_8X16,
    FONT_16X16,
    FONT_128X64,
    SHOW_POINT,
    SHOW_LINE
}Typeface;

void GPIO_LCD_Init(void);
void LCD_Write_Byte(uint8_t dat,uint8_t cmd);

void LCD_Init(void);
void LCD_Set_Pos(uint8_t page,uint8_t column);
void LCD_Display_Clear(void);

void LCD_Draw_Point(uint8_t page,uint8_t column);
void LCD_Draw_Line_Y(uint8_t page,uint8_t column);
void LCD_All_Screen(void);
void Lcd_Display_OneChar(uint8_t page,uint8_t column,uint8_t str);
void Lcd_Display_String(uint8_t page,uint8_t column,uint8_t *str);
uint32_t Lcd_Pow(uint8_t m,uint8_t n);
void Lcd_Display_Num(uint8_t page,uint8_t column,uint32_t num,uint8_t len,uint8_t size_num);


void Display_128x64(uint8_t *dp);
void Display_Graphic_5x8(uint8_t page,uint8_t column,uint8_t *dp);
void Display_Graphic_8x16(uint8_t page,uint8_t column,uint8_t *dp);
void Display_Graphic_16x16(uint8_t page,uint8_t column,uint8_t *dp);

void DisplayFont(uint8_t page, uint8_t column, uint8_t No, uint8_t typeface);
void display_string_8x16(uint8_t page,uint8_t column,uint8_t *text);

void Lcd_Light(void);

void OLED_Init(void);

void LCD_Draw_Line_X(uint8_t page,uint8_t column);

#endif
//源文件,相关GPIO初始化
void GPIO_LCD_Init(void)
{
    nrf_gpio_cfg_output(LCD_CS);
    nrf_gpio_cfg_output(LCD_RST);
    nrf_gpio_cfg_output(LCD_A0);

    LCD_CS_HIGH;
}

MCU向LCD的写入数据:

这里写图片描述

//LCD写数据
void LCD_Write_Byte(uint8_t dat,uint8_t cmd)
{
   // uint8_t len;
        LCD_CS_LOW;

    if(cmd)
        LCD_A0_HIGH;
    else
        LCD_A0_LOW;

    Spi_Write_Byte(dat);
    LCD_CS_HIGH;
    LCD_A0_HIGH;
}
//初始化
void LCD_Init(void)
{
    LCD_RST_HIGH;
    nrf_delay_ms(20);
    LCD_RST_LOW;
    nrf_delay_ms(20);
    LCD_RST_HIGH;

    LCD_Write_Byte(0xe2,LCD_CMD);   //软复位
    nrf_delay_ms(20);

    LCD_Write_Byte(0xae,LCD_CMD);//显示关闭
    LCD_Write_Byte(0x2c,LCD_CMD);// open VB circuit
    nrf_delay_ms(20);
    LCD_Write_Byte(0x2e,LCD_CMD);// open VR circuit
    nrf_delay_ms(20);
    LCD_Write_Byte(0x2f,LCD_CMD);//voltage follower ON  regulator ON  booster ON
    nrf_delay_ms(20);
    LCD_Write_Byte(0x22,LCD_CMD);
    LCD_Write_Byte(0x81,LCD_CMD);
    LCD_Write_Byte(0x3f,LCD_CMD);
    LCD_Write_Byte(0xa2,LCD_CMD);//

    LCD_Write_Byte(0xa0,LCD_CMD);
    LCD_Write_Byte(0xc8,LCD_CMD);//com64 --> com1

    LCD_Write_Byte(0x10,LCD_CMD);//Set Column Address 4 higher bits = 0
    LCD_Write_Byte(0x00,LCD_CMD);//Set Column Address 4 lower bits = 1 , from IC SEG1 -> SEG128
    LCD_Write_Byte(0xb0,LCD_CMD);//Set Page Address = 0
    LCD_Write_Byte(0xa6,LCD_CMD);//Normal Display (not reverse dispplay)
    LCD_Write_Byte(0xaf,LCD_CMD);//Display ON
    /*
    LCD_Write_Byte(0xa0,LCD_CMD);  //列扫描顺序:从左到右
    LCD_Write_Byte(0xc0,LCD_CMD);  //行扫描顺序:C8:从下到上,c0:从上到下
    LCD_Write_Byte(0xa2,LCD_CMD);    //设置偏压比1/9
    LCD_Write_Byte(0x2f,LCD_CMD);   //控制电源
    LCD_Write_Byte(0x25,LCD_CMD);   //粗调对比度
    LCD_Write_Byte(0x81,LCD_CMD);  //微调对比度,进入微调对比度命令
    LCD_Write_Byte(0x10,LCD_CMD);  //设置电压的参数RR值
    LCD_Write_Byte(0x40,LCD_CMD);  //起始行:第一行开始
    LCD_Write_Byte(0xaf,LCD_CMD);  //开显示*/

    LCD_Display_Clear();
    LCD_Set_Pos(0,0);
}
//坐标设置
void LCD_Set_Pos(uint8_t page,uint8_t column)
{
    LCD_Write_Byte(0xb0 + page,LCD_CMD);    //64行分成8个页
    LCD_Write_Byte(((column >> 4) & 0x0f) | 0x10,LCD_CMD);  //列地址高四位
    LCD_Write_Byte((column & 0x0f) | 0x01,LCD_CMD);     //设置列地址的低四位
}
//清屏
/清屏
void LCD_Display_Clear(void)
{
    uint8_t page,j;

    for(page=0;page<8;page++)
    {
        LCD_Write_Byte(0xb0+page,LCD_CMD);  //设置显示页地址,即横坐标
        LCD_Write_Byte(0X00,LCD_CMD);   //列地址的低 4 位
        LCD_Write_Byte(0X10,LCD_CMD);   //列地址的高 4 位
        for(j=0;j<128;j++)
        {
            LCD_Write_Byte(0X00,LCD_DATA);//写入1则显示,写入0则清屏
        }
    }
}
//显示相关实现
/*显示128x64点阵图像*/
void Display_128x64(uint8_t *dp)
{
    uint8_t i,j;

    for(j=0;j<8;j++)
    {
        LCD_Set_Pos(j+1,1);
        for (i=0;i<128;i++)
        {   
          LCD_Write_Byte(*dp,LCD_DATA);     /*写数据到LCD,每写完一个8位的数据后列地址自动加1*/
          dp++;
        }
    }
}

//显示一个点
void LCD_Draw_Point(uint8_t page,uint8_t column)
{
    LCD_Set_Pos(page, column);
    LCD_Write_Byte(0x01,LCD_DATA);
}

//划线
void LCD_Draw_Line_Y(uint8_t page,uint8_t column)
{
    uint8_t i,j;

    for(i=page;i<8;i++)
    {
        LCD_Write_Byte(0xb0+i,LCD_CMD); //设置显示页地址,即横坐标
        for(j=0;j<0x08;j++)
        {
            LCD_Write_Byte(0x00+j,LCD_CMD); //只加其中低或者高,相当于连续显示竖线
            LCD_Write_Byte(0X10+j,LCD_CMD);     //横纵坐标都同时递增,相当于竖线平移
            LCD_Write_Byte(0Xff,LCD_DATA);//写入1则显示,写入0则清屏
        }
    }
}

//可构造斑马线
void LCD_Draw_Line_X(uint8_t page,uint8_t column)
{
    uint8_t i,j;

    LCD_Set_Pos(page, column);
    for(j=0;j<8;j++)
    {
        LCD_Write_Byte(0xb0+j,LCD_CMD);
        LCD_Write_Byte(0X00,LCD_CMD);   //列地址的低 4 位
        LCD_Write_Byte(0X10,LCD_CMD);   //列地址的高 4 位
        for(i=0;i<128;i++)
        {
            //LCD_Write_Byte(0x0f,LCD_DATA);
            LCD_Write_Byte(0x01,LCD_DATA);
        }
    }
}

/显示单独一个字符
void Lcd_Display_OneChar(uint8_t page,uint8_t column,uint8_t str)
{
    uint8_t i=0,ret=0;
    //ret = str -32;
    ret = str - ' ';//得到偏移后的值,对ASCLL码进行一个减法.即在二维数组里找它的位置  
    if(column>Max_Column-1)
    {
        column = 0;
        page = page + 2;//针对16号的字符
    }
    if(SIZE == 16 )
    {
        LCD_Set_Pos(page,column);
        //16的字体分成两部分写入
        for(i=0;i<8;i++)
        {
            LCD_Write_Byte(F8X16[ret*16+i],LCD_DATA);
        }
        LCD_Set_Pos(page+1,column);//页地址增加1
        for(i=0;i<8;i++)
        {
                LCD_Write_Byte(F8X16[ret*16+i+8],LCD_DATA);
        }
    }
    else{
        LCD_Set_Pos(page,column+1);
        for(i=0;i<6;i++)
            LCD_Write_Byte(F6x8[ret][i],LCD_DATA);
    }
}
//字符串显示
void Lcd_Display_String(uint8_t page,uint8_t column,uint8_t *str)
{
    uint8_t i=0;

    while(str[i]!='\0')
    {
        Lcd_Display_OneChar(page,column,str[i]);
        column += 8;
        if(column>120)
        {
            column = 0;
            page += 2;
        }
        i++;
    }
}

//计算m的n次方
uint32_t Lcd_Pow(uint8_t m,uint8_t n)
{
    uint32_t ret = 1;

    while(n--)
        {
            ret *= m;
        }

    return ret;
}

//显示数字
//*num:显示的数字
void Lcd_Display_Num(uint8_t page,uint8_t column,uint32_t num,uint8_t len,uint8_t size_num)
{
    uint8_t t,temp;
    uint8_t flag = 0;

    for(t=0;t<len;t++)
    {
        temp = (num / Lcd_Pow(10,len-t-1)) % 10;//把显示的数字一位一位取出来
        if(((flag==0) && t) < (len-1))
        {
            if(temp == 0)
            {
                Lcd_Display_OneChar(page,column + (size_num/2) * t,'0');
                continue;//temp=0时,结束本次循环,进入下次循环,但不跳出循环
            }
            else
            {
                            flag = 1;
            }

        }
        Lcd_Display_OneChar(page,column + (size_num/2) * t,temp + '0');
    }
}

注:程序中涉及的字库可通过取自摸软件得到。

猜你喜欢

转载自blog.csdn.net/wwt18811707971/article/details/81702970
今日推荐