Провел тест. OLED дисплей 128x64 на чипе SSD1306 подключен через I2C, соплями, без пайки и работает с частотой 271+ FPS

Это 2.5+МГц !
Код (ногодрыг через bit banding) крутится на STM32F103C8… 72МГц.

Program Size: Code=228 RO-data=1096 RW-data=0 ZI-data=0

для Keil
#include "stm32f103xb.h"
#include "core_cm3.h"
#include "stdint.h"
#define SDA_PORT  GPIOA
#define SDA_PIN   0
#define SCL_PORT  GPIOB
#define SCL_PIN   0
#define SSD1306_addr 0x3C

int __main (void) __attribute__ ((section ("InRoot$$Sections")));
void __reset (void);
extern uint32_t const __Vector_Table [];
uint32_t const __Vector_Table [] __attribute__ ((section ("RESET")))={
	(uint32_t) &__Vector_Table [2],									//Top of stack
	(uint32_t) &__reset,											//Reset handler
	0x20005000,
	PERIPH_BB_BASE+((uint32_t)&RCC->CR-PERIPH_BASE)*32+RCC_CR_HSION_Pos*4,
	PERIPH_BB_BASE+((uint32_t)&FLASH->ACR-PERIPH_BASE)*32+POS(FLASH_ACR_LATENCY_1)*4,
	(uint32_t)&RCC->CFGR,
	RCC_CFGR_PLLMULL9|RCC_CFGR_PLLSRC|RCC_CFGR_PPRE1_DIV2,
	PERIPH_BB_BASE+((uint32_t)&RCC->CFGR-PERIPH_BASE)*32+POS(RCC_CFGR_SW_1)*4,
	(uint32_t)&SysTick->CTRL,
	0xffffff,
	SysTick_CTRL_CLKSOURCE_Msk+SysTick_CTRL_ENABLE_Msk
};
//!!! Global variables will not be initialized !!!
static const uint8_t init_bytes[]={SSD1306_addr<<1,0x00,0xAE,0xD5,0x80,0xA8,0x3F,0xD3,0x00,0x40,0x8D,0x14,0x20,0x00,0xA1,0xC8,
                                                                0xDA,0x12,0xD9,0xF1,0xDB,0x40,0xA4,0xA6,0xAF};

static const uint8_t test[]={SSD1306_addr<<1,0x40,0xFF,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static void i2c_init(void) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
  if ((&SDA_PORT->CRL == &GPIOA->CRL) || (&SCL_PORT->CRL == &GPIOA->CRL)) *(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&RCC->APB2ENR-PERIPH_BASE)*32+POS(RCC_APB2ENR_IOPAEN)*4)=1;
  if ((&SDA_PORT->CRL == &GPIOB->CRL) || (&SCL_PORT->CRL == &GPIOB->CRL)) *(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&RCC->APB2ENR-PERIPH_BASE)*32+POS(RCC_APB2ENR_IOPBEN)*4)=1;
