| /**************************************************************************** |
| * drivers/lcd/ht16k33_14seg.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /* Alphanumeric display driver for HOLTEK HT16K33 (and VINKA VK16K33 clone) |
| * This driver is specific for a 0.54" 14-segment LED HT16K33 Backpack |
| * module with 4 14-segment digits (2 Kingbright 5241AS display). |
| * Note: the model I'm testing uses the VK16K33. |
| * |
| * This is how the displays are connected: |
| * Left Display: Digit 1: Catode connected to COM3 |
| * Left Display: Digit 2: Catode connected to COM2 |
| * Right Display: Digit 1: Catode connected to COM1 |
| * Right Display: Digit 2: Catode connected to COM0 |
| * |
| * 14-Segment | LED Controller |
| * ------------------------------- |
| * 8 - DP | ROW14 - 11 |
| * 13 - p | ROW6 - 19 |
| * 2 - n | ROW11 - 14 |
| * 4 - m | ROW12 - 13 |
| * 5 - l | ROW13 - 12 |
| * 6 - k | ROW7 - 18 |
| * 14 - j | ROW10 - 15 |
| * 15 - h | ROW9 - 16 |
| * 17 - g | ROW8 - 17 |
| * 18 - f | ROW5 - 20 |
| * 1 - e | ROW4 - 21 |
| * 7 - d | ROW3 - 22 |
| * 9 - c | ROW2 - 23 |
| * 10 - b | ROW1 - 24 |
| * 12 - a | ROW0 - 25 |
| */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <string.h> |
| |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/signal.h> |
| #include <nuttx/ascii.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/lcd/slcd_codec.h> |
| #include <nuttx/lcd/slcd_ioctl.h> |
| #include <nuttx/i2c/i2c_master.h> |
| #include <nuttx/lcd/ht16k33.h> |
| |
| #ifndef CONFIG_LIBC_SLCDCODEC |
| # error please also select Library Routines, Segment LCD CODEC |
| #endif |
| |
| #if defined(CONFIG_I2C) && defined(CONFIG_LCD_HT16K33) |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* I2C frequency */ |
| |
| #ifndef CONFIG_HT16K33_I2C_FREQ |
| # define CONFIG_HT16K33_I2C_FREQ 400000 |
| #endif |
| |
| #ifndef CONFIG_LCD_HT16K33_NUMBER_MODULES |
| # define CONFIG_LCD_HT16K33_NUMBER_MODULES 1 |
| #endif |
| |
| #define HT16K33_MAX_ROW 1 |
| #define HT16K33_MAX_COL 4 * CONFIG_LCD_HT16K33_NUMBER_MODULES |
| |
| /* Device naming ************************************************************/ |
| |
| #define DEVNAME_FMT "/dev/slcd%d" |
| #define DEVNAME_FMTLEN (9 + 3 + 1) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct ht16k33_dev_s |
| { |
| FAR struct i2c_master_s *i2c; /* I2C interface */ |
| uint8_t row; /* Current row position to write on display */ |
| uint8_t col; /* Current col position to write on display */ |
| uint8_t buffer[HT16K33_MAX_COL]; |
| bool pendscroll; |
| mutex_t lock; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static inline void ht16k33_write_cmd(FAR struct ht16k33_dev_s *priv, |
| int dev_id, uint8_t cmd); |
| |
| static inline void ht16k33_write_data(FAR struct ht16k33_dev_s *priv, |
| int dev_id, uint8_t cmd, |
| uint8_t *values, int nbytes); |
| |
| static inline void ht16k33_setcontrast(FAR struct ht16k33_dev_s *priv, |
| int dev_id, int8_t contrast); |
| |
| static void lcd_scroll_up(FAR struct ht16k33_dev_s *priv); |
| |
| static void ht16k33_clear_display(FAR struct ht16k33_dev_s *priv); |
| |
| /* Character driver methods */ |
| |
| static ssize_t ht16k33_read(FAR struct file *filep, FAR char *buffer, |
| size_t buflen); |
| static ssize_t ht16k33_write(FAR struct file *filep, FAR const char *buffer, |
| size_t buflen); |
| static off_t ht16k33_seek(FAR struct file *filep, off_t offset, |
| int whence); |
| static int ht16k33_ioctl(FAR struct file *filep, int cmd, |
| unsigned long arg); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const struct file_operations g_ht16k33fops = |
| { |
| NULL, /* open */ |
| NULL, /* close */ |
| ht16k33_read, /* read */ |
| ht16k33_write, /* write */ |
| ht16k33_seek, /* seek */ |
| ht16k33_ioctl, /* ioctl */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ht16k33_write_cmd |
| * |
| * Description: |
| * Write an Instruction command to HT16K33 |
| * |
| ****************************************************************************/ |
| |
| static inline void ht16k33_write_cmd(FAR struct ht16k33_dev_s *priv, |
| int dev_id, uint8_t cmd) |
| { |
| struct i2c_msg_s msg; |
| uint8_t data[1]; |
| int ret; |
| |
| /* Prepare data to send */ |
| |
| data[0] = cmd; |
| |
| /* Setup the HT16K33 Command */ |
| |
| msg.frequency = CONFIG_HT16K33_I2C_FREQ; /* I2C frequency */ |
| msg.addr = HT16K33_I2C_ADDR + dev_id; /* 7-bit address */ |
| msg.flags = 0; /* Write transaction */ |
| msg.buffer = data; /* Transfer from this address */ |
| msg.length = 1; /* Send one byte */ |
| |
| /* Perform the transfer */ |
| |
| ret = I2C_TRANSFER(priv->i2c, &msg, 1); |
| if (ret < 0) |
| { |
| lcderr("ERROR: I2C_TRANSFER failed: %d\n", ret); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_write_data |
| * |
| * Description: |
| * Write a Data command to HT16K33 |
| * |
| ****************************************************************************/ |
| |
| static inline void ht16k33_write_data(FAR struct ht16k33_dev_s *priv, |
| int dev_id, uint8_t cmd, |
| uint8_t *values, int nbytes) |
| { |
| struct i2c_msg_s msg; |
| uint8_t data[16]; |
| int ret; |
| int i; |
| |
| /* Prepare data to send */ |
| |
| data[0] = cmd; |
| |
| for (i = 0; i < nbytes; i++) |
| { |
| data[i + 1] = values[i]; |
| } |
| |
| /* Setup the message to write data to HT16K33 */ |
| |
| msg.frequency = CONFIG_HT16K33_I2C_FREQ; /* I2C frequency */ |
| msg.addr = HT16K33_I2C_ADDR + dev_id; /* 7-bit address */ |
| msg.flags = 0; /* Write transaction */ |
| msg.buffer = data; /* Transfer from here */ |
| msg.length = nbytes + 1; /* Send cmd + nbytes */ |
| |
| /* Perform the transfer */ |
| |
| ret = I2C_TRANSFER(priv->i2c, &msg, 1); |
| if (ret < 0) |
| { |
| lcderr("ERROR: I2C_TRANSFER failed: %d\n", ret); |
| } |
| } |
| |
| static inline void ht16k33_setcontrast(FAR struct ht16k33_dev_s *priv, |
| int dev_id, int8_t contrast) |
| { |
| int i; |
| |
| if (contrast < HT16K33_CONTRAST_MIN) |
| { |
| contrast = HT16K33_CONTRAST_MIN; |
| } |
| else if (contrast > HT16K33_CONTRAST_MAX) |
| { |
| contrast = HT16K33_CONTRAST_MAX; |
| } |
| |
| for (i = 0; i < CONFIG_LCD_HT16K33_NUMBER_MODULES; i++) |
| { |
| ht16k33_write_cmd(priv, i, HT16K33_DIMMING_SET | (contrast & 0x0f)); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_getdata |
| * |
| * Description: |
| * Simulate reading data from LCD, we are reading from internal buffer |
| * |
| ****************************************************************************/ |
| |
| static inline uint8_t lcd_getdata(FAR struct ht16k33_dev_s *priv) |
| { |
| uint8_t data; |
| data = priv->buffer[priv->row * priv->col]; |
| return data; |
| } |
| |
| /**************************************************************************** |
| * Name: rc2addr |
| * |
| * Description: |
| * This converts a row/column pair to a screen memory address. |
| * |
| ****************************************************************************/ |
| |
| static inline uint8_t rc2addr(FAR struct ht16k33_dev_s *priv) |
| { |
| /* Each module has 4 digits they correspond to these columns: |
| * |
| * col0: 0x00 - 0x01, col1: 0x02 - 0x03, |
| * col2: 0x04 - 0x05, col3: 0x06 - 0x07 |
| */ |
| |
| return (priv->col % 4) * 0x02; |
| } |
| |
| /**************************************************************************** |
| * Name: addr2rc |
| * |
| * Description: |
| * This converts a screen memory address to a row/column pair. |
| * |
| ****************************************************************************/ |
| |
| static inline void addr2rc(FAR struct ht16k33_dev_s *priv, uint8_t addr, |
| FAR uint8_t *row, FAR uint8_t *col) |
| { |
| *row = 0; |
| *col = addr / 2; |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_set_curpos |
| * |
| * Description: |
| * This sets the cursor position based on row, column addressing. |
| * |
| * Input Parameters: |
| * priv - device instance |
| * |
| ****************************************************************************/ |
| |
| static void lcd_set_curpos(FAR struct ht16k33_dev_s *priv) |
| { |
| uint8_t addr; |
| int dev_id; |
| |
| addr = rc2addr(priv); |
| dev_id = priv->col / 4; |
| |
| /* Define the memory address position */ |
| |
| ht16k33_write_cmd(priv, dev_id, HT16K33_DISP_DATA_ADDR | addr); |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_putdata |
| * |
| * Description: |
| * Write a byte to the LCD and update column/row position |
| * |
| ****************************************************************************/ |
| |
| static inline void lcd_putdata(FAR struct ht16k33_dev_s *priv, uint8_t data) |
| { |
| uint8_t segment[2]; |
| uint8_t addr; |
| uint8_t cmd; |
| int dev_id; |
| |
| /* Get current display memory position */ |
| |
| addr = rc2addr(priv); |
| |
| /* Setup the memory command */ |
| |
| cmd = HT16K33_DISP_DATA_ADDR | addr; |
| |
| /* Get the segments setting */ |
| |
| segment[0] = asciito14seg[data - 32] & 0xff; |
| segment[1] = (asciito14seg[data - 32] & 0xff00) >> 8; |
| |
| dev_id = priv->col / 4; |
| |
| /* Send data to display */ |
| |
| ht16k33_write_data(priv, dev_id, cmd, segment, 2); |
| |
| /* Save it in the buffer because we cannot read from display */ |
| |
| priv->buffer[priv->col * priv->row] = data; |
| |
| /* Update col/row positions */ |
| |
| priv->col++; |
| |
| if (priv->col >= HT16K33_MAX_COL) |
| { |
| priv->col = 0; |
| priv->row++; |
| } |
| |
| if (priv->row >= HT16K33_MAX_ROW) |
| { |
| priv->pendscroll = true; |
| priv->row = HT16K33_MAX_ROW - 1; |
| } |
| |
| /* Update cursor position */ |
| |
| lcd_set_curpos(priv); |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_scroll_up |
| * |
| * Description: |
| * Scroll the display up, and clear the new (last) line. |
| * |
| ****************************************************************************/ |
| |
| static void lcd_scroll_up(FAR struct ht16k33_dev_s *priv) |
| { |
| FAR uint8_t *data; |
| int currow; |
| int curcol; |
| |
| data = kmm_malloc(HT16K33_MAX_COL); |
| if (NULL == data) |
| { |
| lcdinfo("Failed to allocate buffer in lcd_scroll_up()\n"); |
| return; |
| } |
| |
| for (currow = 1; currow < HT16K33_MAX_ROW; ++currow) |
| { |
| priv->row = currow; |
| for (curcol = 0; curcol < HT16K33_MAX_COL; ++curcol) |
| { |
| priv->col = curcol; |
| data[curcol] = lcd_getdata(priv); |
| } |
| |
| priv->col = 0; |
| priv->row = currow - 1; |
| lcd_set_curpos(priv); |
| for (curcol = 0; curcol < HT16K33_MAX_COL; ++curcol) |
| { |
| lcd_putdata(priv, data[curcol]); |
| } |
| } |
| |
| ht16k33_clear_display(priv); |
| |
| kmm_free(data); |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_clear_display |
| * |
| * Description: |
| * Clear the display writing space (' ') to all positions |
| * |
| ****************************************************************************/ |
| |
| static void ht16k33_clear_display(FAR struct ht16k33_dev_s *priv) |
| { |
| int curcol; |
| |
| priv->col = 0; |
| priv->row = HT16K33_MAX_ROW - 1; |
| lcd_set_curpos(priv); |
| for (curcol = 0; curcol < HT16K33_MAX_COL; ++curcol) |
| { |
| lcd_putdata(priv, ' '); |
| } |
| |
| priv->col = 0; |
| priv->row = HT16K33_MAX_ROW - 1; |
| lcd_set_curpos(priv); |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_codec_action |
| * |
| * Description: |
| * Perform an 'action' as per the Segment LCD codec. |
| * |
| * Input Parameters: |
| * priv - device instance |
| * code - SLCD code action code |
| * count - count param for those actions that take it |
| * |
| ****************************************************************************/ |
| |
| static void lcd_codec_action(FAR struct ht16k33_dev_s *priv, |
| enum slcdcode_e code, uint8_t count) |
| { |
| switch (code) |
| { |
| /* Erasure */ |
| |
| case SLCDCODE_BACKDEL: /* Backspace (backward delete) |
| * N characters |
| */ |
| { |
| if (count <= 0) /* we need to delete more 0 positions */ |
| { |
| break; |
| } |
| else |
| { |
| if (count > priv->col) /* saturate to preceding columns |
| * available |
| */ |
| { |
| count = priv->col; |
| } |
| |
| priv->col = priv->col - count; |
| lcd_set_curpos(priv); |
| } |
| |
| /* ... and conscientiously fall through to next case ... */ |
| } |
| |
| case SLCDCODE_FWDDEL: /* Delete (forward delete) N characters |
| * moving text |
| */ |
| { |
| if (count <= 0) /* we need to delete more 0 positions */ |
| { |
| break; |
| } |
| else |
| { |
| uint8_t start; |
| uint8_t end; |
| uint8_t i; |
| uint8_t data; |
| |
| start = priv->col + count; |
| |
| if (start >= HT16K33_MAX_COL) /* nothing left */ |
| { |
| break; |
| } |
| |
| end = start + count; |
| if (end > HT16K33_MAX_COL) /* saturate */ |
| { |
| end = HT16K33_MAX_COL; |
| } |
| |
| for (i = priv->col; i < end; ++start, ++i) /* like memmove */ |
| { |
| priv->col = start; |
| lcd_set_curpos(priv); |
| data = lcd_getdata(priv); |
| priv->col = i; |
| lcd_set_curpos(priv); |
| lcd_putdata(priv, data); |
| } |
| |
| for (; i < HT16K33_MAX_COL; ++i) /* much like memset */ |
| { |
| lcd_putdata(priv, ' '); |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| } |
| break; |
| |
| case SLCDCODE_ERASE: /* Erase N characters from the cursor |
| * position |
| */ |
| if (count > 0) |
| { |
| uint8_t end; |
| uint8_t i; |
| |
| end = priv->col + count; |
| if (end > HT16K33_MAX_COL) |
| { |
| end = HT16K33_MAX_COL; |
| } |
| |
| for (i = priv->col; i < end; ++i) |
| { |
| lcd_putdata(priv, ' '); |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_CLEAR: /* Home the cursor and erase the entire |
| * display |
| */ |
| { |
| /* ht16k33_write_cmd(priv, HT16K33_CLEAR_DISPLAY); */ |
| } |
| break; |
| |
| case SLCDCODE_ERASEEOL: /* Erase from the cursor position to |
| * the end of line |
| */ |
| { |
| uint8_t i; |
| |
| for (i = priv->col; i < HT16K33_MAX_COL; ++i) |
| { |
| lcd_putdata(priv, ' '); |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| /* Cursor movement */ |
| |
| case SLCDCODE_LEFT: /* Cursor left by N characters */ |
| { |
| if (count > priv->col) |
| { |
| priv->col = 0; |
| } |
| else |
| { |
| priv->col -= count; |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_RIGHT: /* Cursor right by N characters */ |
| { |
| priv->col += count; |
| if (priv->col >= HT16K33_MAX_COL) |
| { |
| priv->col = HT16K33_MAX_COL - 1; |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_UP: /* Cursor up by N lines */ |
| { |
| if (count > priv->row) |
| { |
| priv->row = 0; |
| } |
| else |
| { |
| priv->row -= count; |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_DOWN: /* Cursor down by N lines */ |
| { |
| priv->row += count; |
| if (priv->row >= HT16K33_MAX_ROW) |
| { |
| priv->row = HT16K33_MAX_ROW - 1; |
| } |
| |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_HOME: /* Cursor home */ |
| { |
| priv->col = 0; |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_END: /* Cursor end */ |
| { |
| priv->col = HT16K33_MAX_COL - 1; |
| lcd_set_curpos(priv); |
| } |
| break; |
| |
| case SLCDCODE_PAGEUP: /* Cursor up by N pages */ |
| case SLCDCODE_PAGEDOWN: /* Cursor down by N pages */ |
| break; /* Not supportable on this SLCD */ |
| |
| /* Blinking */ |
| |
| case SLCDCODE_BLINKSTART: /* Start blinking with current cursor |
| * position |
| */ |
| ht16k33_write_cmd(priv, 0, HT16K33_DISPLAY_SETUP | |
| DISPLAY_SETUP_BLINK_2HZ); |
| break; |
| |
| case SLCDCODE_BLINKEND: /* End blinking after the current cursor |
| * position |
| */ |
| case SLCDCODE_BLINKOFF: /* Turn blinking off */ |
| ht16k33_write_cmd(priv, 0, HT16K33_DISPLAY_SETUP | |
| DISPLAY_SETUP_BLINK_OFF); |
| break; /* Not implemented */ |
| |
| /* These are actually unreportable errors */ |
| |
| default: |
| case SLCDCODE_NORMAL: /* Not a special keycode */ |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_init |
| * |
| * Description: |
| * perform the initialization sequence to get the LCD into a known state. |
| * |
| ****************************************************************************/ |
| |
| static void lcd_init(FAR struct ht16k33_dev_s *priv) |
| { |
| uint8_t data; |
| int i; |
| |
| for (i = 0; i < CONFIG_LCD_HT16K33_NUMBER_MODULES; i++) |
| { |
| /* Initialize the Display: Turn ON Oscillator */ |
| |
| data = HT16K33_SYSTEM_SETUP | SYSTEM_SETUP_OSC_ON; |
| |
| ht16k33_write_cmd(priv, i, data); |
| |
| /* Clear display */ |
| |
| ht16k33_clear_display(priv); |
| |
| /* Display ON */ |
| |
| data = HT16K33_DISPLAY_SETUP | DISPLAY_SETUP_DISP_ON; |
| |
| ht16k33_write_cmd(priv, i, data); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lcd_curpos_to_fpos |
| * |
| * Description: |
| * Convert a screen cursor pos (row,col) to a file logical offset. This |
| * includes 'synthesized' line feeds at the end of screen lines. |
| * |
| ****************************************************************************/ |
| |
| static void lcd_curpos_to_fpos(FAR struct ht16k33_dev_s *priv, |
| uint8_t row, uint8_t col, FAR off_t *fpos) |
| { |
| /* the logical file position is the linear position plus any synthetic LF */ |
| |
| *fpos = (row * HT16K33_MAX_COL) + col + row; |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_read |
| ****************************************************************************/ |
| |
| static ssize_t ht16k33_read(FAR struct file *filep, FAR char *buffer, |
| size_t buflen) |
| { |
| return -ENOSYS; |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_write |
| ****************************************************************************/ |
| |
| static ssize_t ht16k33_write(FAR struct file *filep, FAR const char *buffer, |
| size_t buflen) |
| { |
| FAR struct inode *inode = filep->f_inode; |
| FAR struct ht16k33_dev_s *priv = inode->i_private; |
| struct lib_meminstream_s instream; |
| struct slcdstate_s state; |
| enum slcdret_e result; |
| uint8_t ch; |
| uint8_t count; |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Initialize the stream for use with the SLCD CODEC */ |
| |
| lib_meminstream(&instream, buffer, buflen); |
| |
| /* Now decode and process every byte in the input buffer */ |
| |
| memset(&state, 0, sizeof(struct slcdstate_s)); |
| while ((result = slcd_decode(&instream.common, |
| &state, &ch, &count)) != SLCDRET_EOF) |
| { |
| /* Is there some pending scroll? */ |
| |
| if (priv->pendscroll) |
| { |
| lcd_scroll_up(priv); |
| priv->pendscroll = false; |
| } |
| |
| if (result == SLCDRET_CHAR) /* A normal character was returned */ |
| { |
| /* Check for ASCII control characters */ |
| |
| if (ch == ASCII_TAB) |
| { |
| /* TODO: define what TAB should do */ |
| } |
| else if (ch == ASCII_VT) |
| { |
| /* Turn the backlight on */ |
| |
| /* TODO: lcd_backlight(priv, true); */ |
| } |
| else if (ch == ASCII_FF) |
| { |
| /* Turn the backlight off */ |
| |
| /* TODO: lcd_backlight(priv, false); */ |
| } |
| else if (ch == ASCII_CR) |
| { |
| /* Perform a Home */ |
| |
| priv->col = 0; |
| lcd_set_curpos(priv); |
| } |
| else if (ch == ASCII_SO) |
| { |
| /* TODO: We don't have cursor */ |
| } |
| else if (ch == ASCII_SI) |
| { |
| /* Perform the re-initialize */ |
| |
| lcd_init(priv); |
| priv->row = 0; |
| priv->col = 0; |
| } |
| else if (ch == ASCII_LF) |
| { |
| /* unixian line term; go to start of next line */ |
| |
| priv->row += 1; |
| if (priv->row >= HT16K33_MAX_ROW) |
| { |
| priv->pendscroll = true; |
| priv->row = HT16K33_MAX_ROW - 1; |
| } |
| |
| priv->col = 0; |
| lcd_set_curpos(priv); |
| } |
| else if (ch == ASCII_BS) |
| { |
| /* Perform the backward deletion */ |
| |
| lcd_codec_action(priv, SLCDCODE_BACKDEL, 1); |
| } |
| else if (ch == ASCII_DEL) |
| { |
| /* Perform the forward deletion */ |
| |
| lcd_codec_action(priv, SLCDCODE_FWDDEL, 1); |
| } |
| else |
| { |
| /* Just print it! */ |
| |
| lcd_putdata(priv, ch); |
| } |
| } |
| else /* (result == SLCDRET_SPEC) */ /* A special SLCD action was returned */ |
| { |
| lcd_codec_action(priv, (enum slcdcode_e)ch, count); |
| } |
| } |
| |
| /* Wherever we wound up, update our logical file pos to reflect it */ |
| |
| lcd_curpos_to_fpos(priv, priv->row, priv->col, &filep->f_pos); |
| |
| nxmutex_unlock(&priv->lock); |
| return buflen; |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_seek |
| * |
| * Description: |
| * Seek the logical file pointer to the specified position. This is |
| * probably not very interesting except possibly for (SEEK_SET, 0) to |
| * rewind the pointer for a subsequent read(). |
| * The file pointer is logical, and includes synthesized LF chars at the |
| * end of the display lines. |
| * |
| ****************************************************************************/ |
| |
| static off_t ht16k33_seek(FAR struct file *filep, off_t offset, int whence) |
| { |
| FAR struct inode *inode = filep->f_inode; |
| FAR struct ht16k33_dev_s *priv = |
| inode->i_private; |
| off_t maxpos; |
| off_t pos; |
| |
| nxmutex_lock(&priv->lock); |
| |
| maxpos = HT16K33_MAX_ROW * HT16K33_MAX_COL + (HT16K33_MAX_ROW - 1); |
| pos = filep->f_pos; |
| |
| switch (whence) |
| { |
| case SEEK_CUR: |
| pos += offset; |
| if (pos > maxpos) |
| { |
| pos = maxpos; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| filep->f_pos = pos; |
| break; |
| |
| case SEEK_SET: |
| pos = offset; |
| if (pos > maxpos) |
| { |
| pos = maxpos; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| filep->f_pos = pos; |
| break; |
| |
| case SEEK_END: |
| pos = maxpos + offset; |
| if (pos > maxpos) |
| { |
| pos = maxpos; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| filep->f_pos = pos; |
| break; |
| |
| default: |
| |
| /* Return EINVAL if the whence argument is invalid */ |
| |
| pos = (off_t)-EINVAL; |
| break; |
| } |
| |
| nxmutex_unlock(&priv->lock); |
| return pos; |
| } |
| |
| /**************************************************************************** |
| * Name: ht16k33_ioctl |
| * |
| * Description: |
| * Perform device operations that are outside the standard I/O model. |
| * |
| ****************************************************************************/ |
| |
| static int ht16k33_ioctl(FAR struct file *filep, int cmd, |
| unsigned long arg) |
| { |
| switch (cmd) |
| { |
| case SLCDIOC_GETATTRIBUTES: /* Get the attributes of the SLCD */ |
| { |
| FAR struct slcd_attributes_s *attr = |
| (FAR struct slcd_attributes_s *)((uintptr_t)arg); |
| |
| lcdinfo("SLCDIOC_GETATTRIBUTES:\n"); |
| |
| if (!attr) |
| { |
| return -EINVAL; |
| } |
| |
| attr->nrows = HT16K33_MAX_ROW; |
| attr->ncolumns = HT16K33_MAX_COL; |
| attr->nbars = 0; |
| attr->maxcontrast = 0; |
| attr->maxbrightness = 16; /* 'brightness' for us is the backlight */ |
| } |
| break; |
| |
| case SLCDIOC_CURPOS: /* Get the SLCD cursor position */ |
| { |
| FAR struct inode *inode = filep->f_inode; |
| FAR struct ht16k33_dev_s *priv = |
| inode->i_private; |
| FAR struct slcd_curpos_s *attr = |
| (FAR struct slcd_curpos_s *)((uintptr_t)arg); |
| |
| attr->row = priv->row; |
| attr->column = priv->col; |
| } |
| break; |
| |
| case SLCDIOC_GETBRIGHTNESS: /* Get the current brightness setting */ |
| { |
| FAR struct inode *inode = filep->f_inode; |
| FAR struct ht16k33_dev_s *priv = |
| inode->i_private; |
| |
| nxmutex_lock(&priv->lock); |
| *(FAR int *)((uintptr_t)arg) = 1; /* Hardcoded */ |
| nxmutex_unlock(&priv->lock); |
| } |
| break; |
| |
| case SLCDIOC_SETBRIGHTNESS: /* Set the brightness to a new value */ |
| { |
| FAR struct inode *inode = filep->f_inode; |
| FAR struct ht16k33_dev_s *priv = |
| inode->i_private; |
| |
| nxmutex_lock(&priv->lock); |
| ht16k33_setcontrast(priv, 0, (uint8_t)arg); |
| nxmutex_unlock(&priv->lock); |
| } |
| break; |
| |
| case SLCDIOC_SETBAR: /* Set bars on a bar display */ |
| case SLCDIOC_GETCONTRAST: /* Get the current contrast setting */ |
| case SLCDIOC_SETCONTRAST: /* Set the contrast to a new value */ |
| default: |
| return -ENOTTY; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ht16k33_register |
| * |
| * Description: |
| * Register the HT16K33 character device as 'devpath' |
| * |
| * Input Parameters: |
| * devno - The device number to register. E.g., "/dev/slcd0" |
| * i2c - An instance of the I2C interface to use to communicate with |
| * HT16K33 |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno value on failure. |
| * |
| ****************************************************************************/ |
| |
| int ht16k33_register(int devno, FAR struct i2c_master_s *i2c) |
| { |
| FAR struct ht16k33_dev_s *priv; |
| char devname[DEVNAME_FMTLEN]; |
| int ret; |
| |
| /* Initialize the HT16K33 device structure */ |
| |
| priv = (FAR struct ht16k33_dev_s *) |
| kmm_malloc(sizeof(struct ht16k33_dev_s)); |
| if (!priv) |
| { |
| snerr("ERROR: Failed to allocate instance\n"); |
| return -ENOMEM; |
| } |
| |
| /* Setup priv with initial values */ |
| |
| priv->i2c = i2c; |
| priv->col = 0; |
| priv->row = 0; |
| priv->pendscroll = false; |
| |
| nxmutex_init(&priv->lock); |
| |
| /* Initialize the display */ |
| |
| lcd_init(priv); |
| |
| /* Create the character device name */ |
| |
| snprintf(devname, sizeof(devname), DEVNAME_FMT, devno); |
| |
| /* Register the driver */ |
| |
| ret = register_driver(devname, &g_ht16k33fops, 0666, priv); |
| if (ret < 0) |
| { |
| snerr("ERROR: Failed to register driver: %d\n", ret); |
| nxmutex_destroy(&priv->lock); |
| kmm_free(priv); |
| } |
| |
| return ret; |
| } |
| #endif /* CONFIG_SPI && CONFIG_HT16K33 */ |