diff --git a/examples/max7219_7seg/Makefile b/examples/max7219_7seg/Makefile new file mode 100644 index 0000000..fc55387 --- /dev/null +++ b/examples/max7219_7seg/Makefile @@ -0,0 +1,3 @@ +PROGRAM = max7219_7seg +EXTRA_COMPONENTS = extras/max7219 +include ../../common.mk diff --git a/examples/max7219_7seg/main.c b/examples/max7219_7seg/main.c new file mode 100644 index 0000000..d90d01b --- /dev/null +++ b/examples/max7219_7seg/main.c @@ -0,0 +1,52 @@ +/* + * Example of using MAX7219 driver with 7 segment displays + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS_PIN 5 +#define DELAY 2000 + +static max7219_display_t disp = { + .cs_pin = CS_PIN, + .digits = 8, + .cascade_size = 1, + .mirrored = true +}; + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + max7219_init(&disp); + //max7219_set_decode_mode(&disp, true); + + char buf[9]; + while (true) + { + max7219_clear(&disp); + max7219_draw_text(&disp, 0, "7219LEDS"); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + + max7219_clear(&disp); + sprintf(buf, "%2.4f A", 34.6782); + max7219_draw_text(&disp, 0, buf); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + + max7219_clear(&disp); + sprintf(buf, "%08x", hwrand()); + max7219_draw_text(&disp, 0, buf); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + } +} diff --git a/extras/max7219/component.mk b/extras/max7219/component.mk new file mode 100644 index 0000000..29dcf2e --- /dev/null +++ b/extras/max7219/component.mk @@ -0,0 +1,7 @@ +# include it as 'max7219/max7219.h' +INC_DIRS += $(max7219_ROOT).. + +# args for passing into compile rule generation +max7219_SRC_DIR = $(max7219_ROOT) + +$(eval $(call component_compile_rules,max7219)) diff --git a/extras/max7219/max7219.c b/extras/max7219/max7219.c new file mode 100644 index 0000000..34ad069 --- /dev/null +++ b/extras/max7219/max7219.c @@ -0,0 +1,189 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include "max7219.h" +#include +#include +#include + +#include "max7219_priv.h" + +#define SPI_BUS 1 + +//#define MAX7219_DEBUG + +#ifdef MAX7219_DEBUG +#include +#define debug(fmt, ...) printf("%s: " fmt "\n", "MAX7219", ## __VA_ARGS__) +#else +#define debug(fmt, ...) +#endif + +#define ALL_CHIPS 0xff +#define ALL_DIGITS 8 + +#define REG_DIGIT_0 (1 << 8) +#define REG_DECODE_MODE (9 << 8) +#define REG_INTENSITY (10 << 8) +#define REG_SCAN_LIMIT (11 << 8) +#define REG_SHUTDOWN (12 << 8) +#define REG_DISPLAY_TEST (15 << 8) + +#define VAL_CLEAR_BCD 0x0f +#define VAL_CLEAR_NORMAL 0x00 + +static const spi_settings_t bus_settings = { + .mode = SPI_MODE0, + .freq_divider = SPI_FREQ_DIV_10M, + .msb = true, + .minimal_pins = true, + .endianness = SPI_BIG_ENDIAN +}; + +static void send(const max7219_display_t *disp, uint8_t chip, uint16_t value) +{ + uint16_t buf[MAX7219_MAX_CASCADE_SIZE] = { 0 }; + if (chip == ALL_CHIPS) + { + for (uint8_t i = 0; i < disp->cascade_size; i++) + buf[i] = value; + } + else buf[chip] = value; + + spi_settings_t old_settings; + spi_get_settings(SPI_BUS, &old_settings); + spi_set_settings(SPI_BUS, &bus_settings); + gpio_write(disp->cs_pin, false); + + spi_transfer(SPI_BUS, buf, NULL, disp->cascade_size, SPI_16BIT); + + gpio_write(disp->cs_pin, true); + spi_set_settings(SPI_BUS, &old_settings); +} + +bool max7219_init(max7219_display_t *disp) +{ + if (!disp->cascade_size || disp->cascade_size > MAX7219_MAX_CASCADE_SIZE) + { + debug("Invalid cascade size %d", disp->cascade_size); + return false; + } + + uint8_t max_digits = disp->cascade_size * ALL_DIGITS; + if (!disp->digits || disp->digits > max_digits) + { + debug("Invalid digits count %d, max %d", disp->cascade_size, max_digits); + return false; + } + + gpio_enable(disp->cs_pin, GPIO_OUTPUT); + gpio_write(disp->cs_pin, true); + + // Shutdown all chips + max7219_set_shutdown_mode(disp, true); + // Disable test + send(disp, ALL_CHIPS, REG_DISPLAY_TEST); + // Set max scan limit + send(disp, ALL_CHIPS, REG_SCAN_LIMIT | (ALL_DIGITS - 1)); + // Set normal decode mode & clear display + max7219_set_decode_mode(disp, false); + // Set minimal brightness + max7219_set_brightness(disp, 0); + // Wake up + max7219_set_shutdown_mode(disp, false); + + return true; +} + +void max7219_set_decode_mode(max7219_display_t *disp, bool bcd) +{ + disp->bcd = bcd; + send(disp, ALL_CHIPS, REG_DECODE_MODE | (bcd ? 0xff : 0)); + max7219_clear(disp); +} + +void max7219_set_brightness(const max7219_display_t *disp, uint8_t value) +{ + send(disp, ALL_CHIPS, REG_INTENSITY | (value > MAX7219_MAX_BRIGHTNESS ? MAX7219_MAX_BRIGHTNESS : value)); +} + +void max7219_set_shutdown_mode(const max7219_display_t *disp, bool shutdown) +{ + send(disp, ALL_CHIPS, REG_SHUTDOWN | !shutdown); +} + +bool max7219_set_digit(const max7219_display_t *disp, uint8_t digit, uint8_t val) +{ + if (digit >= disp->digits) + { + debug("Invalid digit: %d", digit); + return false; + } + + if (disp->mirrored) + digit = disp->digits - digit - 1; + + uint8_t c = digit / ALL_DIGITS; + uint8_t d = digit % ALL_DIGITS; + + send(disp, c, (REG_DIGIT_0 + ((uint16_t)d << 8)) | val); + + return true; +} + +void max7219_clear(const max7219_display_t *disp) +{ + uint8_t val = disp->bcd ? VAL_CLEAR_BCD : VAL_CLEAR_NORMAL; + for (uint8_t i = 0; i < ALL_DIGITS; i++) + send(disp, ALL_CHIPS, (REG_DIGIT_0 + ((uint16_t)i << 8)) | val); +} + +inline static uint8_t get_char(const max7219_display_t *disp, char c) +{ + if (disp->bcd) + { + if (c >= '0' && c <= '9') + return c - '0'; + switch (c) + { + case '-': + return 0x0a; + case 'E': + case 'e': + return 0x0b; + case 'H': + case 'h': + return 0x0c; + case 'L': + case 'l': + return 0x0d; + case 'P': + case 'p': + return 0x0e; + } + return VAL_CLEAR_BCD; + } + + return font_7seg[(c - 0x20) & 0x7f]; +} + +void max7219_draw_text(const max7219_display_t *disp, uint8_t pos, const char *s) +{ + while (s && pos < disp->digits) + { + uint8_t c = get_char(disp, *s); + if (*(s + 1) == '.') + { + c |= 0x80; + s++; + } + max7219_set_digit(disp, pos, c); + pos++; + s++; + } +} diff --git a/extras/max7219/max7219.h b/extras/max7219/max7219.h new file mode 100644 index 0000000..127d6c0 --- /dev/null +++ b/extras/max7219/max7219.h @@ -0,0 +1,96 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_MAX7219_H_ +#define EXTRAS_MAX7219_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MAX7219_MAX_CASCADE_SIZE 8 +#define MAX7219_MAX_BRIGHTNESS 31 + +/** + * Display descriptor + */ +typedef struct +{ + uint8_t cs_pin; //!< GPIO connected to CS/LOAD pin + + uint8_t digits; //!< Accessible digits in 7seg. Up to cascade_size * 8 + + uint8_t cascade_size; //!< Up to 8 MAX721xx cascaded + bool mirrored; //!< true for horizontally mirrored displays + + bool bcd; +} max7219_display_t; + +/** + * Initialize display: switch it to normal operation from shutdown mode, + * set scan limit to the max and clear + * @param disp Pointer to display descriptor + * @return false if error occured + */ +bool max7219_init(max7219_display_t *disp); + +/** + * Set decode mode and clear display + * @param disp Pointer to display descriptor + * @param bcd true to set BCD decode mode, false to normal + */ +void max7219_set_decode_mode(max7219_display_t *disp, bool bcd); + +/** + * Set display brightness + * @param disp Pointer to display descriptor + * @param value Brightness value, 0..MAX7219_MAX_BRIGHTNESS + */ +void max7219_set_brightness(const max7219_display_t *disp, uint8_t value); + +/** + * Shutdown display or set it to normal mode + * @param disp Pointer to display descriptor + * @param shutdown Shutdown display if true + */ +void max7219_set_shutdown_mode(const max7219_display_t *disp, bool shutdown); + +/** + * Write data to display digit + * @param disp Pointer to display descriptor + * @param digit Digit index, 0..disp->digits - 1 + * @param val Data + * @return false if error occured + */ +bool max7219_set_digit(const max7219_display_t *disp, uint8_t digit, uint8_t val); + +/** + * Clear display + * @param disp Pointer to display descriptor + */ +void max7219_clear(const max7219_display_t *disp); + +/** + * Draw text. + * Currently only 7-segment displays supported + * @param disp Pointer to display descriptor + * @param pos Start digit + * @param s Text + */ +void max7219_draw_text(const max7219_display_t *disp, uint8_t pos, const char *s); + +#ifdef __cplusplus +extern "C" +} +#endif + +#endif /* EXTRAS_MAX7219_H_ */ diff --git a/extras/max7219/max7219_priv.h b/extras/max7219/max7219_priv.h new file mode 100644 index 0000000..f7c03cd --- /dev/null +++ b/extras/max7219/max7219_priv.h @@ -0,0 +1,36 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_MAX7219_PRIV_H_ +#define EXTRAS_MAX7219_PRIV_H_ + +static const uint8_t font_7seg[] = { + /* ' ' ! " # $ % & ' ( ) */ + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4e, 0x78, + /* * + , - . / 0 1 2 3 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x30, 0x6d, 0x79, + /* 4 5 6 7 8 9 : ; < = */ + 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b, 0x00, 0x00, 0x0d, 0x09, + /* > ? @ A B C D E F G */ + 0x19, 0x65, 0x00, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47, 0x5e, + /* H I J K L M N O P Q */ + 0x37, 0x06, 0x38, 0x57, 0x0e, 0x76, 0x15, 0x1d, 0x67, 0x73, + /* R S T U V W X Y Z [ */ + 0x05, 0x5b, 0x0f, 0x1c, 0x3e, 0x2a, 0x49, 0x3b, 0x6d, 0x4e, + /* \ ] ^ _ ` a b c d e */ + 0x00, 0x78, 0x00, 0x08, 0x02, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, + /* f g h i j k l m n o */ + 0x47, 0x5e, 0x37, 0x06, 0x38, 0x57, 0x0e, 0x76, 0x15, 0x1d, + /* p q r s t u v w x y */ + 0x67, 0x73, 0x05, 0x5b, 0x0f, 0x1c, 0x3e, 0x2a, 0x49, 0x3b, + /* z { | } ~ */ + 0x6d, 0x4e, 0x06, 0x78, 0x00 +}; + + +#endif /* EXTRAS_MAX7219_PRIV_H_ */