diff --git a/examples/i2c_oled/Makefile b/examples/i2c_oled/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a69a60cb705cb740c687912294bf8713b86298b0 --- /dev/null +++ b/examples/i2c_oled/Makefile @@ -0,0 +1,41 @@ +TARGET:=i2c_oled + +all : flash + +PREFIX:=riscv64-unknown-elf + +GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User + +CH32V003FUN:=../../ch32v003fun +MINICHLINK:=../../minichlink + +CFLAGS:= \ + -g -Os -flto -ffunction-sections \ + -static-libgcc -lgcc \ + -march=rv32ec \ + -mabi=ilp32e \ + -I/usr/include/newlib \ + -I$(CH32V003FUN) \ + -nostdlib \ + -I. -DSTDOUT_UART -Wall + +LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun.ld -Wl,--gc-sections + +SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c + +$(TARGET).elf : $(SYSTEM_C) $(TARGET).c + $(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) + +$(TARGET).bin : $(TARGET).elf + $(PREFIX)-size $^ + $(PREFIX)-objdump -S $^ > $(TARGET).lst + $(PREFIX)-objdump -t $^ > $(TARGET).map + $(PREFIX)-objcopy -O binary $< $(TARGET).bin + $(PREFIX)-objcopy -O ihex $< $(TARGET).hex + +flash : $(TARGET).bin + $(MINICHLINK)/minichlink -w $< flash -b + +clean : + rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map $(TARGET).hex + diff --git a/examples/i2c_oled/i2c_oled.c b/examples/i2c_oled/i2c_oled.c new file mode 100644 index 0000000000000000000000000000000000000000..f5d4286400de90430badc901e78469954214b746 --- /dev/null +++ b/examples/i2c_oled/i2c_oled.c @@ -0,0 +1,37 @@ +/* + * Example for using I2C with graphic OLED + * 03-29-2023 E. Brombaugh + */ + +// Could be defined here, or in the processor defines. +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK + +#include "ch32v003fun.h" +#include <stdio.h> +#include "oled.h" + +int main() +{ + uint32_t count = 0; + + SystemInit48HSI(); + + // start serial @ default 115200bps + SetupUART( UART_BRR ); + Delay_Ms( 100 ); + printf("\r\r\n\ni2c_oled example\n\r"); + + // init i2c and oled + printf("initializing i2c oled..."); + oled_init(); + printf("done.\n\r"); + + printf("looping...\n\r"); + while(1) + { + count++; + count &= 255; + Delay_Ms( 5 ); + } +} diff --git a/examples/i2c_oled/oled.h b/examples/i2c_oled/oled.h new file mode 100644 index 0000000000000000000000000000000000000000..693eac6454b63877d6d0c171931bc845fc73af5e --- /dev/null +++ b/examples/i2c_oled/oled.h @@ -0,0 +1,387 @@ +/* + * Single-File-Header for using I2C OLED + * 03-29-2023 E. Brombaugh + */ + +#ifndef _OLED_H +#define _OLED_H + +#include <stdint.h> +#include <string.h> + +// I2C clock rate +#define I2C_CLKRATE 80000 + +// OLED I2C address +#define OLED_ADDR 0x3c + +// I2C Timeout count +#define TIMEOUT_MAX 100000 + +/* + * init I2C + */ +void i2c_init(void) +{ + uint16_t tempreg; + + // Enable GPIOC and I2C + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + RCC->APB1PCENR |= RCC_APB1Periph_I2C1; + + // PC1/PC2 are SDA/SCL, 50MHz Output PP CNF = 11: Mux OD, MODE = 11: Out 50MHz + GPIOC->CFGLR &= ~(GPIO_CFGLR_MODE1 | GPIO_CFGLR_CNF1 | + GPIO_CFGLR_MODE1 | GPIO_CFGLR_CNF1); + GPIOC->CFGLR |= GPIO_CFGLR_CNF1_1 | GPIO_CFGLR_CNF1_0 | + GPIO_CFGLR_MODE1_1 | GPIO_CFGLR_MODE1_0 | + GPIO_CFGLR_CNF2_1 | GPIO_CFGLR_CNF2_0 | + GPIO_CFGLR_MODE2_1 | GPIO_CFGLR_MODE2_0; + + // Reset I2C1 to init all regs + RCC->APB1PRSTR |= RCC_APB1Periph_I2C1; + RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1; + + // set freq + tempreg = I2C1->CTLR2; + tempreg &= ~I2C_CTLR2_FREQ; + tempreg |= (APB_CLOCK/1000000)&I2C_CTLR2_FREQ; + I2C1->CTLR2 = tempreg; + + // Set clock config + tempreg = 0; +#if (I2C_CLKRATE <= 100000) + // standard mode good to 100kHz + tempreg = (APB_CLOCK/(2*I2C_CLKRATE))&I2C_CKCFGR_CCR; +#else + // fast mode not yet handled here +#endif + I2C1->CKCFGR = tempreg; + + // Enable I2C + I2C1->CTLR1 |= I2C_CTLR1_PE; + + // set ACK mode + I2C1->CTLR1 |= I2C_CTLR1_ACK; + + // is this needed for a master? + I2C1->OADDR1 = 2; +} + +/* + * error handler + */ +void i2c_error(void) +{ + // toggle SWRST bit + I2C1->CTLR1 |= I2C_CTLR1_SWRST; + I2C1->CTLR1 &= ~I2C_CTLR1_SWRST; +} + +// event codes we use +#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */ +#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ +#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ + +/* + * check for 32-bit event codes + */ +uint8_t i2c_chk_evt(uint32_t event_mask) +{ + uint32_t status = (I2C1->STAR2<<16) | I2C1->STAR1; + return (status & event_mask) == event_mask; +} + +/* + * send OLED command byte + */ +void oled_cmd(uint8_t cmd) +{ + int32_t timeout; + + // wait for not busy + timeout = TIMEOUT_MAX; + while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for not busy\n\r"); + i2c_error(); + return; + } + + // Set START condition + I2C1->CTLR1 |= I2C_CTLR1_START; + + // wait for master mode select + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for master mode\n\r"); + i2c_error(); + return; + } + + // send 7-bit address + write flag + I2C1->DATAR = OLED_ADDR<<1; + + // wait for transmit condition + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for transmit mode\n\r"); + i2c_error(); + return; + } + + // wait for TX Empty + timeout = TIMEOUT_MAX; + while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for tx empty\n\r"); + i2c_error(); + return; + } + + // send command flag + I2C1->DATAR = 0; + + // wait for TX Empty + timeout = TIMEOUT_MAX; + while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for tx empty\n\r"); + i2c_error(); + return; + } + + // send command + I2C1->DATAR = cmd; + + // wait for tx complete + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for byte transmitted\n\r"); + i2c_error(); + return; + } + + // set STOP condition + I2C1->CTLR1 |= I2C_CTLR1_STOP; +} + +/* + * send OLED data packet (up to 32 bytes) + */ +void oled_data(uint8_t *data, uint8_t sz) +{ + int32_t timeout; + + // max 32 bytes + sz = sz > 32 ? 32 : sz; + + // wait for not busy + timeout = TIMEOUT_MAX; + while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); + if(timeout==-1) + { + printf("oled_data - timeout waiting for not busy\n\r"); + i2c_error(); + return; + } + + // Set START condition + I2C1->CTLR1 |= I2C_CTLR1_START; + + // wait for master mode select + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); + if(timeout==-1) + { + printf("oled_data - timeout waiting for master mode\n\r"); + i2c_error(); + return; + } + + // send 7-bit address + write flag + I2C1->DATAR = OLED_ADDR<<1; + + // wait for transmit condition + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); + if(timeout==-1) + { + printf("oled_data - timeout waiting for transmit mode\n\r"); + i2c_error(); + return; + } + + // wait for TX Empty + timeout = TIMEOUT_MAX; + while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); + if(timeout==-1) + { + printf("oled_data - timeout waiting for tx empty\n\r"); + i2c_error(); + return; + } + + // send data flag + I2C1->DATAR = 0x40; + + // send data one byte at a time + while(sz--) + { + // wait for TX Empty + timeout = TIMEOUT_MAX; + while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); + if(timeout==-1) + { + printf("oled_cmd - timeout waiting for tx empty\n\r"); + i2c_error(); + return; + } + + // send command + I2C1->DATAR = *data++; + } + + // wait for tx complete + timeout = TIMEOUT_MAX; + while((!i2c_chk_evt(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--)); + if(timeout==-1) + { + printf("oled_data - timeout waiting for byte transmitted\n\r"); + i2c_error(); + return; + } + + // set STOP condition + I2C1->CTLR1 |= I2C_CTLR1_STOP; +} + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_SEGREMAP 0xA0 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA +#define SSD1306_SETVCOMDETECT 0xDB +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 +#define SSD1306_SETMULTIPLEX 0xA8 +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 +#define SSD1306_SETSTARTLINE 0x40 +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COLUMNADDR 0x21 +#define SSD1306_PAGEADDR 0x22 +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 +#define SSD1306_CHARGEPUMP 0x8D +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +#define SSD1306_TERMINATE_CMDS 0xFF + +/* choose VCC mode */ +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +//#define vccstate SSD1306_EXTERNALVCC +#define vccstate SSD1306_SWITCHCAPVCC + +// OLED initialization commands for 64x32 +uint8_t oled_init_array[] = +{ + SSD1306_DISPLAYOFF, // 0xAE + SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 + 0x80, // the suggested ratio 0x80 + SSD1306_SETMULTIPLEX, // 0xA8 + 0x1F, // different for tiny + SSD1306_SETDISPLAYOFFSET, // 0xD3 + 0x00, // no offset + SSD1306_SETSTARTLINE | 0x0, // 0x40 | line + SSD1306_CHARGEPUMP, // 0x8D + 0x14, // enable? + SSD1306_MEMORYMODE, // 0x20 + 0x00, // 0x0 act like ks0108 + SSD1306_SEGREMAP | 0x1, // 0xA0 | bit + SSD1306_COMSCANDEC, + SSD1306_SETCOMPINS, // 0xDA + 0x12, + SSD1306_SETCONTRAST, // 0x81 + 0x8F, + SSD1306_SETPRECHARGE, // 0xd9 + 0xF1, + SSD1306_SETVCOMDETECT, // 0xDB + 0x40, + SSD1306_DISPLAYALLON_RESUME, // 0xA4 + SSD1306_NORMALDISPLAY, // 0xA6 + SSD1306_DISPLAYON, // 0xAF --turn on oled panel + SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end +}; +#define OLED_W 64 +#define OLED_H 32 + +// the display buffer +uint8_t oled_buffer[128*32/8]; + +/* + * set the buffer to a color + */ +void oled_setbuf(uint8_t color) +{ + memset(oled_buffer, color ? 0xFF : 0x00, sizeof(oled_buffer)); +} + +/* + * Send the frame buffer + */ +void oled_refresh(void) +{ + uint16_t i; + + oled_cmd(SSD1306_COLUMNADDR); + oled_cmd(0); // Column start address (0 = reset) + oled_cmd(OLED_W-1); // Column end address (127 = reset) + + oled_cmd(SSD1306_PAGEADDR); + oled_cmd(4); // Page start address (0 = reset) + oled_cmd(7); // Page end address + + for (i=0;i<sizeof(oled_buffer);i+=16) + { + /* send a block of data */ + oled_data(&oled_buffer[i], 16); + } +} + +/* + * initialize act + */ +void oled_init( void ) +{ + // initialize port + i2c_init(); + + // initialize OLED + uint8_t *cmd_list = oled_init_array; + while(*cmd_list != SSD1306_TERMINATE_CMDS) + { + oled_cmd(*cmd_list++); + } + printf("oled_init - list complete\n\r"); + + // clear display + oled_setbuf(0); + oled_refresh(); +} + +#endif