本實驗依據 LCD1602A datasheet 撰寫以Raspberry Pi Pico PIO為基底的 C Driver。
實驗項目:
- 輸入啟用密碼後,控制伺服馬達轉動。
- 更改密碼,存在開發板上的Flash Memory。
- 展示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; }
沒有留言:
張貼留言