LCD12864串口模式操作

  • Post author:
  • Post category:其他




LCD12864串口模式操作

非转载,仅为个人经验记录。如需转载请注明出处;若有侵权,请管理告知删帖。


  1. 准备工作




    a、硬件及文档


  • LCD12864(控制器型号ST7920);

  • STM32F103RCT6核心板;

  • 杜邦线若干;

  • STM32F1标准固件库V3.5;

  • ST7920中文、英文版手册;



b、开发环境


  • MDK V5.0



c、硬件连接



LCD16284 —————-> STM32F103RCT6核心板

GND ————————-> GND

VCC ————————-> 5V

V0 自己接一个5Ω可调电阻到LCD的Vout

RS —————————> GPIOA_Pin0这几根线参考lcd12864头文件

EN —————————> GPIOA_Pin1

RW —————————> GPIOA_Pin2


  1. 代码

usr_lcd12864.h

#ifndef _USR_LCD12864_H
#define _USR_LCD12864_H

#include <stdint.h> 

// 引脚及对应连接
// LCD -> STM32F103RCT6
// CS  -> GPIOA_Pin0
// SCL -> GPIOA_Pin1
// SDA -> GPIOA_Pin2
#define LCD12864_CS_PIN     GPIO_Pin_0
#define LCD12864_CS_PORT    GPIOA
#define LCD12864_CS_RCC     RCC_APB2Periph_GPIOA

#define LCD12864_SCL_PIN    GPIO_Pin_1
#define LCD12864_SCL_PORT   GPIOA
#define LCD12864_SCL_RCC    RCC_APB2Periph_GPIOA

#define LCD12864_SDA_PIN    GPIO_Pin_2
#define LCD12864_SDA_PORT   GPIOA
#define LCD12864_SDA_RCC    RCC_APB2Periph_GPIOA

// 指令预览
#define DISPLAYCLEAR        0x01
#define RETURNHOME          0x02
#define ENTRY_MODE          0x04
#define DISPLAY_CONTROL     0x08
#define CURSOR_DISPLAY      0x10
#define FUNCTION_SET        0x20
#define CGRAM_ADDR          0x40
#define DDRAM_ADDR          0x80
#define STANDBY_MODE        0x01
#define SCROLL_CGRAM_ADDR   0x02 
#define REVERSE             0x04
#define EXTENDED_FUNCTION   0x20
#define SCROLL_ADDR         0x40
#define GDRAM_ADDR          0x80

// 引脚操作声明
#define CS_SET()     LCD12864_CS_PORT->BSRR=LCD12864_CS_PIN 
#define CS_RESET()   LCD12864_CS_PORT->BRR=LCD12864_CS_PIN
#define SCL_SET()    LCD12864_SCL_PORT->BSRR=LCD12864_SCL_PIN 
#define SCL_RESET()  LCD12864_SCL_PORT->BRR=LCD12864_SCL_PIN
#define SDA_SET()    LCD12864_SDA_PORT->BSRR=LCD12864_SDA_PIN 
#define SDA_RESET()  LCD12864_SDA_PORT->BRR=LCD12864_SDA_PIN

// 函数声明
void lcd_write_byte( uint8_t dat, uint8_t datflag);
void lcd_port_init( void );
void lcd_device_init(void);
void lcd_init(void);
void lcd_write(uint8_t *dat);
void lcd_display_string(uint8_t *str, uint8_t line, uint8_t colum);
void lcd_display_picture(uint8_t *pic);
void lcd_reverse(uint8_t line_par, uint8_t colum_par, uint8_t width, uint8_t height, uint8_t page_erease, uint8_t reverse);

usr_lcd12864.c

#include "stm32f10x.h"
#include "usr_lcd12864.h"
#include "usr_systick.h"

/* @par1: the value of data or command
 * @par2: 0x00 reprenrs command; 0x01 reprenrs data;
*/
void lcd_write_byte( uint8_t dat, uint8_t datflag)
{
    uint8_t dat_tmp = 0x00;
    uint8_t i_bit   = 0;
    
    dat_tmp = ( (datflag&0x01) ? 0xFA : 0xF8 );
    
    for(i_bit=0; i_bit < 8; i_bit++)
    {
        if( dat_tmp & 0x80 )
            SDA_SET();
        else
            SDA_RESET(); 
        
        SCL_RESET();
        SCL_SET();
        SCL_RESET();
        
        dat_tmp = dat_tmp << 1;
    }
    
    delay( SYSTICK_PER_SECOND / 2000 );
    
    dat_tmp = dat & 0xF0;
    for(i_bit=0; i_bit < 8; i_bit++)
    {
        SCL_RESET();
        
        if( dat_tmp & 0x80 )
            SDA_SET();
        else
            SDA_RESET();
        
        SCL_RESET();
        SCL_SET();
        SCL_RESET();
        
        dat_tmp = dat_tmp << 1;
    }
    
    dat_tmp = dat << 4;
    for(i_bit=0; i_bit < 8; i_bit++)
    {

        
        if( dat_tmp & 0x80 )
            SDA_SET();
        else
            SDA_RESET();
        
        SCL_RESET();
        SCL_SET();
        SCL_RESET();
        
        dat_tmp = dat_tmp << 1;
    }    
}

