Display driver fixes, UI

This commit is contained in:
Jan-Henrik 2019-09-19 16:41:32 +02:00
parent 49632d8cfc
commit f594aa9f43
8 changed files with 195 additions and 205 deletions

View file

@ -4,7 +4,7 @@
using namespace stmlib;
const uint32_t kStartAddress = 0x08001000;
const uint32_t kStartAddress = 0x08008000;
int main(void) {
SystemInit();

View file

@ -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

View file

@ -1,176 +1,122 @@
#include "display.h"
#include "gpio.h"
#include "stmlib/system/system_clock.h"
#include "stmlib/utils/random.h"
#include <stm32f37x_conf.h>
#include <u8g2.h>
// 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)) {
}
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);
SPI_SendData8(SPI2, (uint8_t)*data);
arg_int--;
data++;
}
}
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));
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;
}
return 1;
}
void Display::InitGLib()
{
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);
}

View file

@ -3,10 +3,7 @@
#include <stm32f37x_conf.h>
#include "stmlib/stmlib.h"
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
#include <u8g2.h>
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

View file

@ -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<BitAction>(state));
}

View file

@ -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);
RCC_APB2PeriphClockCmd(
RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1 | RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
// 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)
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();
}
}

31
midi2cv/ui.cc Normal file
View file

@ -0,0 +1,31 @@
#include "ui.h"
#include "drivers/display.h"
#include "stmlib/utils/random.h"
#include <u8g2.h>
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_);
}

19
midi2cv/ui.h Normal file
View file

@ -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