2019-04-27 00:13:57 -04:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
2021-01-03 17:29:23 -05:00
|
|
|
#include "driver/spi_master.h"
|
|
|
|
#include "driver/gpio.h"
|
2019-04-27 00:13:57 -04:00
|
|
|
#include "esp_log.h"
|
|
|
|
|
|
|
|
#include "ssd1306.h"
|
|
|
|
|
2022-05-03 22:33:07 -04:00
|
|
|
#define TAG "SSD1306"
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2022-11-18 17:50:20 -05:00
|
|
|
#if CONFIG_SPI2_HOST
|
|
|
|
#define HOST_ID SPI2_HOST
|
|
|
|
#elif CONFIG_SPI3_HOST
|
|
|
|
#define HOST_ID SPI3_HOST
|
|
|
|
#else
|
|
|
|
#define HOST_ID SPI2_HOST // If i2c is selected
|
2020-12-06 04:11:27 -05:00
|
|
|
#endif
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2024-03-23 19:14:25 -04:00
|
|
|
#define SPI_COMMAND_MODE 0
|
|
|
|
#define SPI_DATA_MODE 1
|
2024-03-25 09:28:55 -04:00
|
|
|
#define SPI_DEFAULT_FREQUENCY 1000000; // 1MHz
|
|
|
|
|
|
|
|
int clock_speed_hz = SPI_DEFAULT_FREQUENCY;
|
|
|
|
|
|
|
|
void spi_clock_speed(int speed) {
|
|
|
|
ESP_LOGI(TAG, "SPI clock speed=%d MHz", speed/1000000);
|
|
|
|
clock_speed_hz = speed;
|
|
|
|
}
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2024-09-08 22:01:19 -04:00
|
|
|
void spi_master_init(SSD1306_t * dev, int16_t mosi, int16_t sclk, int16_t cs, int16_t dc, int16_t reset)
|
2019-04-27 00:13:57 -04:00
|
|
|
{
|
|
|
|
esp_err_t ret;
|
|
|
|
|
2024-09-08 22:01:19 -04:00
|
|
|
gpio_reset_pin( cs );
|
|
|
|
gpio_set_direction( cs, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( cs, 0 );
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2024-09-08 22:01:19 -04:00
|
|
|
gpio_reset_pin( dc );
|
|
|
|
gpio_set_direction( dc, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( dc, 0 );
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2024-09-08 22:01:19 -04:00
|
|
|
if ( reset >= 0 ) {
|
|
|
|
gpio_reset_pin( reset );
|
|
|
|
gpio_set_direction( reset, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( reset, 0 );
|
2019-05-13 06:11:44 -04:00
|
|
|
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
2024-09-08 22:01:19 -04:00
|
|
|
gpio_set_level( reset, 1 );
|
2019-05-02 20:02:10 -04:00
|
|
|
}
|
|
|
|
|
2019-05-13 06:11:44 -04:00
|
|
|
spi_bus_config_t spi_bus_config = {
|
2024-09-08 22:01:19 -04:00
|
|
|
.mosi_io_num = mosi,
|
2019-05-13 06:11:44 -04:00
|
|
|
.miso_io_num = -1,
|
2024-09-08 22:01:19 -04:00
|
|
|
.sclk_io_num = sclk,
|
2019-05-13 06:11:44 -04:00
|
|
|
.quadwp_io_num = -1,
|
2021-07-26 20:34:50 -04:00
|
|
|
.quadhd_io_num = -1,
|
|
|
|
.max_transfer_sz = 0,
|
|
|
|
.flags = 0
|
2019-05-13 06:11:44 -04:00
|
|
|
};
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2022-11-18 17:50:20 -05:00
|
|
|
ESP_LOGI(TAG, "SPI HOST_ID=%d", HOST_ID);
|
|
|
|
ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO );
|
2022-05-03 22:33:07 -04:00
|
|
|
ESP_LOGI(TAG, "spi_bus_initialize=%d",ret);
|
2019-04-27 00:13:57 -04:00
|
|
|
assert(ret==ESP_OK);
|
|
|
|
|
2019-05-13 06:11:44 -04:00
|
|
|
spi_device_interface_config_t devcfg;
|
|
|
|
memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) );
|
2024-03-25 09:28:55 -04:00
|
|
|
//devcfg.clock_speed_hz = SPI_DEFAULT_FREQUENCY;
|
|
|
|
devcfg.clock_speed_hz = clock_speed_hz;
|
2024-09-08 22:01:19 -04:00
|
|
|
devcfg.spics_io_num = cs;
|
2019-05-13 06:11:44 -04:00
|
|
|
devcfg.queue_size = 1;
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2024-09-11 19:00:14 -04:00
|
|
|
spi_device_handle_t spi_device_handle;
|
|
|
|
ret = spi_bus_add_device( HOST_ID, &devcfg, &spi_device_handle);
|
2022-05-03 22:33:07 -04:00
|
|
|
ESP_LOGI(TAG, "spi_bus_add_device=%d",ret);
|
2019-04-27 00:13:57 -04:00
|
|
|
assert(ret==ESP_OK);
|
2024-09-08 22:01:19 -04:00
|
|
|
|
|
|
|
dev->_dc = dc;
|
|
|
|
dev->_address = SPI_ADDRESS;
|
|
|
|
dev->_flip = false;
|
2024-09-11 19:00:14 -04:00
|
|
|
dev->_spi_device_handle = spi_device_handle;
|
2024-09-08 22:01:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void spi_device_add(SSD1306_t * dev, int16_t cs, int16_t dc, int16_t reset)
|
|
|
|
{
|
|
|
|
ESP_LOGW(TAG, "Will not install spi master driver");
|
|
|
|
esp_err_t ret;
|
|
|
|
|
|
|
|
gpio_reset_pin( cs );
|
|
|
|
gpio_set_direction( cs, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( cs, 0 );
|
|
|
|
|
|
|
|
gpio_reset_pin( dc );
|
|
|
|
gpio_set_direction( dc, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( dc, 0 );
|
|
|
|
|
|
|
|
if ( reset >= 0 ) {
|
|
|
|
gpio_reset_pin( reset );
|
|
|
|
gpio_set_direction( reset, GPIO_MODE_OUTPUT );
|
|
|
|
gpio_set_level( reset, 0 );
|
|
|
|
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
|
|
|
gpio_set_level( reset, 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
spi_bus_config_t spi_bus_config = {
|
|
|
|
.mosi_io_num = mosi,
|
|
|
|
.miso_io_num = -1,
|
|
|
|
.sclk_io_num = sclk,
|
|
|
|
.quadwp_io_num = -1,
|
|
|
|
.quadhd_io_num = -1,
|
|
|
|
.max_transfer_sz = 0,
|
|
|
|
.flags = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "SPI HOST_ID=%d", HOST_ID);
|
|
|
|
ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO );
|
|
|
|
ESP_LOGI(TAG, "spi_bus_initialize=%d",ret);
|
|
|
|
assert(ret==ESP_OK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
spi_device_interface_config_t devcfg;
|
|
|
|
memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) );
|
|
|
|
//devcfg.clock_speed_hz = SPI_DEFAULT_FREQUENCY;
|
|
|
|
devcfg.clock_speed_hz = clock_speed_hz;
|
|
|
|
devcfg.spics_io_num = cs;
|
|
|
|
devcfg.queue_size = 1;
|
|
|
|
|
2024-09-11 19:00:14 -04:00
|
|
|
spi_device_handle_t spi_device_handle;
|
|
|
|
ret = spi_bus_add_device( HOST_ID, &devcfg, &spi_device_handle);
|
2024-09-08 22:01:19 -04:00
|
|
|
ESP_LOGI(TAG, "spi_bus_add_device=%d",ret);
|
|
|
|
assert(ret==ESP_OK);
|
|
|
|
|
|
|
|
dev->_dc = dc;
|
2024-03-23 19:14:25 -04:00
|
|
|
dev->_address = SPI_ADDRESS;
|
2021-04-23 21:15:14 -04:00
|
|
|
dev->_flip = false;
|
2024-09-11 19:00:14 -04:00
|
|
|
dev->_spi_device_handle = spi_device_handle;
|
2019-04-27 00:13:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-05 19:18:28 -04:00
|
|
|
bool spi_master_write_byte(spi_device_handle_t SPIHandle, const uint8_t* Data, size_t DataLength )
|
2019-04-27 00:13:57 -04:00
|
|
|
{
|
2019-05-13 06:11:44 -04:00
|
|
|
spi_transaction_t SPITransaction;
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2019-05-13 06:11:44 -04:00
|
|
|
if ( DataLength > 0 ) {
|
|
|
|
memset( &SPITransaction, 0, sizeof( spi_transaction_t ) );
|
|
|
|
SPITransaction.length = DataLength * 8;
|
|
|
|
SPITransaction.tx_buffer = Data;
|
|
|
|
spi_device_transmit( SPIHandle, &SPITransaction );
|
|
|
|
}
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2019-05-13 06:11:44 -04:00
|
|
|
return true;
|
2019-04-27 00:13:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool spi_master_write_command(SSD1306_t * dev, uint8_t Command )
|
|
|
|
{
|
2019-05-13 06:11:44 -04:00
|
|
|
static uint8_t CommandByte = 0;
|
|
|
|
CommandByte = Command;
|
2024-03-23 19:14:25 -04:00
|
|
|
gpio_set_level( dev->_dc, SPI_COMMAND_MODE );
|
2024-09-11 19:00:14 -04:00
|
|
|
return spi_master_write_byte( dev->_spi_device_handle, &CommandByte, 1 );
|
2019-04-27 00:13:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool spi_master_write_data(SSD1306_t * dev, const uint8_t* Data, size_t DataLength )
|
|
|
|
{
|
2024-03-23 19:14:25 -04:00
|
|
|
gpio_set_level( dev->_dc, SPI_DATA_MODE );
|
2024-09-11 19:00:14 -04:00
|
|
|
return spi_master_write_byte( dev->_spi_device_handle, Data, DataLength );
|
2019-04-27 00:13:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void spi_init(SSD1306_t * dev, int width, int height)
|
|
|
|
{
|
|
|
|
dev->_width = width;
|
|
|
|
dev->_height = height;
|
|
|
|
dev->_pages = 8;
|
|
|
|
if (dev->_height == 32) dev->_pages = 4;
|
|
|
|
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_DISPLAY_OFF); // AE
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_MUX_RATIO); // A8
|
2019-04-27 00:13:57 -04:00
|
|
|
if (dev->_height == 64) spi_master_write_command(dev, 0x3F);
|
|
|
|
if (dev->_height == 32) spi_master_write_command(dev, 0x1F);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_DISPLAY_OFFSET); // D3
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x00);
|
|
|
|
spi_master_write_command(dev, OLED_CONTROL_BYTE_DATA_STREAM); // 40
|
2021-04-23 21:15:14 -04:00
|
|
|
if (dev->_flip) {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP_0); // A0
|
|
|
|
} else {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP_1); // A1
|
|
|
|
}
|
|
|
|
//spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP); // A1
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_COM_SCAN_MODE); // C8
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_DISPLAY_CLK_DIV); // D5
|
|
|
|
spi_master_write_command(dev, 0x80);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_COM_PIN_MAP); // DA
|
2019-04-27 00:13:57 -04:00
|
|
|
if (dev->_height == 64) spi_master_write_command(dev, 0x12);
|
|
|
|
if (dev->_height == 32) spi_master_write_command(dev, 0x02);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_CONTRAST); // 81
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0xFF);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_DISPLAY_RAM); // A4
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_VCOMH_DESELCT); // DB
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x40);
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_MEMORY_ADDR_MODE); // 20
|
|
|
|
//spi_master_write_command(dev, OLED_CMD_SET_HORI_ADDR_MODE); // 00
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_PAGE_ADDR_MODE); // 02
|
2019-04-27 00:13:57 -04:00
|
|
|
// Set Lower Column Start Address for Page Addressing Mode
|
|
|
|
spi_master_write_command(dev, 0x00);
|
|
|
|
// Set Higher Column Start Address for Page Addressing Mode
|
|
|
|
spi_master_write_command(dev, 0x10);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_CHARGE_PUMP); // 8D
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x14);
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_DEACTIVE_SCROLL); // 2E
|
|
|
|
spi_master_write_command(dev, OLED_CMD_DISPLAY_NORMAL); // A6
|
|
|
|
spi_master_write_command(dev, OLED_CMD_DISPLAY_ON); // AF
|
2019-04-27 00:13:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void spi_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width)
|
|
|
|
{
|
|
|
|
if (page >= dev->_pages) return;
|
|
|
|
if (seg >= dev->_width) return;
|
|
|
|
|
2021-01-29 20:57:39 -05:00
|
|
|
int _seg = seg + CONFIG_OFFSETX;
|
|
|
|
uint8_t columLow = _seg & 0x0F;
|
|
|
|
uint8_t columHigh = (_seg >> 4) & 0x0F;
|
2019-04-27 00:13:57 -04:00
|
|
|
|
2021-04-23 21:15:14 -04:00
|
|
|
int _page = page;
|
|
|
|
if (dev->_flip) {
|
|
|
|
_page = (dev->_pages - page) - 1;
|
|
|
|
}
|
|
|
|
|
2019-04-27 00:13:57 -04:00
|
|
|
// Set Lower Column Start Address for Page Addressing Mode
|
|
|
|
spi_master_write_command(dev, (0x00 + columLow));
|
|
|
|
// Set Higher Column Start Address for Page Addressing Mode
|
|
|
|
spi_master_write_command(dev, (0x10 + columHigh));
|
|
|
|
// Set Page Start Address for Page Addressing Mode
|
2021-04-23 21:15:14 -04:00
|
|
|
spi_master_write_command(dev, 0xB0 | _page);
|
2019-04-27 00:13:57 -04:00
|
|
|
|
|
|
|
spi_master_write_data(dev, images, width);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void spi_contrast(SSD1306_t * dev, int contrast) {
|
|
|
|
int _contrast = contrast;
|
|
|
|
if (contrast < 0x0) _contrast = 0;
|
|
|
|
if (contrast > 0xFF) _contrast = 0xFF;
|
|
|
|
|
|
|
|
spi_master_write_command(dev, OLED_CMD_SET_CONTRAST); // 81
|
|
|
|
spi_master_write_command(dev, _contrast);
|
|
|
|
}
|
|
|
|
|
2019-06-22 21:09:29 -04:00
|
|
|
void spi_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll)
|
2019-04-27 00:13:57 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (scroll == SCROLL_RIGHT) {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_HORIZONTAL_RIGHT); // 26
|
|
|
|
spi_master_write_command(dev, 0x00); // Dummy byte
|
|
|
|
spi_master_write_command(dev, 0x00); // Define start page address
|
|
|
|
spi_master_write_command(dev, 0x07); // Frame frequency
|
|
|
|
spi_master_write_command(dev, 0x07); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x00); //
|
|
|
|
spi_master_write_command(dev, 0xFF); //
|
|
|
|
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll == SCROLL_LEFT) {
|
2019-06-03 06:27:01 -04:00
|
|
|
spi_master_write_command(dev, OLED_CMD_HORIZONTAL_LEFT); // 27
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x00); // Dummy byte
|
|
|
|
spi_master_write_command(dev, 0x00); // Define start page address
|
|
|
|
spi_master_write_command(dev, 0x07); // Frame frequency
|
|
|
|
spi_master_write_command(dev, 0x07); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x00); //
|
|
|
|
spi_master_write_command(dev, 0xFF); //
|
|
|
|
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll == SCROLL_DOWN) {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_CONTINUOUS_SCROLL); // 29
|
|
|
|
spi_master_write_command(dev, 0x00); // Dummy byte
|
|
|
|
spi_master_write_command(dev, 0x00); // Define start page address
|
|
|
|
spi_master_write_command(dev, 0x07); // Frame frequency
|
|
|
|
//spi_master_write_command(dev, 0x01); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x00); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x3F); // Vertical scrolling offset
|
|
|
|
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_VERTICAL); // A3
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x00);
|
|
|
|
if (dev->_height == 64)
|
2019-06-03 06:27:01 -04:00
|
|
|
spi_master_write_command(dev, 0x40);
|
2019-04-27 00:13:57 -04:00
|
|
|
if (dev->_height == 32)
|
2019-06-03 06:27:01 -04:00
|
|
|
spi_master_write_command(dev, 0x20);
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll == SCROLL_UP) {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_CONTINUOUS_SCROLL); // 29
|
|
|
|
spi_master_write_command(dev, 0x00); // Dummy byte
|
|
|
|
spi_master_write_command(dev, 0x00); // Define start page address
|
|
|
|
spi_master_write_command(dev, 0x07); // Frame frequency
|
|
|
|
//spi_master_write_command(dev, 0x01); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x00); // Define end page address
|
|
|
|
spi_master_write_command(dev, 0x01); // Vertical scrolling offset
|
|
|
|
|
2021-01-03 17:29:23 -05:00
|
|
|
spi_master_write_command(dev, OLED_CMD_VERTICAL); // A3
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, 0x00);
|
|
|
|
if (dev->_height == 64)
|
2019-06-03 06:27:01 -04:00
|
|
|
spi_master_write_command(dev, 0x40);
|
2019-04-27 00:13:57 -04:00
|
|
|
if (dev->_height == 32)
|
2019-06-03 06:27:01 -04:00
|
|
|
spi_master_write_command(dev, 0x20);
|
2019-04-27 00:13:57 -04:00
|
|
|
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll == SCROLL_STOP) {
|
|
|
|
spi_master_write_command(dev, OLED_CMD_DEACTIVE_SCROLL); // 2E
|
|
|
|
}
|
|
|
|
}
|