/*
 *  防止引脚不在同一GPIO口,如在同一GPIO端口,可简化代码
 */
void lcd_port_init( void )
{
    GPIO_InitTypeDef lcd_gpio;
    
    lcd_gpio.GPIO_Pin   = LCD12864_CS_PIN;
    lcd_gpio.GPIO_Mode  = GPIO_Mode_Out_PP;
    lcd_gpio.GPIO_Speed = GPIO_Speed_10MHz;
    RCC_APB2PeriphClockCmd( LCD12864_CS_RCC, ENABLE );
    GPIO_Init( LCD12864_CS_PORT, &lcd_gpio );
    
    lcd_gpio.GPIO_Pin   = LCD12864_SCL_PIN;
    RCC_APB2PeriphClockCmd( LCD12864_SCL_RCC, ENABLE );
    GPIO_Init( LCD12864_SCL_PORT, &lcd_gpio );

    lcd_gpio.GPIO_Pin   = LCD12864_SDA_PIN;
    RCC_APB2PeriphClockCmd( LCD12864_SDA_RCC, ENABLE );
    GPIO_Init( LCD12864_SDA_PORT, &lcd_gpio );         
    
}                                                

/*
 * LCD初始化,设置工作模式、数据宽度等
 */
void lcd_device_init(void)
{
    delay(SYSTICK_PER_SECOND / 10);
    CS_SET();
    
    lcd_write_byte( 0x0C, 0x00); delay(SYSTICK_PER_SECOND / 100); 
    lcd_write_byte( 0x01, 0x00); delay(SYSTICK_PER_SECOND / 100);
}

void lcd_init(void)
{
    lcd_port_init();
    lcd_device_init();
}

/*
 * 超过一个字节时,连续写  字符串型  显示数据
 */
void lcd_write(uint8_t *dat)
{
    uint8_t *ptr =dat;
    
    while( *ptr )
    {
        lcd_write_byte( *ptr, 0x01 );
        ptr++;
    }
}

/*
 *  @par1: the character string will be displaied.
 *  @par2: the vertical position of LCD 1-4
 *  @par3: the horizontal postion of LCD 1-8
 */                                          
void lcd_display_string(uint8_t *str, uint8_t line, uint8_t colum)
{
    const uint8_t line_pos[]= {0x80, 0x90, 0x88, 0x98};
    
    lcd_write_byte( line_pos[line - 1] + (colum - 1), 0x00 );
    lcd_write( str );
}


void lcd_display_picture(uint8_t *pic)
{
    uint8_t pos_i = 0;
    uint8_t pos_j = 0;
    uint8_t *tmp  = pic;
    uint8_t pos_x = 0x80;
    uint8_t pos_y = 0x80;
    uint16_t k=0;
    
    for( pos_i = 0; pos_i < 32; pos_i ++ )
    {
        lcd_write_byte( pos_y, 0x00 );
        lcd_write_byte( pos_x, 0x00 );
        
        for( pos_j = 0; pos_j < 16; pos_j ++ )
        {
            lcd_write_byte( tmp[k++], 0x01 );
        }
        pos_y ++;
    }
    
    pos_x = 0x88;
    pos_y = 0x80;
    for( pos_i = 0; pos_i < 32; pos_i ++ )
    {
        lcd_write_byte( pos_y, 0x00 );
        lcd_write_byte( pos_x, 0x00 );
        for( pos_j = 0; pos_j < 16; pos_j ++ )
        {
            lcd_write_byte( tmp[k++], 0x01 );
        }
        pos_y ++;
    }    
    
}
/*  @par1: 1~4 big Line 
 *  @par2: 1~8 big colum
 *  @par3: widthes of each line
 *  @par4: height of each line
 *  @par5: clean display page 
 *  @par6: 0x00 nomal; 0xFF reverse
 *
*/
void lcd_reverse(uint8_t line_par, uint8_t colum_par, uint8_t width, uint8_t height, uint8_t page_erease, uint8_t reverse)
{
    uint8_t pos_x     = line_par - 1;
    uint8_t pos_y     = colum_par - 1;
    uint8_t line_i    = 0;
    uint8_t pot       = 0;
    extern uint8_t non_tab[];
    
    if(page_erease)
    {
        lcd_write_byte( 0x36, 0x00);
        lcd_display_picture(non_tab);
    }
    
    // 确定半屏横坐标基地址
    switch( line_par - 1 )
    {
        case 0: pos_x = 0x80; pos_y = 0x80; break;
        case 1: pos_x = 0x80; pos_y = 0x90; break;
        case 2: pos_x = 0x88; pos_y = 0x80; break;
        case 3: pos_x = 0x88; pos_y = 0x90; break;            
    }   
    
    // 确定在第几列
    pos_x = pos_x + colum_par - 1;

    for( line_i =0; line_i < height; line_i++ )
    {
        lcd_write_byte( 0x34,  0x00);
        lcd_write_byte( pos_y, 0x00 );
        lcd_write_byte( pos_x, 0x00 );
        lcd_write_byte( 0x30,  0x00);
        
        for( pot =0; pot < width*2; pot++)
        {
            lcd_write_byte( reverse, 0x01 );
        }
        
        pos_y ++;
    }
    
    lcd_write_byte( 0x36, 0x00 );
    lcd_write_byte( 0x30, 0x00 );
    
}

