/* * Single-File-Header for SSD1306 emulated I2C interface * 02-12-2025 cnlohr */ #ifndef _SSD1306_I2C_BITBANG_H #define _SSD1306_I2C_BITBANG_H #include // SSD1306 I2C address #ifndef SSD1306_I2C_ADDR #define SSD1306_I2C_ADDR 0x3c #endif #ifndef SSD1306_I2C_BITBANG_SDA #define SSD1306_I2C_BITBANG_SDA PC1 #endif #ifndef SSD1306_I2C_BITBANG_SCL #define SSD1306_I2C_BITBANG_SCL PC2 #endif /* * init just I2C */ void ssd1306_i2c_setup(void) { funGpioInitAll(); funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP); funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1); funPinMode(SSD1306_I2C_BITBANG_SCL, GPIO_CFGLR_OUT_10Mhz_PP); funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1); } #define SDA_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1); #define SCL_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1); #define SDA_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 0); #define SCL_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 0); #define SDA_IN funDigitalRead(SSD1306_I2C_BITBANG_SDA); #define I2CSPEEDBASE 1 #define I2CDELAY_FUNC(x) ADD_N_NOPS(x * 1) // Delay_Us(x*1); static void ssd1306_i2c_sendstart() { SCL_HIGH I2CDELAY_FUNC(1 * I2CSPEEDBASE); SDA_LOW I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_LOW I2CDELAY_FUNC(1 * I2CSPEEDBASE); } void ssd1306_i2c_sendstop() { SDA_LOW I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_LOW I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_HIGH I2CDELAY_FUNC(1 * I2CSPEEDBASE); SDA_HIGH I2CDELAY_FUNC(1 * I2CSPEEDBASE); } // Return nonzero on failure. unsigned char ssd1306_i2c_sendbyte(unsigned char data) { unsigned int i; for (i = 0; i < 8; i++) { I2CDELAY_FUNC(1 * I2CSPEEDBASE); if (data & 0x80) { SDA_HIGH; } else { SDA_LOW; } data <<= 1; I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_HIGH I2CDELAY_FUNC(2 * I2CSPEEDBASE); SCL_LOW } // Immediately after sending last bit, open up DDDR for control. I2CDELAY_FUNC(1 * I2CSPEEDBASE); funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_IN_PUPD); SDA_HIGH I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_HIGH I2CDELAY_FUNC(1 * I2CSPEEDBASE); i = SDA_IN; I2CDELAY_FUNC(1 * I2CSPEEDBASE); SCL_LOW I2CDELAY_FUNC(1 * I2CSPEEDBASE); SDA_HIGH // Maybe? funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP); I2CDELAY_FUNC(1 * I2CSPEEDBASE); return !!i; } uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd) { ssd1306_i2c_sendstart(); int r = ssd1306_i2c_sendbyte(SSD1306_I2C_ADDR << 1); if (r) return r; // ssd1306_i2c_sendstart(); For some reason displays don't want repeated start if (cmd) { if (ssd1306_i2c_sendbyte(0x00)) return 1; // Control } else { if (ssd1306_i2c_sendbyte(0x40)) return 1; // Data } for (int i = 0; i < sz; i++) { if (ssd1306_i2c_sendbyte(data[i])) return 1; } ssd1306_i2c_sendstop(); return 0; } void ssd1306_rst(void) { funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_10Mhz_PP); funDigitalWrite(SSD1306_RST_PIN, 0); Delay_Ms(10); funDigitalWrite(SSD1306_RST_PIN, 1); Delay_Us(10); } #endif