prettyprint

2022年12月7日 星期三

[Raspberry Pi Pico (c-sdk)] Display: Ep 1. PIO LCD1602A C-Code Driver & 4x4 keypad

本實驗依據 LCD1602A datasheet 撰寫以Raspberry Pi Pico PIO為基底的 C Driver。


實驗項目:

  1. 輸入啟用密碼後,控制伺服馬達轉動。
  2. 更改密碼,存在開發板上的Flash Memory。
  3. 展示LCD1602 CGRAM,Cursor Blink,Text Scroll 功能。

接線:



LCD1602指令表:

使用4 bit模式,因此每個指令須依序送出4 High bit與 4 Low bits。

例如Display clear指令為0000000001(RS,RW D7~D0),則依序送出00 0000(0x00, RS,RW D7~D4)與0x00 0001(0x01)相對Pi Pico PIO指令為:


 成果影片:



程式碼:


4x4 matrix keypad請參閱:

Raspberry Pi Pico PIO(Programmable IO) Episode 1: 4x4 matrix keypad

  • lcd1602_4b.c

#include "stdio.h"
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "lcd1602_pio.h"
#include "lcd1602.h"
#include "hardware/clocks.h"
#include "string.h"
PIO lcd_pio = pio0;
uint lcd_sm = 0;
uint lcd_out_base=16; //rs, rw, db7, db6, db5, db4
uint lcd_en_pin = 22; // En

static uint8_t CGRAM[8][8] = {
    {0x00,0x0a,0x1f,0x1f,0x1f,0x0e,0x04,0x00},
    {0x1f,0x15,0x00,0x00,0x00,0x11,0x1b,0x1f},
    {0x04,0x0e,0x0a,0x0a,0x0a,0x0a,0x0e,0x00},
    {0x04,0x0e,0x0a,0x0a,0x0e,0x0e,0x0e,0x00},
    {0x04,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x00},
    {0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
};



void lcd1602_4b_pio_init(PIO pio, uint sm, uint out_base,  uint en_pin, uint freq) {
    uint offset=0;
    pio_sm_config c;
    offset = pio_add_program(pio, &lcd1602_4b_program);
    c = lcd1602_4b_program_get_default_config(offset);
    
    for (int i=0; i < 6; i++) pio_gpio_init(pio, out_base+i);
    pio_gpio_init(pio, en_pin);

    pio_sm_set_consecutive_pindirs(pio, sm, out_base, 6, true);
    pio_sm_set_consecutive_pindirs(pio, sm, en_pin, 1, true);

    sm_config_set_out_pins(&c, out_base, 6);
    sm_config_set_set_pins(&c, en_pin, 1);
   
    sm_config_set_jmp_pin(&c, out_base+3);
    
    sm_config_set_out_shift(&c, true, false, 32);

    float div = clock_get_hz(clk_sys)/freq;
    sm_config_set_clkdiv(&c, div);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
   
}

void lcd1602_set_cursor(uint8_t line, uint8_t pos) {
    if (line > 1 || line < 0 || pos < 0 || pos > 15) return;
    pio_sm_put_blocking(pio0, 0, 0x08 | line << 2);
    pio_sm_put_blocking(pio0, 0, 0x00 | pos);
}
void lcd1602_display_cursor_on_off(uint8_t on_off, uint8_t cursor_on_off, uint8_t cursor_blink) {
    uint8_t stat = 0x8;
    if (on_off) stat = stat | (on_off << 2);
    if (cursor_on_off) {
        stat = stat | (cursor_on_off << 1);
        if (cursor_blink) stat = stat | (cursor_blink &0x01);
    }
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00);
    pio_sm_put_blocking(lcd_pio, lcd_sm, stat);

    
}
void lcd1602_cursor_shift(uint8_t right) {
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x01); // cursor shift left
    if (right)
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x06);
     else 
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x04);
    
}
void lcd1602_clear() {
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00); //display clear
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x01);
    sleep_ms(2);
    
}