#pragma clang diagnostic pop
}
static void i2c_start(void) {
	*(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&SDA_PORT->CRL-PERIPH_BASE)*32+SDA_PIN*4*4)=1;
	*(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&SCL_PORT->CRL-PERIPH_BASE)*32+SCL_PIN*4*4)=1;
}
static void i2c_stop(void) {
	*(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&SDA_PORT->CRL-PERIPH_BASE)*32+SDA_PIN*4*4)=1;
	*(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&SCL_PORT->CRL-PERIPH_BASE)*32+SCL_PIN*4*4)=0;
	*(volatile uint32_t*)(PERIPH_BB_BASE+((uint32_t)&SDA_PORT->CRL-PERIPH_BASE)*32+SDA_PIN*4*4)=0;
	__asm volatile (
		"PUSH {r0} \n\t"
		"POP {r0} \n\t"
	);
}
static void __attribute__ ((noinline)) i2c_write_buff(const uint8_t* buff, uint32_t num) {
	uint32_t input=0;
	uint32_t output=1;
	uint32_t sda=(PERIPH_BB_BASE+((uint32_t)&SDA_PORT->CRL-PERIPH_BASE)*32+SDA_PIN*4*4);
	uint32_t scl=(PERIPH_BB_BASE+((uint32_t)&SCL_PORT->CRL-PERIPH_BASE)*32+SCL_PIN*4*4);
	__asm volatile (
	"LDRB r2,[%[buff]],1 \n\t"
	"MVNS r2,r2 \n\t"
	"LSLS r2,24 \n\t"
	"RBIT r2,r2 \n\t"
	"MOVS r3,8 \n\t"
	"NOP \n\t"
	"NOP \n\t"
	"B first_bit \n\t"
	"next_bit: \n\t"
	"NOP \n\t"
	"STR %[input],[%[scl]] \n\t"
	"PUSH {r0} \n\t"
	"POP {r0} \n\t"
	"NOP \n\t"
	"first_bit: \n\t"
	"STR %[output],[%[scl]] \n\t"
	"STR r2,[%[sda]] \n\t"
	"LSRS r2,#1 \n\t"
	"SUBS r3,#1 \n\t"
	"BNE next_bit \n\t"
	"LDRB r2,[%[buff]],1 \n\t"
	"MVNS r2,r2 \n\t"
	"STR %[input],[%[scl]] \n\t"
	"LSLS r2,24 \n\t"
	"RBIT r2,r2 \n\t"
	"MOVS r3,8 \n\t"
	"NOP \n\t"
	"STR %[output],[%[scl]] \n\t"
	"STR %[input],[%[sda]] \n\t"
	"STR %[input],[%[scl]] \n\t"
	"PUSH {r0} \n\t"
	"POP {r0} \n\t"
	"SUBS %[num],#1 \n\t"
	"BNE first_bit \n\t"
	"STR %[output],[%[scl]] \n\t"
	:
	:[buff] "r" (buff),[num] "r" (num), [input] "r" (input), [output] "r" (output),[sda] "r" (sda), [scl] "r" (scl),
	 [sdar] "X" (((uint32_t)&SDA_PORT->IDR-PERIPH_BASE)*32+SDA_PIN*4-(((uint32_t)&SDA_PORT->CRL-PERIPH_BASE)*32+SDA_PIN*4*4)),
	 [sclr] "X" (((uint32_t)&SCL_PORT->IDR-PERIPH_BASE)*32+SCL_PIN*4-(((uint32_t)&SCL_PORT->CRL-PERIPH_BASE)*32+SCL_PIN*4*4))
	:"r2","r3"
	);
}
int __main (void){
	i2c_init();
	i2c_start();
	i2c_write_buff(init_bytes, sizeof(init_bytes));
	i2c_stop();
	i2c_start();
	i2c_write_buff(test, sizeof(test));
	do {
		i2c_write_buff(test+2, sizeof(test)-2);
	} 
	while (1);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
	i2c_stop();
	__asm volatile ("BKPT");
	return 0;
#pragma clang diagnostic pop
}
void __attribute__ ((naked)) __reset(void){
	__asm volatile(
		"POP {r1-r6,r8,r9,r10} \n\t"
		"MOV sp,r1 \n\t"
		"MOVS r1,1 \n\t"
		"STR r1,[r2,%0] \n\t"		//HSE On
		"STR r1,[r3] \n\t"			//FLASH LATENCY = 2
		"wait_HSERDY: \n\t"
		"LDR r0,[r2,%1] \n\t"
		"MOVS r0,r0 \n\t"
		"BEQ wait_HSERDY \n\t"
		"STR r5,[r4] \n\t"			//PLL MULL = 9
		"STR r1,[r2,%2] \n\t"		//PLL On
		"wait_PLLRDY: \n\t"
		"LDR r0,[r2,%3] \n\t"
		"MOVS r0,r0 \n\t"
		"BEQ wait_PLLRDY \n\t"
		"STR r1,[r6] \n\t"			//PLL selected as system clock
		"wait_PLL: \n\t"
		"LDR r0,[r6,%4] \n\t"
		"MOVS r0,r0 \n\t"
		"BEQ wait_PLL \n\t"
		"STR r7,[r2] \n\t"			//HSI Off
		"STR r9,[r8,4] \n\t"
		"STR r10,[r8] \n\t"
		"B __main \n\t"
	:
	: "X" ((RCC_CR_HSEON_Pos-RCC_CR_HSION_Pos)*4),
	  "X" ((RCC_CR_HSERDY_Pos-RCC_CR_HSION_Pos)*4),
	  "X" ((RCC_CR_PLLON_Pos-RCC_CR_HSION_Pos)*4),
	  "X" ((RCC_CR_PLLRDY_Pos-RCC_CR_HSION_Pos)*4),
	  "X" ((POS(RCC_CFGR_SWS_1)-POS(RCC_CFGR_SW_1))*4)
	:
	);
}

В строках 4-7 можно прописать “свои” пины для SDA и SCL - при компиляции все константы изменятся под эти пины.
Строки запуска SysTick 206-207 и константы для него строки 22-24 можно удалить.
Я через SysTick смотрел число тактов с учетом wait states …

1 лайк

а зачем дисплею более 30 кадров в секунду?

Дисплей тут как I2C тестер - картинка бегает = I2C работает.

1 лайк

Я как-то гонял I2C SSD1306 на Arduino Due.
Маленькими шажками дошел до 2 МГц, после чего (2.1 МГц) дисплей зависает намертво. После этого восстанавливается только отключением питания (аппаратный сброс контроллера не помогает). Результат стабильный - 2.0 МГц работает часами, а 2.1 МГц зависает сразу.
Возможно, это зависит от конкретного экземпляра, партии или производителя совместимого оборудования.

на HW I2C ?

Меня снова сочтут душнилой …ну и хамлом заодно, шоп два раза не вставать.
Но я бы хотел узнать цель исследования?
А то уж больно смахивает на анекдот про “…вжжух, сказала японская бензопила, у-уу - сказали суровые сибирские мужики.”

Верите ли, коллеги, но за 33 года за рулём мне никогда не приходило в голову узнать: “А скока она выжимает?”.

Есть ли какое-то оправданное применение недокументированным режимам разгона частоты шины, кроме подросткового любопытства? Может кто пример приведет. Ясно, что из DIY, в продакшене такое прямо запрещено, но я даже “тилки для сэбе” не могу придумать ничего разумного.

1 лайк

Есть стандарт I2C 3.4 и даже 5 МГц.

Есть шампанское Вёв Клико, чёрная икра и самолёт Гольфстрим. К нам-то какое отношение? Про тебя не знаю, но ко мне - никакого.

В ДШ на 1306 аглицким по белому написано, что

  
 Table 13-6 :I2C Interface Timing Characteristics 
  
Symbol Parameter  Min  Typ  Max  Unit 
tcycle  Clock Cycle Time  2.5  -  -  us 

Что как бы намекает на частоту шины 400МГц, усё, што выше - …ну ты знаешь эти слова, а то снова меня за грубость говнить станут. :wink: