From 53a833fb33808274615743f579d7c4299a3ab84e Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Mon, 2 Sep 2019 13:41:24 +0200 Subject: [PATCH] Try to implement a display driver (but failed). hardware problem? new processor incoming anyways --- midi2cv/drivers/display.cc | 176 +++++++++++++++++++++++++++++++++++++ midi2cv/drivers/display.h | 29 ++++++ midi2cv/drivers/gpio.cc | 8 +- midi2cv/drivers/gpio.h | 5 ++ midi2cv/midi2cv.cc | 119 +++++++++++++++++-------- 5 files changed, 298 insertions(+), 39 deletions(-) create mode 100644 midi2cv/drivers/display.cc create mode 100644 midi2cv/drivers/display.h diff --git a/midi2cv/drivers/display.cc b/midi2cv/drivers/display.cc new file mode 100644 index 0000000..96d319f --- /dev/null +++ b/midi2cv/drivers/display.cc @@ -0,0 +1,176 @@ +#include "display.h" +#include "gpio.h" +#include + +// SH1106 command definitions +#define SH1106_CMD_SETMUX (uint8_t)0xA8 // Set multiplex ratio (N, number of lines active on display) +#define SH1106_CMD_SETOFFS (uint8_t)0xD3 // Set display offset +#define SH1106_CMD_STARTLINE (uint8_t)0x40 // Set display start line +#define SH1106_CMD_SEG_NORM (uint8_t)0xA0 // Column 0 is mapped to SEG0 (X coordinate normal) +#define SH1106_CMD_SEG_INV (uint8_t)0xA1 // Column 127 is mapped to SEG0 (X coordinate inverted) +#define SH1106_CMD_COM_NORM (uint8_t)0xC0 // Scan from COM0 to COM[N-1] (N - mux ratio, Y coordinate normal) +#define SH1106_CMD_COM_INV (uint8_t)0xC8 // Scan from COM[N-1] to COM0 (N - mux ratio, Y coordinate inverted) +#define SH1106_CMD_COM_HW (uint8_t)0xDA // Set COM pins hardware configuration +#define SH1106_CMD_CONTRAST (uint8_t)0x81 // Contrast control +#define SH1106_CMD_EDON (uint8_t)0xA5 // Entire display ON enabled (all pixels on, RAM content ignored) +#define SH1106_CMD_EDOFF (uint8_t)0xA4 // Entire display ON disabled (output follows RAM content) +#define SH1106_CMD_INV_OFF (uint8_t)0xA6 // Entire display inversion OFF (normal display) +#define SH1106_CMD_INV_ON (uint8_t)0xA7 // Entire display inversion ON (all pixels inverted) +#define SH1106_CMD_CLOCKDIV (uint8_t)0xD5 // Set display clock divide ratio/oscillator frequency +#define SH1106_CMD_DISP_ON (uint8_t)0xAF // Display ON +#define SH1106_CMD_DISP_OFF (uint8_t)0xAE // Display OFF (sleep mode) + +#define SH1106_CMD_COL_LOW (uint8_t)0x00 // Set Lower Column Address +#define SH1106_CMD_COL_HIGH (uint8_t)0x10 // Set Higher Column Address +#define SH1106_CMD_PAGE_ADDR (uint8_t)0xB0 // Set Page Address + +#define SH1106_CMD_CHARGE (uint8_t)0x22 // Dis-charge / Pre-charge Period +#define SH1106_CMD_SCRL_HR (uint8_t)0x26 // Setup continuous horizontal scroll right +#define SH1106_CMD_SCRL_HL (uint8_t)0x27 // Setup continuous horizontal scroll left +#define SH1106_CMD_SCRL_VHR (uint8_t)0x29 // Setup continuous vertical and horizontal scroll right +#define SH1106_CMD_SCRL_VHL (uint8_t)0x2A // Setup continuous vertical and horizontal scroll left +#define SH1106_CMD_SCRL_STOP (uint8_t)0x2E // Deactivate scroll +#define SH1106_CMD_SCRL_ACT (uint8_t)0x2F // Activate scroll + +void Display::Init() +{ + gpio.Write(GPIO_PIN(SS_OLED), Bit_SET); // deactivate OLED spi + + gpio.Write(GPIO_PIN(RST_OLED), Bit_SET); + gpio.Write(GPIO_PIN(RST_OLED), Bit_RESET); + gpio.Write(GPIO_PIN(RST_OLED), Bit_SET); + + GPIO_InitTypeDef gpio_init; + gpio_init.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; + gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOB, &gpio_init); + + SPI_InitTypeDef spi_init; + spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + spi_init.SPI_Mode = SPI_Mode_Master; + spi_init.SPI_DataSize = SPI_DataSize_8b; + spi_init.SPI_CPOL = SPI_CPOL_Low; + spi_init.SPI_CPHA = SPI_CPHA_1Edge; + spi_init.SPI_NSS = SPI_NSS_Soft; + spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; + spi_init.SPI_FirstBit = SPI_FirstBit_LSB; + spi_init.SPI_CRCPolynomial = 10; + SPI_Init(SPI2, &spi_init); + SPI_CalculateCRC(SPI2, DISABLE); + SPI_Cmd(SPI2, ENABLE); + +#define SH1106_cmd_double(x, y) \ + WriteCommand(x); \ + WriteCommand(y); +#define SH1106_cmd(x) WriteCommand(x); + + // Set multiplex ratio (visible lines) + SH1106_cmd_double(SH1106_CMD_SETMUX, 0x3F); // 64MUX + + // Set display offset (offset of first line from the top of display) + SH1106_cmd_double(SH1106_CMD_SETOFFS, 0x00); // Offset: 0 + + // Set display start line (first line displayed) + SH1106_cmd(SH1106_CMD_STARTLINE | 0x00); // Start line: 0 + + // Set segment re-map (X coordinate) + SH1106_cmd(SH1106_CMD_SEG_NORM); + + // Set COM output scan direction (Y coordinate) + SH1106_cmd(SH1106_CMD_COM_NORM); + + // Set COM pins hardware configuration + // bit[4]: reset - sequential COM pin configuration + // set - alternative COM pin configuration (reset value) + // bit[5]: reset - disable COM left/right remap (reset value) + // set - enable COM left/right remap + SH1106_cmd_double(SH1106_CMD_COM_HW, 0x12); + + uint8_t dis_charge = 0x00; + uint8_t pre_charge = 0x00; + + SH1106_cmd_double(SH1106_CMD_CHARGE, dis_charge | (pre_charge << 4)); + + // Set contrast control + SH1106_cmd_double(SH1106_CMD_CONTRAST, 0x0F); // Contrast: middle level + + SH1106_cmd(0x30); + + // Disable entire display ON + SH1106_cmd(SH1106_CMD_EDOFF); // Display follows RAM content + + // Disable display inversion + SH1106_cmd(SH1106_CMD_INV_OFF); // Normal display mode + + // Set clock divide ratio and oscillator frequency + // bits[3:0] defines the divide ratio of the display clocks (bits[3:0] + 1) + // bits[7:4] set the oscillator frequency (Fosc), frequency increases with the value of these bits + // 0xF0 value gives maximum frequency (maximum Fosc without divider) + // 0x0F value gives minimum frequency (minimum Fosc divided by 16) + // The higher display frequency decreases image flickering but increases current consumption and vice versa + SH1106_cmd_double(SH1106_CMD_CLOCKDIV, 0xF0); + + // Display ON + SH1106_cmd(SH1106_CMD_DISP_ON); // Display enabled + SH1106_cmd(SH1106_CMD_EDON); + Clear(); + Update(); +} + +void Display::WriteCommand(uint8_t byte) +{ + gpio.Write(GPIO_PIN(SDC_OLED), Bit_RESET); // command mode + gpio.Write(GPIO_PIN(SS_OLED), Bit_RESET); // activate OLED spi + while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET) + ; + while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) + ; + SPI_I2S_SendData(SPI2, byte); // transmit via spi + gpio.Write(GPIO_PIN(SS_OLED), Bit_SET); // disable oled spi +} + +void Display::WriteData(uint8_t* buffer, uint8_t size) +{ + gpio.Write(GPIO_PIN(SDC_OLED), Bit_SET); // data mode + gpio.Write(GPIO_PIN(SS_OLED), Bit_RESET); // activate OLED spi + for (int i = 0; i < size; i++) { + while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET) + ; + while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) + ; + SPI_I2S_SendData(SPI2, buffer[i]); // transmit via spi + for (int i = 0; i < 100; i++) + ; + } + gpio.Write(GPIO_PIN(SS_OLED), Bit_SET); // disable oled spi +} + +void Display::Clear() +{ + uint32_t i = 0; + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = 0x00; +} + +void Display::Update() +{ + for (uint32_t i = 0; i < 8; i++) { + WriteCommand(0xb0 + i); + WriteCommand(0x00); + WriteCommand(0x10); + WriteData(&buffer[128 * i], 128); + } +} + +void Display::WritePixel(uint8_t x, uint8_t y, bool color) +{ + if (x > DISPLAY_WIDTH || y > DISPLAY_HEIGHT) + return; + + if (color) { + buffer[x + (y / 8) * DISPLAY_WIDTH] |= 1 << (y % 8); + } else { + buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y % 8)); + } +} diff --git a/midi2cv/drivers/display.h b/midi2cv/drivers/display.h new file mode 100644 index 0000000..a622cb3 --- /dev/null +++ b/midi2cv/drivers/display.h @@ -0,0 +1,29 @@ +#ifndef MIDI2CV_DRIVERS_DISPLAY_H +#define MIDI2CV_DRIVERS_DISPLAY_H + +#include +#include "stmlib/stmlib.h" + +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 64 + + + +class Display { + public: + Display() { } + ~Display() {} + + void Init(); + void WritePixel(uint8_t x, uint8_t y, bool color); + void Clear(); + void Update(); + private: + DISALLOW_COPY_AND_ASSIGN(Display); + void WriteCommand(uint8_t byte); + void WriteData(uint8_t* data, uint8_t length); + + uint8_t buffer[DISPLAY_WIDTH*DISPLAY_HEIGHT / 8]; +}; + +#endif diff --git a/midi2cv/drivers/gpio.cc b/midi2cv/drivers/gpio.cc index 8e64115..4f868b1 100644 --- a/midi2cv/drivers/gpio.cc +++ b/midi2cv/drivers/gpio.cc @@ -2,10 +2,11 @@ #include + void GPIO::Init() { GPIO_InitTypeDef gpio_init; - gpio_init.GPIO_Pin = PIN_SS_DAC0 | PIN_SS_DAC1 | PIN_RST_USB | PIN_SS_USB | PIN_SS_OLED | PIN_SDC_OLED; + gpio_init.GPIO_Pin = PIN_RST_OLED | PIN_SS_DAC0 | PIN_SS_DAC1 | PIN_RST_USB | PIN_SS_USB | PIN_SS_OLED | PIN_SDC_OLED; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &gpio_init); @@ -14,6 +15,11 @@ void GPIO::Init() { gpio_init.GPIO_Speed = GPIO_Speed_50MHz; gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio_init); + + Write(GPIO_PIN(SS_DAC0), Bit_SET); + Write(GPIO_PIN(SS_DAC1), Bit_SET); + Write(GPIO_PIN(SS_USB), Bit_SET); + Write(GPIO_PIN(SS_OLED), Bit_SET); } void GPIO::Write(GPIO_TypeDef* port, uint16_t pin, bool state) { diff --git a/midi2cv/drivers/gpio.h b/midi2cv/drivers/gpio.h index 1cd92d6..8931e27 100644 --- a/midi2cv/drivers/gpio.h +++ b/midi2cv/drivers/gpio.h @@ -5,6 +5,8 @@ #include "stmlib/stmlib.h" #include +#define PORT_RST_OLED GPIOB +#define PIN_RST_OLED GPIO_Pin_0 #define PORT_SS_DAC0 GPIOB #define PIN_SS_DAC0 GPIO_Pin_6 #define PORT_SS_DAC1 GPIOB @@ -44,4 +46,7 @@ class GPIO { DISALLOW_COPY_AND_ASSIGN(GPIO); }; +extern GPIO gpio; + #endif + diff --git a/midi2cv/midi2cv.cc b/midi2cv/midi2cv.cc index 4055edc..86adcbc 100644 --- a/midi2cv/midi2cv.cc +++ b/midi2cv/midi2cv.cc @@ -1,54 +1,97 @@ #include +#include "drivers/display.h" #include "drivers/gpio.h" using namespace stmlib; - - GPIO gpio; - +Display display; // Default interrupt handlers. extern "C" { - void NMI_Handler() { } - void HardFault_Handler() { - while (1); - } - void MemManage_Handler() { while (1); } - void BusFault_Handler() { while (1); } - void UsageFault_Handler() { while (1); } - void SVC_Handler() { } - void DebugMon_Handler() { } - void PendSV_Handler() { } - - // called every 1ms - void SysTick_Handler() { - } +void NMI_Handler() {} +void HardFault_Handler() +{ + while (1) + ; } - - - -void Init(void) { - SystemInit(); - NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x1000); - - RCC_APB2PeriphClockCmd( - RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | - RCC_APB2Periph_TIM1 | RCC_APB2Periph_USART1, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); - - gpio.Init(); +void MemManage_Handler() +{ + while (1) + ; } +void BusFault_Handler() +{ + while (1) + ; +} +void UsageFault_Handler() +{ + while (1) + ; +} +void SVC_Handler() {} +void DebugMon_Handler() {} +void PendSV_Handler() {} -int main(void) { - Init(); -while (1) { - // hi - gpio.Write(GPIO_PIN(GATE_OUT_1), 1); - gpio.Write(GPIO_PIN(GATE_OUT_2), 0); - gpio.Write(GPIO_PIN(GATE_OUT_3), 1); - gpio.Write(GPIO_PIN(GATE_OUT_4), 0); +// called every 1ms +void SysTick_Handler() +{ } } +void Init(void) +{ + SystemInit(); + NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x1000); + + RCC_APB2PeriphClockCmd( + RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1 | RCC_APB2Periph_USART1, ENABLE); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); + TIM_TimeBaseInitTypeDef timer_init; + timer_init.TIM_Period = F_CPU / (48000 * 4) - 1; + timer_init.TIM_Prescaler = 0; + timer_init.TIM_ClockDivision = TIM_CKD_DIV1; + timer_init.TIM_CounterMode = TIM_CounterMode_Up; + timer_init.TIM_RepetitionCounter = 0; + TIM_InternalClockConfig(TIM1); + TIM_TimeBaseInit(TIM1, &timer_init); + TIM_Cmd(TIM1, ENABLE); + + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2.2 priority split. + + // DAC interrupt is given highest priority + NVIC_InitTypeDef timer_interrupt; + timer_interrupt.NVIC_IRQChannel = TIM1_UP_IRQn; + timer_interrupt.NVIC_IRQChannelPreemptionPriority = 0; + timer_interrupt.NVIC_IRQChannelSubPriority = 0; + timer_interrupt.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&timer_interrupt); + + SysTick_Config(F_CPU / 8000); + + gpio.Init(); +// asm("bkpt #0"); + display.Init(); +} + +int main(void) +{ + Init(); + while (1) { + for (int x = 0; x < DISPLAY_WIDTH; x++) + for (int y = 0; y < DISPLAY_HEIGHT; y++) { + display.WritePixel(x, y, 0xff); + display.WritePixel(DISPLAY_WIDTH - x, DISPLAY_WIDTH - y, 0xff); + } + display.Update(); + // hi + gpio.Write(GPIO_PIN(GATE_OUT_1), 0); + gpio.Write(GPIO_PIN(GATE_OUT_2), 0); + gpio.Write(GPIO_PIN(GATE_OUT_3), 0); + gpio.Write(GPIO_PIN(GATE_OUT_4), 0); + gpio.Write(GPIO_PIN(GATE_OUT_5), 0); + gpio.Write(GPIO_PIN(GATE_OUT_6), 0); + } +}