本文章介紹使用Raspberry Pi Pico的PIO功能製作一個8位數的七段式顯示器的驅動程式,另外結合以前使用PIO製作的4x4 KEYPAD作為輸入,建構一個簡單的計算器。
8位數七段顯示除使用8個共陰極的顯示器外,另外使用2個74HC595 shift register與8個NPN電晶體,第一個shift register控制七段顯示器A~G與dp的顯示,第二個shift register Q0~Q7連接個別7段顯示器的pin 3/8控制顯示器顯示(low)或關閉(high)。
線路圖下所示:
顯示方式以快速一次顯示一個7段顯示器,因此每顯示一個位數須輸出2 bytes,第一個byte為7段顯示器內容,第二個byte為第幾個顯示器開啟(其他關閉)。
PIO程式部份使用side-set 控制shift register 的SH_CP與ST_CP clock,程式碼如文末所示。
4x4 keypad詳細內容請參閱:
Raspberry Pi Pico PIO(Programmable IO) Episode 1: 4x4 matrix keypad
成果影片
程式碼:
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) # == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == if(WIN32) set(USERHOME $ENV{USERPROFILE}) else() set(USERHOME $ENV{HOME}) endif() set(sdkVersion 2.1.0) set(toolchainVersion 13_3_Rel1) set(picotoolVersion 2.1.0) set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) project(pico_pio_7seg C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(pico_pio_7seg pico_pio_7seg.c) pico_set_program_name(pico_pio_7seg "pico_pio_7seg") pico_set_program_version(pico_pio_7seg "0.1") target_compile_definitions(pico_pio_7seg PRIVATE WIFI_SSID="$ENV{WIFI_SSID}" ) # Modify the below lines to enable/disable output over UART/USB pico_enable_stdio_uart(pico_pio_7seg 0) pico_enable_stdio_usb(pico_pio_7seg 1) # Add the standard library to the build target_link_libraries(pico_pio_7seg pico_stdlib ) # Add the standard include files to the build target_include_directories(pico_pio_7seg PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) # Add any user requested libraries target_link_libraries(pico_pio_7seg hardware_pio ) add_subdirectory(disp_7segment) add_subdirectory(pico_keypad) target_link_libraries(pico_pio_7seg disp_7segment pico_keypad) pico_add_extra_outputs(pico_pio_7seg)
pico_pio_7seg.c
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/pio.h" #include "pico/multicore.h" #include "disp_7segment.pio.h" #include "disp_7segment.h" #include "keypad.h" #include "stdlib.h" #include "string.h" #define DS_PIN 16 #define SH_ST_CLK_PIN 17 uint8_t* disp_expression_value(double value) { static uint8_t value_str[20]; sprintf(value_str, "%f", value); if (strchr(value_str, '.')) { for (int i = strlen(value_str)-1; i>=0; i--) { if (value_str[i] == '0' ) { value_str[i]=0; } else { if (value_str[i] == '.') value_str[i]=0; break; } } } if (value_str[0]==0) { value_str[0]='0'; } disp_7segment_write_str_digits(value_str); return value_str; } void demo(); int main() { stdio_init_all(); disp_7segment_init(pio0, 1, DS_PIN, SH_ST_CLK_PIN, 8, 10000000); keypad_default_init(); //demo(); uint8_t exp[256]={0}; uint8_t input[20]={0}; int pos_index=0; uint8_t key; uint8_t ks[2]={0,0}; double pre_result=0; uint8_t pre_op=0; disp_7segment_write_str_digits("0"); while (true) { key = get_new_keypad_value(); switch (key) { case '0': case '1': case '2' :case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': if (pos_index > 8) continue; input[pos_index++] = key; disp_7segment_write_str_digits(input); break; case 'b': --pos_index; if (pos_index < 0) { memset(input, 0, sizeof(input));pos_index = 0; } input[pos_index] = 0; disp_7segment_write_str_digits(input); break; case '+': case '-': case '*': // update expression ks[0] = key; strcat(exp, input); strcat(exp, ks); // calculate previous sub-expression if (pre_op) { if (pre_op == '+') { pre_result += atof(input); } if (pre_op == '-') { pre_result -= atof(input); } if (pre_op == '*') { pre_result *= atof(input); } disp_expression_value(pre_result); } else pre_result=atof(input); pre_op = key; //clean input buffer memset(input, 0, 20); pos_index = 0; break; case '=': //calculate total expression strcat(exp, input); if (pre_op) { if (pre_op == '+') { pre_result += atof(input); } if (pre_op == '-') { pre_result -= atof(input); } if (pre_op == '*') { pre_result *= atof(input); } } printf("%s=%f\n", exp, pre_result); disp_expression_value(pre_result); memset(input, 0, 20); pos_index = 0; pre_op = 0; pre_result = 0; memset(exp, 0, sizeof(exp)); break; } //disp_7segment_write_str_digits(input); tight_loop_contents(); } } void demo() { //sleep_ms(5000); uint8_t buf[20]; for (int i =0; i < 16;i++) { disp_7segment_write_digits(i); sleep_ms(500); } sleep_ms(500); disp_7segment_write_str_digits("a"); sleep_ms(500); disp_7segment_write_str_digits("ab"); sleep_ms(500); disp_7segment_write_str_digits("abc"); sleep_ms(500); disp_7segment_write_str_digits("abcd"); sleep_ms(500); disp_7segment_write_str_digits("abced"); sleep_ms(500); disp_7segment_write_str_digits("abcdef"); sleep_ms(1000); for (int i =-1; i >= -15;i--) { disp_7segment_write_digits(i); sleep_ms(500); } sleep_ms(500); disp_7segment_write_str_digits("1"); sleep_ms(500); disp_7segment_write_str_digits("12"); sleep_ms(500); disp_7segment_write_str_digits("123"); sleep_ms(500); disp_7segment_write_str_digits("1234"); sleep_ms(500); disp_7segment_write_str_digits("12345"); sleep_ms(500); disp_7segment_write_str_digits("123456"); sleep_ms(500); disp_7segment_write_str_digits("1234567"); sleep_ms(500); disp_7segment_write_str_digits("12345678"); sleep_ms(1000); for (double i=0.67; i < 100; i+=5.71) { sprintf(buf, "%f", i); disp_expression_value(i); sleep_ms(500); } sleep_ms(2000); }
disp_7segment library:
- disp_7segment.c
#include "pico/stdlib.h" #include "stdio.h" #include "stdlib.h" #include "disp_7segment.h" #include "disp_7segment.pio.h" #include "string.h" #include "pico/multicore.h" uint8_t led_digit[] = { 0b11111100, //0 0b01100000, //1 0b11011010, //2 0b11110010, //3 0b01100110, //4 0b10110110, //5 0b10111110, //6 0b11100000, //7 0b11111110, //8 0b11100110, //9 0b11101110, //A 0b00111110, //b 0b10011100, //C 0b01111010, //d 0b10011110, //E 0b10001110, //F 0b00000010, //- 0b00000000, // default no display }; static uint8_t total_digits; static PIO disp_7segment_pio; static uint disp_7segment_sm; static void display_digits(uint8_t *value_str, uint8_t value_len); void core1_display_digits(void) { uint32_t popup_data; uint8_t display_data_len=0; uint8_t display_data_str[20]={0}; while(1) { if (multicore_fifo_pop_timeout_us(100,&popup_data)) { strcpy(display_data_str, (uint8_t*)popup_data); display_data_len = strlen(display_data_str); } display_digits(display_data_str, display_data_len); } } void disp_7segment_init(PIO pio, uint sm, uint ds, uint sh_st_clk, uint8_t digits, uint freq) { total_digits = digits; uint offset = pio_add_program(pio, &disp_7segment_program); pio_sm_config c = disp_7segment_program_get_default_config(offset); pio_gpio_init(pio, ds); for (int i = 0; i < 2; i++) pio_gpio_init(pio, sh_st_clk+i); disp_7segment_pio = pio; disp_7segment_sm = sm; pio_sm_set_consecutive_pindirs(pio, sm, ds, 1, true); pio_sm_set_consecutive_pindirs(pio, sm, sh_st_clk, 2, true); sm_config_set_sideset_pin_base(&c, sh_st_clk); sm_config_set_out_pins(&c, ds,1); sm_config_set_out_shift(&c, true, true, 8); if (freq > 100000) freq = 100000; sm_config_set_clkdiv(&c, SYS_CLK_HZ/(freq*2)); //sm_config_set_clkdiv(&c, 62.5); pio_sm_init(pio, sm, offset, &c); pio_sm_exec(pio, sm, pio_encode_set(pio_y, digits%8 ? digits/8+1 : digits/8)); pio_sm_set_enabled(pio, sm, true); // launch core1 to continuely display ditits multicore_launch_core1(core1_display_digits); } static uint8_t char_to_index(uint8_t c) { //if (c >='0' && c <='9') return c-'0'; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c-'0'; break; case 'A': case 'a': return 10; break; case 'B': case 'b': return 11; break; case 'C': case 'c': return 12; break; case 'D': case 'd': return 13; break; case 'E': case 'e': return 14; break; case 'F': case 'f': return 15; break; case '-': return 16; break; default: return 17; break; } } static void display_digits(uint8_t *value_str, uint8_t value_len) { uint32_t digit_pos=0; uint8_t temp_total_digitals = (total_digits %8) ? (total_digits/8+1)*8 : total_digits; bool dp = false; uint8_t real_digits=total_digits; for (int i=0, pos=0; i < real_digits; i++) { if (i > value_len-1) pio_sm_put_blocking(disp_7segment_pio, disp_7segment_sm, 0); else { if (value_str[value_len-i-1] == '.') { dp=true; real_digits++; continue; } if (dp) { dp = false; pio_sm_put_blocking(disp_7segment_pio, disp_7segment_sm, led_digit[char_to_index(value_str[value_len-i-1])]+1); } else { pio_sm_put_blocking(disp_7segment_pio, disp_7segment_sm, led_digit[char_to_index(value_str[value_len-i-1])]); } } digit_pos=0; digit_pos = 1 << (temp_total_digitals-pos-1); pio_sm_put_blocking(disp_7segment_pio, disp_7segment_sm, digit_pos); pos++; //sleep_us(100); } } void disp_7segment_write_digits(int32_t value) { static uint8_t value_str[20]; sprintf(value_str, "%ld", value); multicore_fifo_push_blocking((uint32_t)value_str); } void disp_7segment_write_str_digits(uint8_t* value) { static uint8_t value_str[20]; if (value[0] == 0) strcpy(value_str,"0"); else strcpy(value_str, value); multicore_fifo_push_blocking((uint32_t)value_str); }
- disp_7segment.h
#ifndef __DISP_7_SEGMENT__ #define __DISP_7_SEGMENT__ #include "hardware/pio.h" extern uint8_t led_digit[]; void disp_7segment_init(PIO pio, uint sm, uint ds, uint sh_st_clk, uint8_t digits, uint freq); void disp_7segment_write_digits(int32_t value); void disp_7segment_write_str_digits(uint8_t* value); #endif
- disp_7segment.pio
.program disp_7segment .side_set 2 ;SH_CP(bit0), ST_CP(bit1) mov isr, y side 0b00 ;total digits preload to y ;isr used to store y temporarily .wrap_target digits_loop: set x,7 side 0b00 ;a~g and dp seg_loop: out pins, 1 side 0b00 jmp x--, seg_loop side 0b01 ;shift out one shift register data jmp y--, digits_loop side 0b00 ; mov y, isr side 0b10 ;restore y .wrap
- CMakeLists.txt
add_library(disp_7segment INTERFACE) pico_generate_pio_header(disp_7segment ${CMAKE_CURRENT_LIST_DIR}/disp_7segment.pio) target_sources(disp_7segment INTERFACE ${CMAKE_CURRENT_LIST_DIR}/disp_7segment.c ) target_include_directories(disp_7segment INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(disp_7segment INTERFACE hardware_pio pico_multicore )
pico_keypad libarary:
- keypad.c
#include "keypad.pio.h" #include "keypad.h" #include "hardware/clocks.h" #include "stdio.h" #include "pico/stdlib.h" static uint8_t key_value=0; static PIO keypad_pio=pio1; static uint keypad_sm=0; static const uint8_t keys[4][4]={ // define user key {'1','2','3','+'}, {'4','5','6','-'}, {'7','8','9','*'}, {'b','0','.','='} }; static void keypad_handle() { if (pio_interrupt_get(keypad_pio, KEYPAD_PIO_INT_NUM)) { key_value=0; pio_interrupt_clear(keypad_pio, KEYPAD_PIO_INT_NUM); uint32_t x, y; y=pio_sm_get_blocking(keypad_pio, keypad_sm); x=pio_sm_get_blocking(keypad_pio, keypad_sm); for(uint8_t i = 0 ; i < 4; i++){ if ((x >> i)==1) {x=i;break;} } for(uint8_t j = 0 ; j < 4; j++){ if ((y >> j)==1) {y=j;break;} } key_value = keys[x][y]; } } void keypad_pio_init(PIO pio, uint sm, uint set_base, uint in_base, uint freq) { uint offset=0; pio_sm_config c; offset = pio_add_program(pio, &keypad_program); c = keypad_program_get_default_config(offset); for (int i=0; i < 4; i++) pio_gpio_init(pio, in_base+i); for (int i=0; i < 4; i++) pio_gpio_init(pio, set_base+i); pio_sm_set_consecutive_pindirs(pio, sm, in_base, 4, false); pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true); sm_config_set_in_pins(&c, in_base); sm_config_set_set_pins(&c, set_base, 4); sm_config_set_in_shift(&c, false, false, 32); float div = clock_get_hz(clk_sys)/freq; sm_config_set_clkdiv(&c, div); uint pio_irq = pio_get_index(pio)? PIO1_IRQ_0:PIO0_IRQ_0; pio_interrupt_source_t pis_int_num; switch (KEYPAD_PIO_INT_NUM) { case 0: pis_int_num = pis_interrupt0; break; case 1: pis_int_num = pis_interrupt1; break; case 2: pis_int_num = pis_interrupt2; break; case 3: pis_int_num = pis_interrupt3; break; } pio_set_irq0_source_enabled(pio, pis_int_num, true); irq_add_shared_handler(pio_irq, keypad_handle, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); irq_set_enabled(pio_irq, true); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } uint8_t get_new_keypad_value() { uint8_t ret_vale = key_value; key_value=0; return ret_vale; } /* default value: pio: pio1 sm: 0 set pins:gpio 8-11 in pins: 12-15 */ void keypad_default_init() { keypad_pio_init(keypad_pio, keypad_sm, 8, 12, 100000); }
- keypad.h
#ifndef __KEYPAD_H #define __KEYPAD_H void keypad_default_init(); uint8_t get_new_keypad_value(); void keypad_pio_init(PIO pio, uint sm, uint set_base, uint in_base, uint freq); #endif
- keypad.pio
.define PUBLIC KEYPAD_PIO_INT_NUM 0 // only 0-3, PIO version 0 .program keypad .wrap_target set_row_1: set pins, 0b0001 // pull up line 1(pin 1) set x,0b0001 // store value in register X in pins,4 // get input value of line 5~8 (pin 5~8) mov y, isr // and store in Y jmp !y set_row_2 // If no button is pressed (0b0000), continue checking line 2 jmp rx_fifo [10] // if any button is pressed, jump to push x, y set_row_2: set pins, 0b0010 set x,0b0010 in pins,4 mov y, isr jmp !y set_row_3 jmp rx_fifo [10] // waiting for button bounce set_row_3: set pins, 0b0100 set x,0b0100 in pins,4 mov y, isr jmp !y set_row_4 jmp rx_fifo [10] set_row_4: set pins, 0b1000 set x,0b1000 in pins,4 mov y, isr jmp !y set_row_1 nop [10] rx_fifo: push // push y col value in x,4 push // and then x row value irq KEYPAD_PIO_INT_NUM // raising interrupt wait 0 pin 0 // check whether key is released wait 0 pin 1 wait 0 pin 2 wait 0 pin 3 .wrap
- CMakeLists.txt
add_library(pico_keypad INTERFACE) pico_generate_pio_header(pico_keypad ${CMAKE_CURRENT_LIST_DIR}/keypad.pio) target_sources(pico_keypad INTERFACE ${CMAKE_CURRENT_LIST_DIR}/keypad.c ) target_include_directories(pico_keypad INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(pico_keypad INTERFACE hardware_pio )
沒有留言:
張貼留言