本實驗依據 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;
}

沒有留言:
張貼留言