From 82eae4c8ea03fe97cd2a62491041fdfb3d7fb6b1 Mon Sep 17 00:00:00 2001 From: James Hughes Date: Fri, 25 Nov 2022 17:32:13 +0000 Subject: [PATCH] Added a 14 segment LED example, driven by the HT16K33 (#219) No fritzing parts available for the device so no diagrams --- README.md | 1 + i2c/CMakeLists.txt | 1 + i2c/ht16k33_i2c/CMakeLists.txt | 12 ++ i2c/ht16k33_i2c/README.adoc | 39 ++++++ i2c/ht16k33_i2c/ht16k33_i2c.c | 228 +++++++++++++++++++++++++++++++++ 5 files changed, 281 insertions(+) create mode 100644 i2c/ht16k33_i2c/CMakeLists.txt create mode 100644 i2c/ht16k33_i2c/README.adoc create mode 100644 i2c/ht16k33_i2c/ht16k33_i2c.c diff --git a/README.md b/README.md index b200f5a..459b5f9 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ App|Description [ssd1306_i2c](i2c/ssd1306_i2c) | Convert and display a bitmap on a 128x32 or 128x64 SSD1306-driven OLED display [pa1010d_i2c](i2c/pa1010d_i2c) | Read GPS location data, parse and display data via I2C. [pcf8523_i2c](i2c/pcf8523_i2c) | Read time and date values from a real time clock. Set current time and alarms on it. +[ht16k33_i2c](i2c/ht16k33_i2c) | Drive a 4 digit 14 segment LED with an HT16K33. ### Interpolator diff --git a/i2c/CMakeLists.txt b/i2c/CMakeLists.txt index 52f555f..977a3d2 100644 --- a/i2c/CMakeLists.txt +++ b/i2c/CMakeLists.txt @@ -10,4 +10,5 @@ if (NOT PICO_NO_HARDWARE) add_subdirectory(ssd1306_i2c) add_subdirectory(pa1010d_i2c) add_subdirectory(pcf8523_i2c) + add_subdirectory(ht16k33_i2c) endif () diff --git a/i2c/ht16k33_i2c/CMakeLists.txt b/i2c/ht16k33_i2c/CMakeLists.txt new file mode 100644 index 0000000..b9b1110 --- /dev/null +++ b/i2c/ht16k33_i2c/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(ht16k33_i2c + ht16k33_i2c.c + ) + +# pull in common dependencies and additional i2c hardware support +target_link_libraries(ht16k33_i2c pico_stdlib hardware_i2c) + +# create map/bin/hex file etc. +pico_add_extra_outputs(ht16k33_i2c) + +# add url via pico_set_program_url +example_auto_set_url(ht16k33_i2c) diff --git a/i2c/ht16k33_i2c/README.adoc b/i2c/ht16k33_i2c/README.adoc new file mode 100644 index 0000000..9a10b97 --- /dev/null +++ b/i2c/ht16k33_i2c/README.adoc @@ -0,0 +1,39 @@ += Attaching a 4 digit 14 segment LED driven by the HT15k33 via I2C + +This example code shows how to interface the Raspberry Pi Pico to one HT16K33 driven 4 digit 14 segment LED. The device used for development +uses VCC at 5v, but also allows the I2C voltage to be selected by connecting the vi2c pin to the appropriate voltage, in the Pico case 3v3 as the +GPIO pins are not 5v tolerant. + +NOTE: Other manufacturers make very similar devices with very similar names (e.g. Vinka VK16K33)). These may or may not work! + +== Wiring information + +Wiring up the device requires 5 jumpers, to connect VCC (3.3v), GND, vi2c (3v3), SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3.3V pin. + +WARNING: Some displays of this type are 5v with no option to select 3v3 for the I2c. If you wish to use a 5v display you will need to use level shifters on the SDA and SCL lines to convert from the 3.3V used by the RP2040. + +Connections on Raspberry Pi Pico board, other boards may vary. + + GPIO 4 (pin 6)-> SDA on LED board + GPIO 5 (pin 7)-> SCL on LED board + GND (pin 38) -> GND on LED board + 5v (pin 40) -> VCC on LED board + 3.3v (pin 36) -> vi2c on LED board + +== List of Files + +CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree. +ht15k33_i2c.c:: The example code. + +== Bill of Materials + +.A list of materials required for the example +[[ht15k33_i2c-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/ +| ht16k33 based LCD panel | 1 | generic part +| M/M Jumper wires | 5 | generic part +|=== diff --git a/i2c/ht16k33_i2c/ht16k33_i2c.c b/i2c/ht16k33_i2c/ht16k33_i2c.c new file mode 100644 index 0000000..e2ecaf3 --- /dev/null +++ b/i2c/ht16k33_i2c/ht16k33_i2c.c @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "pico/binary_info.h" +#include + +/* Example code to drive a 4 digit 14 segment LED backpack using a HT16K33 I2C + driver chip + + NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico + GPIO (and therefore I2C) cannot be used at 5v. In development the particular + device used allowed the PCB VCC to be 5v, but you can set the I2C voltage + to 3.3v. + + Connections on Raspberry Pi Pico board, other boards may vary. + + GPIO 4 (pin 6)-> SDA on LED board + GPIO 5 (pin 7)-> SCL on LED board + GND (pin 38) -> GND on LED board + 5v (pin 40) -> VCC on LED board + 3.3v (pin 36) -> vi2c on LED board +*/ + +// How many digits are on our display. +#define NUM_DIGITS 4 + +// By default these display drivers are on bus address 0x70. Often there are +// solder on options on the PCB of the backpack to set an address between +// 0x70 and 0x77 to allow multiple devices to be used. +const int I2C_addr = 0x70; + + +// commands + + +#define HT16K33_SYSTEM_STANDBY 0x20 +#define HT16K33_SYSTEM_RUN 0x21 + +#define HT16K33_SET_ROW_INT 0xA0 + +#define HT16K33_BRIGHTNESS 0xE0 + +// Display on/off/blink +#define HT16K33_DISPLAY_SETUP 0x80 +// OR/clear these to display setup register +#define HT16K33_DISPLAY_OFF 0x0 +#define HT16K33_DISPLAY_ON 0x1 +#define HT16K33_BLINK_2HZ 0x2 +#define HT16K33_BLINK_1HZ 0x4 +#define HT16K33_BLINK_0p5HZ 0x6 + +// Converts a character to the bit pattern needed to display the right segments. +// These are pretty standard for 14segment LED's +uint16_t char_to_pattern(char ch) { +// Map, "A" to "Z" +int16_t alpha[] = { + 0xF7,0x128F,0x39,0x120F,0xF9,0xF1,0xBD,0xF6,0x1209,0x1E,0x2470,0x38,0x536,0x2136, + 0x3F,0xF3,0x203F,0x20F3,0x18D,0x1201,0x3E,0xC30,0x2836,0x2D00,0x1500,0xC09 + }; + +// Map, "0" to "9" +int16_t num[] = { + 0xC3F,0x406,0xDB,0x8F,0xE6,0xED,0xFD,0x1401,0xFF,0xE7 + }; + + if (isalpha(ch)) + return alpha[toupper(ch) - 'A']; + + if (isdigit(ch)) + return num[ch - '0']; + + return 0; +} + +/* Quick helper function for single byte transfers */ +inline void i2c_write_byte(uint8_t val) { +#ifdef i2c_default + i2c_write_blocking(i2c_default, I2C_addr, &val, 1, false); +#endif +} + + +void ht16k33_init() { + i2c_write_byte(HT16K33_SYSTEM_RUN); + i2c_write_byte(HT16K33_SET_ROW_INT); + i2c_write_byte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON); +} + +// Send a specific binary value to the specified digit +inline void ht16k33_display_set(int position, uint16_t bin) { + uint8_t buf[3]; + buf[0] = position * 2; + buf[1] = bin & 0xff; + buf[2] = bin >> 8; + i2c_write_blocking(i2c_default, I2C_addr, buf, count_of(buf), false); +} + +inline void ht16k33_display_char(int position, char ch) { + ht16k33_display_set(position, char_to_pattern(ch)); +} + +void ht16k33_display_string(char *str) { + int digit = 0; + while (*str && digit <= NUM_DIGITS) { + ht16k33_display_char(digit++, *str++); + } +} + +void ht16k33_scroll_string(char *str, int interval_ms) { + int l = strlen(str); + + if (l <= NUM_DIGITS) { + ht16k33_display_string(str); + } + else { + for (int i = 0; i < l - NUM_DIGITS + 1; i++) { + ht16k33_display_string(&str[i]); + sleep_ms(interval_ms); + } + } +} + +void ht16k33_set_brightness(int bright) { + i2c_write_byte(HT16K33_BRIGHTNESS | (bright <= 15 ? bright : 15)); +} + +void ht16k33_set_blink(int blink) { + int s = 0; + switch (blink) { + default: break; + case 1: s = HT16K33_BLINK_2HZ; break; + case 2: s = HT16K33_BLINK_1HZ; break; + case 3: s = HT16K33_BLINK_0p5HZ; break; + } + + i2c_write_byte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON | s); +} + +int main() { + + stdio_init_all(); + +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) + #warning i2c/ht16k33_i2c example requires a board with I2C pins +#else + // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico) + i2c_init(i2c_default, 100 * 1000); + gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); + gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); + // Make the I2C pins available to picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + + printf("Welcome to HT33k16!\n"); + + ht16k33_init(); + + ht16k33_display_set(0, 0); + ht16k33_display_set(1, 0); + ht16k33_display_set(2, 0); + ht16k33_display_set(3, 0); + +again: + + ht16k33_scroll_string("Welcome to the Raspberry Pi Pico", 150); + + // Do a speeding up propeller effort using the inner segments + int bits[] = {0x40, 0x0100, 0x0200, 0x0400, 0x80, 0x2000, 0x1000, 0x0800}; + for (int j = 0;j < 10;j++) { + for (int i = 0;i< count_of(bits); i++) { + for (int digit = 0;digit <= NUM_DIGITS; digit++) { + ht16k33_display_set(digit, bits[i]); + } + sleep_ms(155 - j*15); + } + } + + char *strs[] = { + "Help", "I am", "in a", "Pico", "and ", "Cant", "get ", "out " + }; + + for (int i = 0; i < count_of(strs); i++) { + ht16k33_display_string(strs[i]); + sleep_ms(500); + } + + sleep_ms(1000); + + // Test brightness and blinking + + // Set all segments on all digits on + ht16k33_display_set(0, 0xffff); + ht16k33_display_set(1, 0xffff); + ht16k33_display_set(2, 0xffff); + ht16k33_display_set(3, 0xffff); + + // Fade up and down + for (int j=0;j<5;j++) { + for (int i = 0; i < 15; i++) { + ht16k33_set_brightness(i); + sleep_ms(30); + } + + for (int i = 14; i >=0; i--) { + ht16k33_set_brightness(i); + sleep_ms(30); + } + } + + ht16k33_set_brightness(15); + + ht16k33_set_blink(1); // 0 for no blink, 1 for 2Hz, 2 for 1Hz, 3 for 0.5Hz + sleep_ms(5000); + ht16k33_set_blink(0); + + goto again; + + return 0; +#endif +}