delay函数所在源文件及头文件如下:

usr_systick.h

#ifndef _USR_SYSTICK_H
#define _USR_SYSTICK_H

#include <stdint.h>
//选择延时最小单位为US
#define DELAY_UNIT_US  

//定义延时单位所需参数
#if defined  DELAY_UNIT_US
    #define SYSTICK_PER_SECOND 1000000
#elif defined DELAY_UNIT_10US
    #define SYSTICK_PER_SECOND 100000
#elif defined DELAY_UNIT_100US
    #define SYSTICK_PER_SECOND 10000
#elif defined DELAY_UNIT_MS
    #define SYSTICK_PER_SECOND 1000
#endif

#if !defined (DELAY_UNIT_US) && !defined (DELAY_UNIT_10US) && !defined (DELAY_UNIT_100US) && !defined (DELAY_UNIT_MS) 
 #error "Please select the delay unit (in systic_delay.h file)"
#endif

void delay_init( void );
void delay( uint32_t nTime );
void TimingDelay_Decrement( void );//在中断函数Systick里调用此
#endif

usr_systick.c

#include "stm32f10x.h"
#include "usr_systick.h"

static uint32_t TimingDly = 0;

void delay_init( void )
{
    if( SysTick_Config( SystemCoreClock / SYSTICK_PER_SECOND ) )
        while( 1 );
}

void TimingDelay_Decrement( void )
{
        if( TimingDly != 0x00 )
            TimingDly --;
    
}

void delay( uint32_t nTime )
{
    TimingDly = nTime;
    
    while( TimingDly != 0 );
 }

main.c测试

#include "stm32f10x.h"
#include "usr_led.h"
#include "usr_systick.h"
#include "usr_lcd12864.h"
#include "pic.h"

int main(void)
{

    uint8_t i = 0;
    delay_init();
    leds_init();
    leds_off();
    lcd_init();
    uint8_t font[]={0xB0, 0xA1, 0xB0, 0xA1,0xB0, 0xA1,0xB0, 0xA1,0xB0, 0xA1, 0xB0, 0xA1, 0xB0, 0xA1, 0xB0, 0xA1, '\0'};//字库文件的汉字代码
    
    lcd_write_byte( 0x36, 0x00 );delay(SYSTICK_PER_SECOND /100);
    lcd_display_picture(pic_tab);

    delay(SYSTICK_PER_SECOND / 2);
    lcd_display_picture(non_tab);
    lcd_write_byte( 0x30, 0x00 );delay(SYSTICK_PER_SECOND /100); // 10ms
    lcd_write_byte( DISPLAYCLEAR, 0x00 );
    lcd_display_string("  床前明月光,", 1, 1);
    lcd_display_string("  疑是地上霜;", 2, 1);
    lcd_display_string("  举头望明月,", 3, 1);
    lcd_display_string("  低头思故乡。", 4, 1);
    
    lcd_reverse( 4, 2 , 6, 16,0x00, 0x00);
    lcd_reverse( 1, 2 , 6, 16,0x00, 0xFF);
    delay(SYSTICK_PER_SECOND / 2); //500ms
    lcd_reverse( 1, 2 , 6, 16,0x00, 0x00);
    lcd_reverse( 2, 2 , 6, 16,0x00, 0xFF);
    delay(SYSTICK_PER_SECOND / 2);    
    lcd_reverse( 2, 2 , 6, 16,0x00, 0x00);
    lcd_reverse( 3, 2 , 6, 16,0x00, 0xFF);
    delay(SYSTICK_PER_SECOND / 2);    
    lcd_reverse( 3, 2 , 6, 16,0x00, 0x00);
    lcd_reverse( 4, 2 , 6, 16,0x00, 0xFF);
    delay(SYSTICK_PER_SECOND / 2); 
    lcd_reverse( 4, 2 , 6, 16,0x00, 0x00);
    
    lcd_display_string("朝辞白帝彩云间,", 1, 1);
    
    while (1)
    { }
}

  1. 总结


    要注意,写代码时的文件编码格式。如果英文字符能调通正常显示时,但是显示汉字乱码时,请参考手册,选择特定的字库中汉字测试,如font[]数组中的0xB0A1就是字库中”啊”的字库码。如能正常显示啊字符串,说明只是文件编码问题,此时请设置为GB2312,或者器件指定的汉字编码所用格式。反白比较麻烦,目前反白只能指定大行(16小行)位置、宽度、高度,很不完善。多看英文文档,参考中文文档,收获就会很大。


  2. 后续维护


    未完待续。。。



版权声明:本文为sinat_25020831原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。