void lcd1602_print_ch(uint8_t l, uint8_t p, uint8_t c) {
    if (l > 1 || l < 0 || p < 0 || p > 15) return;
    pio_sm_put_blocking(pio0, 0, 0x08 | l << 2);
    pio_sm_put_blocking(pio0, 0, 0x00 | p);
    pio_sm_put_blocking(pio0, 0, 0x20 | (c & 0xff) >> 4);
    pio_sm_put_blocking(pio0, 0, 0x20 | (c & 0x0f));
}
void lcd1602_print(uint8_t l, uint8_t p, uint8_t * buff) {
    if (l > 1 || l < 0 || p < 0 || p > 15) return;
    uint slen= strlen(buff);
 
    if (!slen) return;
    slen = (slen+p)>16?16-p:slen;   // max positon is 15

    for (uint8_t i = p;  i < p + slen; i++) {
        /* set address*/
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x08 | l << 2);
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00 | i);
        /* put data */
        pio_sm_put_blocking(lcd_pio, lcd_sm, ((buff[i-p] & 0xff) >> 4) | 0x20);
        pio_sm_put_blocking(lcd_pio, lcd_sm, (buff[i-p]&0x0f) | 0x20);

    }
    
}
void lcd1602_scroll(uint8_t line, uint8_t *buff, bool left, uint16_t delay_ms, int loop_count) {
    uint8_t shift_out_char;
    uint8_t len = strlen(buff);
    uint8_t *l_buff = (uint8_t*) malloc(len+1);
    strcpy(l_buff, buff);
    lcd1602_print(line, 0, l_buff);
    for (int l = 0; l < loop_count; l++) {
        if (left) {
            sleep_ms(delay_ms);
            shift_out_char = l_buff[0];
            for (int i = 0; i < len-1; i++) {
                l_buff[i] = l_buff[i+1];
            }
            l_buff[len-1]=shift_out_char;

            lcd1602_print(line, 0, l_buff);
        

        } else {

            sleep_ms(delay_ms);
            shift_out_char = l_buff[len-1];
            for (int i = len-1; i > 0; i--) l_buff[i] = l_buff[i-1];
            l_buff[0]=shift_out_char;
            lcd1602_print(line, 0, l_buff);

        }
    }
    free(l_buff);

}
void lcd1602_pw_on_init() {
    sleep_ms(16);
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x03);
     pio_sm_put_blocking(lcd_pio, lcd_sm, 0x03);
 
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x03 );
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x02);

    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x02 );  // 2 line, font 5*8 
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x08);

    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00);  // display on, cursor off
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x0c);

    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00);  // entry mode
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x06);
  

    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00);
    pio_sm_put_blocking(lcd_pio, lcd_sm, 0x01 );  // display clear
    sleep_ms(1000);
}
void lcd1602_write_to_CGRAM() {
    for (uint8_t i = 0; i < 8; i++) {
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x04 | ((i << 3) & 0xf0) >> 4 ); //CGRAM address i
        pio_sm_put_blocking(lcd_pio, lcd_sm, 0x00 | (i << 3)&0x0f);
        for (uint8_t j =0; j < 8; j++) {
            pio_sm_put_blocking(lcd_pio, lcd_sm, 0x20 | (CGRAM[i][j] & 0xff)>>4); //CGRAM data j
            pio_sm_put_blocking(lcd_pio, lcd_sm, 0x20 |  CGRAM[i][j] & 0x0f);
        }
    }
    
    
}
void lcd1602_init() {
    /* bits */
    lcd1602_4b_pio_init(lcd_pio, lcd_sm, lcd_out_base,  lcd_en_pin, 100000);
    
    lcd1602_pw_on_init();

    lcd1602_write_to_CGRAM();
    
}

  • lcd1602_4b.pio

.program lcd1602_4b
; RS,R/W, DB7,DB6, Db5, DB4   : bits order
.wrap_target
pull
check_busy:
set x, 0x10
mov pins, x
set pins, 0
set pins, 1 [2]
set pins, 0 [1]
jmp pin check_busy

set pins,0
out pins, 6 
set pins,1 [3]
set pins,0 [2]

  • lcd1602.h

