|
| 1 | +/** |
| 2 | + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: BSD-3-Clause |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include <string.h> |
| 9 | +#include "pico/stdlib.h" |
| 10 | +#include "hardware/i2c.h" |
| 11 | +#include "pico/binary_info.h" |
| 12 | +#include <ctype.h> |
| 13 | + |
| 14 | +/* Example code to drive a 4 digit 14 segment LED backpack using a HT16K33 I2C |
| 15 | + driver chip |
| 16 | +
|
| 17 | + NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico |
| 18 | + GPIO (and therefore I2C) cannot be used at 5v. In development the particular |
| 19 | + device used allowed the PCB VCC to be 5v, but you can set the I2C voltage |
| 20 | + to 3.3v. |
| 21 | +
|
| 22 | + Connections on Raspberry Pi Pico board, other boards may vary. |
| 23 | +
|
| 24 | + GPIO 4 (pin 6)-> SDA on LED board |
| 25 | + GPIO 5 (pin 7)-> SCL on LED board |
| 26 | + GND (pin 38) -> GND on LED board |
| 27 | + 5v (pin 40) -> VCC on LED board |
| 28 | + 3.3v (pin 36) -> vi2c on LED board |
| 29 | +*/ |
| 30 | + |
| 31 | +// How many digits are on our display. |
| 32 | +#define NUM_DIGITS 4 |
| 33 | + |
| 34 | +// By default these display drivers are on bus address 0x70. Often there are |
| 35 | +// solder on options on the PCB of the backpack to set an address between |
| 36 | +// 0x70 and 0x77 to allow multiple devices to be used. |
| 37 | +const int I2C_addr = 0x70; |
| 38 | + |
| 39 | + |
| 40 | +// commands |
| 41 | + |
| 42 | + |
| 43 | +#define HT16K33_SYSTEM_STANDBY 0x20 |
| 44 | +#define HT16K33_SYSTEM_RUN 0x21 |
| 45 | + |
| 46 | +#define HT16K33_SET_ROW_INT 0xA0 |
| 47 | + |
| 48 | +#define HT16K33_BRIGHTNESS 0xE0 |
| 49 | + |
| 50 | +// Display on/off/blink |
| 51 | +#define HT16K33_DISPLAY_SETUP 0x80 |
| 52 | +// OR/clear these to display setup register |
| 53 | +#define HT16K33_DISPLAY_OFF 0x0 |
| 54 | +#define HT16K33_DISPLAY_ON 0x1 |
| 55 | +#define HT16K33_BLINK_2HZ 0x2 |
| 56 | +#define HT16K33_BLINK_1HZ 0x4 |
| 57 | +#define HT16K33_BLINK_0p5HZ 0x6 |
| 58 | + |
| 59 | +// Converts a character to the bit pattern needed to display the right segments. |
| 60 | +// These are pretty standard for 14segment LED's |
| 61 | +uint16_t char_to_pattern(char ch) { |
| 62 | +// Map, "A" to "Z" |
| 63 | +int16_t alpha[] = { |
| 64 | + 0xF7,0x128F,0x39,0x120F,0xF9,0xF1,0xBD,0xF6,0x1209,0x1E,0x2470,0x38,0x536,0x2136, |
| 65 | + 0x3F,0xF3,0x203F,0x20F3,0x18D,0x1201,0x3E,0xC30,0x2836,0x2D00,0x1500,0xC09 |
| 66 | + }; |
| 67 | + |
| 68 | +// Map, "0" to "9" |
| 69 | +int16_t num[] = { |
| 70 | + 0xC3F,0x406,0xDB,0x8F,0xE6,0xED,0xFD,0x1401,0xFF,0xE7 |
| 71 | + }; |
| 72 | + |
| 73 | + if (isalpha(ch)) |
| 74 | + return alpha[toupper(ch) - 'A']; |
| 75 | + |
| 76 | + if (isdigit(ch)) |
| 77 | + return num[ch - '0']; |
| 78 | + |
| 79 | + return 0; |
| 80 | +} |
| 81 | + |
| 82 | +/* Quick helper function for single byte transfers */ |
| 83 | +inline void i2c_write_byte(uint8_t val) { |
| 84 | +#ifdef i2c_default |
| 85 | + i2c_write_blocking(i2c_default, I2C_addr, &val, 1, false); |
| 86 | +#endif |
| 87 | +} |
| 88 | + |
| 89 | + |
| 90 | +void ht16k33_init() { |
| 91 | + i2c_write_byte(HT16K33_SYSTEM_RUN); |
| 92 | + i2c_write_byte(HT16K33_SET_ROW_INT); |
| 93 | + i2c_write_byte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON); |
| 94 | +} |
| 95 | + |
| 96 | +// Send a specific binary value to the specified digit |
| 97 | +inline void ht16k33_display_set(int position, uint16_t bin) { |
| 98 | + uint8_t buf[3]; |
| 99 | + buf[0] = position * 2; |
| 100 | + buf[1] = bin & 0xff; |
| 101 | + buf[2] = bin >> 8; |
| 102 | + i2c_write_blocking(i2c_default, I2C_addr, buf, count_of(buf), false); |
| 103 | +} |
| 104 | + |
| 105 | +inline void ht16k33_display_char(int position, char ch) { |
| 106 | + ht16k33_display_set(position, char_to_pattern(ch)); |
| 107 | +} |
| 108 | + |
| 109 | +void ht16k33_display_string(char *str) { |
| 110 | + int digit = 0; |
| 111 | + while (*str && digit <= NUM_DIGITS) { |
| 112 | + ht16k33_display_char(digit++, *str++); |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +void ht16k33_scroll_string(char *str, int interval_ms) { |
| 117 | + int l = strlen(str); |
| 118 | + |
| 119 | + if (l <= NUM_DIGITS) { |
| 120 | + ht16k33_display_string(str); |
| 121 | + } |
| 122 | + else { |
| 123 | + for (int i = 0; i < l - NUM_DIGITS + 1; i++) { |
| 124 | + ht16k33_display_string(&str[i]); |
| 125 | + sleep_ms(interval_ms); |
| 126 | + } |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +void ht16k33_set_brightness(int bright) { |
| 131 | + i2c_write_byte(HT16K33_BRIGHTNESS | (bright <= 15 ? bright : 15)); |
| 132 | +} |
| 133 | + |
| 134 | +void ht16k33_set_blink(int blink) { |
| 135 | + int s = 0; |
| 136 | + switch (blink) { |
| 137 | + default: break; |
| 138 | + case 1: s = HT16K33_BLINK_2HZ; break; |
| 139 | + case 2: s = HT16K33_BLINK_1HZ; break; |
| 140 | + case 3: s = HT16K33_BLINK_0p5HZ; break; |
| 141 | + } |
| 142 | + |
| 143 | + i2c_write_byte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON | s); |
| 144 | +} |
| 145 | + |
| 146 | +int main() { |
| 147 | + |
| 148 | + stdio_init_all(); |
| 149 | + |
| 150 | +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) |
| 151 | + #warning i2c/ht16k33_i2c example requires a board with I2C pins |
| 152 | +#else |
| 153 | + // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico) |
| 154 | + i2c_init(i2c_default, 100 * 1000); |
| 155 | + gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); |
| 156 | + gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); |
| 157 | + gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); |
| 158 | + gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); |
| 159 | + // Make the I2C pins available to picotool |
| 160 | + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); |
| 161 | + |
| 162 | + printf("Welcome to HT33k16!\n"); |
| 163 | + |
| 164 | + ht16k33_init(); |
| 165 | + |
| 166 | + ht16k33_display_set(0, 0); |
| 167 | + ht16k33_display_set(1, 0); |
| 168 | + ht16k33_display_set(2, 0); |
| 169 | + ht16k33_display_set(3, 0); |
| 170 | + |
| 171 | +again: |
| 172 | + |
| 173 | + ht16k33_scroll_string("Welcome to the Raspberry Pi Pico", 150); |
| 174 | + |
| 175 | + // Do a speeding up propeller effort using the inner segments |
| 176 | + int bits[] = {0x40, 0x0100, 0x0200, 0x0400, 0x80, 0x2000, 0x1000, 0x0800}; |
| 177 | + for (int j = 0;j < 10;j++) { |
| 178 | + for (int i = 0;i< count_of(bits); i++) { |
| 179 | + for (int digit = 0;digit <= NUM_DIGITS; digit++) { |
| 180 | + ht16k33_display_set(digit, bits[i]); |
| 181 | + } |
| 182 | + sleep_ms(155 - j*15); |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + char *strs[] = { |
| 187 | + "Help", "I am", "in a", "Pico", "and ", "Cant", "get ", "out " |
| 188 | + }; |
| 189 | + |
| 190 | + for (int i = 0; i < count_of(strs); i++) { |
| 191 | + ht16k33_display_string(strs[i]); |
| 192 | + sleep_ms(500); |
| 193 | + } |
| 194 | + |
| 195 | + sleep_ms(1000); |
| 196 | + |
| 197 | + // Test brightness and blinking |
| 198 | + |
| 199 | + // Set all segments on all digits on |
| 200 | + ht16k33_display_set(0, 0xffff); |
| 201 | + ht16k33_display_set(1, 0xffff); |
| 202 | + ht16k33_display_set(2, 0xffff); |
| 203 | + ht16k33_display_set(3, 0xffff); |
| 204 | + |
| 205 | + // Fade up and down |
| 206 | + for (int j=0;j<5;j++) { |
| 207 | + for (int i = 0; i < 15; i++) { |
| 208 | + ht16k33_set_brightness(i); |
| 209 | + sleep_ms(30); |
| 210 | + } |
| 211 | + |
| 212 | + for (int i = 14; i >=0; i--) { |
| 213 | + ht16k33_set_brightness(i); |
| 214 | + sleep_ms(30); |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + ht16k33_set_brightness(15); |
| 219 | + |
| 220 | + ht16k33_set_blink(1); // 0 for no blink, 1 for 2Hz, 2 for 1Hz, 3 for 0.5Hz |
| 221 | + sleep_ms(5000); |
| 222 | + ht16k33_set_blink(0); |
| 223 | + |
| 224 | + goto again; |
| 225 | + |
| 226 | + return 0; |
| 227 | +#endif |
| 228 | +} |
0 commit comments