From f594aa9f435ed3f6484ee46eb618f8fd50d91ea9 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Thu, 19 Sep 2019 16:41:32 +0200 Subject: [PATCH] Display driver fixes, UI --- midi2cv/bootloader/bootloader.cc | 2 +- midi2cv/bootloader/makefile | 9 +- midi2cv/drivers/display.cc | 230 ++++++++++++------------------- midi2cv/drivers/display.h | 16 +-- midi2cv/drivers/gpio.cc | 17 --- midi2cv/midi2cv.cc | 76 ++++++---- midi2cv/ui.cc | 31 +++++ midi2cv/ui.h | 19 +++ 8 files changed, 195 insertions(+), 205 deletions(-) create mode 100644 midi2cv/ui.cc create mode 100644 midi2cv/ui.h diff --git a/midi2cv/bootloader/bootloader.cc b/midi2cv/bootloader/bootloader.cc index c560bb3..ddb002c 100644 --- a/midi2cv/bootloader/bootloader.cc +++ b/midi2cv/bootloader/bootloader.cc @@ -4,7 +4,7 @@ using namespace stmlib; -const uint32_t kStartAddress = 0x08001000; +const uint32_t kStartAddress = 0x08008000; int main(void) { SystemInit(); diff --git a/midi2cv/bootloader/makefile b/midi2cv/bootloader/makefile index 58b8d74..154b635 100644 --- a/midi2cv/bootloader/makefile +++ b/midi2cv/bootloader/makefile @@ -24,12 +24,11 @@ # System specifications F_CRYSTAL = 8000000L -F_CPU = 24000000L -SYSCLOCK = SYSCLK_FREQ_24MHz -FAMILY = f10x -DENSITY = md +F_CPU = 72000000L +SYSCLOCK = SYSCLK_FREQ_72MHz +FAMILY = f37x MEMORY_MODE = flash - +U8G2 = enabled # USB = enabled # Preferred upload command diff --git a/midi2cv/drivers/display.cc b/midi2cv/drivers/display.cc index a719b7b..43ed223 100644 --- a/midi2cv/drivers/display.cc +++ b/midi2cv/drivers/display.cc @@ -1,176 +1,122 @@ #include "display.h" #include "gpio.h" +#include "stmlib/system/system_clock.h" +#include "stmlib/utils/random.h" #include #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 +using namespace stmlib; -#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 +const uint16_t kPinEnable = GPIO_Pin_2; +const uint16_t kPinReset = GPIO_Pin_0; +const uint16_t kPinDataCommand = GPIO_Pin_9; + +u8g2_t u8g2_; + +u8g2_t* Display::u8g2() +{ + return &u8g2_; +} 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); + // init SS/CS/RST GPIO + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); 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_Mode = GPIO_Mode_OUT; + gpio_init.GPIO_OType = GPIO_OType_PP; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; + gpio_init.GPIO_Pin = kPinEnable | kPinReset | kPinDataCommand; GPIO_Init(GPIOB, &gpio_init); + GPIO_WriteBit(GPIOB, kPinEnable, Bit_SET); + + // init AF GPIO + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); + + gpio_init.GPIO_Mode = GPIO_Mode_AF; + gpio_init.GPIO_OType = GPIO_OType_PP; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; + gpio_init.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOB, &gpio_init); + GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_5); + GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5); + GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); + + // init SPI + RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); + SPI_I2S_DeInit(SPI2); + // Initialize SPI 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_CPOL = SPI_CPOL_High; + spi_init.SPI_CPHA = SPI_CPHA_2Edge; 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.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; + spi_init.SPI_FirstBit = SPI_FirstBit_MSB; + spi_init.SPI_CRCPolynomial = 7; 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); + GPIO_WriteBit(GPIOB, kPinReset, Bit_RESET); + asm("nop"); - // 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(); + GPIO_WriteBit(GPIOB, kPinReset, Bit_SET); + InitGLib(); } -void Display::WriteCommand(uint8_t byte) +uint8_t u8x8_stm32_gpio_and_delay(U8X8_UNUSED u8x8_t* u8x8, + U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, + U8X8_UNUSED void* arg_ptr) { - 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 + return 1; } -void Display::WriteData(uint8_t* buffer, uint8_t size) +uint8_t u8x8_byte_4wire_hw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, + void* arg_ptr) { - 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++) - ; + uint8_t* data; + switch (msg) { + case U8X8_MSG_BYTE_SEND: + data = (uint8_t*)arg_ptr; + while (arg_int > 0) { + while (!(SPI2->SR & SPI_SR_TXE)) { + } + SPI_SendData8(SPI2, (uint8_t)*data); + arg_int--; + data++; + } + break; + case U8X8_MSG_BYTE_INIT: + break; + case U8X8_MSG_BYTE_SET_DC: + if (arg_int) + GPIO_WriteBit(GPIOB, kPinDataCommand, Bit_SET); + else + GPIO_WriteBit(GPIOB, kPinDataCommand, Bit_RESET); + break; + case U8X8_MSG_BYTE_START_TRANSFER: + GPIO_WriteBit(GPIOB, kPinEnable, Bit_RESET); + break; + case U8X8_MSG_BYTE_END_TRANSFER: + GPIO_WriteBit(GPIOB, kPinEnable, Bit_SET); + break; + default: + return 0; } - gpio.Write(GPIO_PIN(SS_OLED), Bit_SET); // disable oled spi + return 1; } -void Display::Clear() +void Display::InitGLib() { - 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)); - } + u8g2_Setup_sh1106_128x64_noname_f(&u8g2_, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay); + u8g2_InitDisplay(&u8g2_); + u8g2_SetContrast(&u8g2_, 127); + u8g2_SetPowerSave(&u8g2_, 0); + Random::Seed(42); } diff --git a/midi2cv/drivers/display.h b/midi2cv/drivers/display.h index 717e389..dfce649 100644 --- a/midi2cv/drivers/display.h +++ b/midi2cv/drivers/display.h @@ -3,10 +3,7 @@ #include #include "stmlib/stmlib.h" - -#define DISPLAY_WIDTH 128 -#define DISPLAY_HEIGHT 64 - +#include class Display { @@ -15,15 +12,10 @@ class Display { ~Display() {} void Init(); - void WritePixel(uint8_t x, uint8_t y, bool color); - void Clear(); - void Update(); + u8g2_t* u8g2(); 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]; + void InitGLib(); }; - +extern Display display; #endif diff --git a/midi2cv/drivers/gpio.cc b/midi2cv/drivers/gpio.cc index 37505bf..191df01 100644 --- a/midi2cv/drivers/gpio.cc +++ b/midi2cv/drivers/gpio.cc @@ -4,24 +4,7 @@ void GPIO::Init() { - GPIO_InitTypeDef gpio_init; - - 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); - - gpio_init.GPIO_Pin = PIN_GATE_OUT_1 | PIN_GATE_OUT_2 | PIN_GATE_OUT_3 | PIN_GATE_OUT_4 | PIN_GATE_OUT_5 | PIN_GATE_OUT_6; - 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) { - GPIO_WriteBit(port, pin, static_cast(state)); } diff --git a/midi2cv/midi2cv.cc b/midi2cv/midi2cv.cc index 1265a20..7622ef9 100644 --- a/midi2cv/midi2cv.cc +++ b/midi2cv/midi2cv.cc @@ -2,11 +2,15 @@ #include "drivers/display.h" #include "drivers/gpio.h" +#include "stmlib/system/system_clock.h" +#include "ui.h" using namespace stmlib; GPIO gpio; Display display; +UI ui; +//SystemClock system_clock; // Default interrupt handlers. extern "C" { @@ -38,60 +42,76 @@ void PendSV_Handler() {} // called every 1ms void SysTick_Handler() { -} + IWDG_ReloadCounter(); + system_clock.Tick(); } -void Init(void) +void TIM2_IRQHandler(void) { - SystemInit(); - NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x1000); + if (TIM_GetITStatus(TIM2, TIM_IT_Update) == RESET) { + return; + } + TIM_ClearITPendingBit(TIM2, TIM_IT_Update); + + // this will get called with 192 kHz (foof) + // we want to reduce the amount of times the ui gets polled to 1kHz + // which still is a lot (60fps would be enough tbh) - RCC_APB2PeriphClockCmd( - RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1 | RCC_APB2Periph_USART1, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); + static uint8_t count = 0; + count++; + if (count % 192 == 0) { + ui.Update(); + count = 0; + } +} +} + +void InitTimers(void) +{ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef timer_init; - timer_init.TIM_Period = F_CPU / (48000 * 4) - 1; + timer_init.TIM_Period = F_CPU / (48000 * 4) - 1; // 192 kHz, 48kHz for each audio DAC 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); + //TIM_InternalClockConfig(TIM2); + TIM_TimeBaseInit(TIM2, &timer_init); + TIM_Cmd(TIM2, 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_IRQChannel = TIM2_IRQn; timer_interrupt.NVIC_IRQChannelPreemptionPriority = 0; - timer_interrupt.NVIC_IRQChannelSubPriority = 0; + timer_interrupt.NVIC_IRQChannelSubPriority = 1; timer_interrupt.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&timer_interrupt); + TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); +} +void Init(void) +{ + //SystemInit(); + NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000); + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + IWDG_SetPrescaler(IWDG_Prescaler_16); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); + + system_clock.Init(); SysTick_Config(F_CPU / 8000); - + IWDG_Enable(); gpio.Init(); -// asm("bkpt #0"); + asm("bkpt #0"); display.Init(); + InitTimers(); } 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); + //ui.Update(); } } diff --git a/midi2cv/ui.cc b/midi2cv/ui.cc new file mode 100644 index 0000000..78bb293 --- /dev/null +++ b/midi2cv/ui.cc @@ -0,0 +1,31 @@ +#include "ui.h" +#include "drivers/display.h" +#include "stmlib/utils/random.h" +#include +using namespace stmlib; + +void UI::Update() +{ + u8g2_t* u8g2_ = display.u8g2(); + static float x = 0, y = 16, dx = 1, dy = 1, ddx = .2, ddy = .2; + u8g2_ClearBuffer(u8g2_); + u8g2_SetFont(u8g2_, u8g2_font_6x10_mf); + u8g2_DrawStr(u8g2_, (int)x, (int)y, "send nudes"); + x += dx * ddx; + y += dy * ddy; + if (x >= 70 || x <= 0) { + ddx = Random::GetFloat() / 4.0 + 0.1; + } + if (x >= 70) + dx = -1; + if (x <= 0) + dx = 1; + if (y >= 64 || y <= 7) { + ddy = Random::GetFloat() / 4.0 + 0.1; + } + if (y >= 64) + dy = -1; + if (y <= 7) + dy = 1; + u8g2_SendBuffer(u8g2_); +} diff --git a/midi2cv/ui.h b/midi2cv/ui.h new file mode 100644 index 0000000..dcb8bdd --- /dev/null +++ b/midi2cv/ui.h @@ -0,0 +1,19 @@ +#ifndef MIDI2CV_UI_H +#define MIDI2CV_UI_H + +#include "stmlib/stmlib.h" + +class UI { +public: + UI() {} + ~UI() {} + + void Update(); + +private: + DISALLOW_COPY_AND_ASSIGN(UI); +}; + +extern UI ui; + +#endif