#ifndef __LCD1602_H
#define __LCD1602_H

void lcd1602_init();
void lcd1602_print(uint8_t l, uint8_t p, uint8_t * buff);
void lcd1602_print_ch(uint8_t l, uint8_t p, uint8_t c);
void lcd1602_clear();
void lcd1602_display_cursor_on_off(uint8_t on_off, uint8_t cursor_on_off, uint8_t cursor_blink);
void lcd1602_cursor_shift(uint8_t right);
void lcd1602_set_cursor(uint8_t line, uint8_t pos);
void lcd1602_scroll(uint8_t line, uint8_t *buff, bool left, uint16_t delay_ms, int loop_count);

#endif

  • main.c

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/pwm.h"
#include "hardware/flash.h"
#include "lcd1602.h"
#include "keypad.h"
#include "string.h"

#define PWM_PIN 28
#define XIP_OFFSET (0x153600)

uint8_t test_passwd[7]="123456";
uint8_t passwd[7], apasswd[7];
uint8_t passLen=0;
uint8_t passRow=0;
int slice, pwm_ch;

enum {
    MENU=1,
    ENTER_PWD,
    VALIFIED,
    CHANGE_PWD
} State;

void setServoAngle(int ang) {
    uint16_t top_count = (uint16_t)(ang/0.09 + 1500);  // angle 0: 1.5 ms duty
    pwm_set_chan_level(slice, pwm_ch, top_count);

}
void show_menu() {
    lcd1602_clear();
    lcd1602_display_cursor_on_off(1,0,0);
    lcd1602_print(0,4, "Hellow!");
    lcd1602_print(1,0, "*:Enter Password");
    State = MENU;
}
void function_A() {
    setServoAngle(90);
}
void function_B() {
    setServoAngle(0);

}
void save_password() {
    char tempbuff[256];
    lcd1602_clear();
    lcd1602_display_cursor_on_off(1,0,0);
    lcd1602_print(0,0, "PWD Changed!");
    strcpy(test_passwd,passwd);
    memset(tempbuff, 0, 256);
    sprintf(tempbuff, "Password:%s", passwd);
    flash_range_erase(XIP_OFFSET, 4096);
    flash_range_program(XIP_OFFSET, tempbuff, 256);
    sleep_ms(1000);
    show_menu();
}
void load_password() {
    char tempbuff[256];
    memset(tempbuff, 0, 256);
    strncpy(tempbuff, (uint8_t*)(XIP_BASE+XIP_OFFSET), 15);
    if (strstr(tempbuff, "Password:")) {
        memset(test_passwd, 0, 6);
        for (int i =0; i < 6; i++) {
            test_passwd[i]=*(uint8_t*)(XIP_BASE+XIP_OFFSET+9+i);
        }
    }
}
void servo_init() {
    // set Servo pwm
    gpio_set_function(PWM_PIN, GPIO_FUNC_PWM);
    slice = pwm_gpio_to_slice_num(PWM_PIN);
    pwm_ch = pwm_gpio_to_channel(PWM_PIN);
    pwm_config c = pwm_get_default_config();
    pwm_config_set_clkdiv(&c, 125);  // 20ms period, steps 20000, clkdiv = 125 
    pwm_config_set_wrap(&c, 20000);
    pwm_config_set_phase_correct(&c, false);
    pwm_init(slice, &c, true);    
    
    setServoAngle(0);
}
void show_change_pwd() {
    lcd1602_clear();
    lcd1602_display_cursor_on_off(1,1,1);
    lcd1602_print(0,0, "Enter PWD:");
    lcd1602_print(1,0, "PWD AGAIN:");
    passRow=0;
    lcd1602_set_cursor(passRow,10);
    memset(passwd,0,6);
    memset(apasswd,0,6);
    passLen=0;
    State=CHANGE_PWD;
}

