140 lines
3.2 KiB
C
140 lines
3.2 KiB
C
/*
|
|
* Single-File-Header for SSD1306 emulated I2C interface
|
|
* 02-12-2025 cnlohr
|
|
*/
|
|
|
|
#ifndef _SSD1306_I2C_BITBANG_H
|
|
#define _SSD1306_I2C_BITBANG_H
|
|
|
|
#include <string.h>
|
|
|
|
// 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
|