void demo() {
    lcd1602_clear();
    lcd1602_print(0, 0, "Some Demos");
    sleep_ms(1000);
    lcd1602_print(0, 0, "Print CGRAM Char");
    lcd1602_print_ch(1,0,0);
    lcd1602_print_ch(1,1,1);
    lcd1602_print_ch(1,2,2);
    lcd1602_print_ch(1,3,3);
    lcd1602_print_ch(1,4,4);
    lcd1602_print_ch(1,5,5);
    sleep_ms(3000);
    lcd1602_clear();
    lcd1602_print(0, 0, "Cursor On     ");
    lcd1602_display_cursor_on_off(1, 1, 0);
    lcd1602_print(1, 0, "              ");
    lcd1602_set_cursor(1,5);
    sleep_ms(3000);
    lcd1602_print(0, 0, "Cursor Blink   ");
    lcd1602_display_cursor_on_off(1, 1, 1);
    lcd1602_print(1, 0, "               ");
    lcd1602_set_cursor(1,5);
    sleep_ms(4000);
    lcd1602_print(0, 0, "Cursor Off    ");
    lcd1602_display_cursor_on_off(1, 0, 0);
    lcd1602_print(1, 0, "              ");
    lcd1602_set_cursor(1,5);
    sleep_ms(2000);
    lcd1602_print(0, 0, "Scroll left   ");
    lcd1602_scroll(1, "This is a scrolling messages.",true, 500, 20);
    lcd1602_clear();
     sleep_ms(1000);
    lcd1602_print(0, 0, "Scroll Right   ");
    lcd1602_scroll(1, "This is a scrolling messages.",false, 500, 20);
 
 show_menu();

}

int main()
{
    uint8_t key;
    
    stdio_init_all();
    keypad_init();
    lcd1602_init();
    load_password();
    servo_init();
    
    show_menu();
    while (1) {
        key = get_new_keypad_value();
        switch (key) {
            case 0:
            break;
            case '*':
                lcd1602_clear();
                lcd1602_display_cursor_on_off(1,1,1);
                lcd1602_print(0,0, "Enter PWD:");
                lcd1602_print(1,0, "C:Menu, #:Enter ");
                lcd1602_set_cursor(0,10);
                State=ENTER_PWD;
                memset(passwd,0,6);
                passLen=0;
                passRow=0;
            break;
            case '#':
                if (State == ENTER_PWD) {
                    if (strcmp(passwd, test_passwd)==0) {
                        State = VALIFIED;
                        lcd1602_display_cursor_on_off(1,0,0);
                        lcd1602_print(0,0, "A: UP, B:Down   ");
                        lcd1602_print(1,0, "Confirm!       ");
                        sleep_ms(1000);      
                        lcd1602_print(1,0, "C:Menu, D:CH PWD");     
                    }
                    else {
                        lcd1602_print(1,0, "Password Error! ");
                        lcd1602_print(0,10,"      ");
                        lcd1602_display_cursor_on_off(1,1,1);
                        lcd1602_set_cursor(0,10);
                        memset(passwd,0,6);
                        passLen=0;
                    }
                } 
                if (State == CHANGE_PWD) {
                    if (passRow == 0) {
                        lcd1602_set_cursor(++passRow, 10);
                        passLen=0;
                    } else {
                        if (strcmp(passwd, apasswd)!=0) {
                            lcd1602_clear();
                            lcd1602_print(1,0, "Mismatch!");
                            sleep_ms(1000); 
                            show_change_pwd();
                        } else {
                            save_password();
                        }
                    }


                }
            break;
            case 'A':
                if (State == VALIFIED) {
                    
                    function_A();
                }
            break;
            case 'B':
                if (State == VALIFIED) {
                    
                    function_B();
                }
            break;
            case 'C':
                show_menu();
                demo();

            break;
            case 'D':
                if (State == VALIFIED) {
                    show_change_pwd();
                }
            break;
            default:
                if ((State==ENTER_PWD || State == CHANGE_PWD) && passLen < 6) {
                    lcd1602_print_ch(passRow,10+passLen, '*');
                    if (passRow == 0) 
                        passwd[passLen++] = key;
                    else 
                        apasswd[passLen++] = key;
                }
            break;
        }
        sleep_ms(1);
    }
    return 0;
}





沒有留言:

張貼留言