diff --git a/F1:F103/I2C_tiny/I2Ctiny.bin b/F1:F103/I2C_tiny/I2Ctiny.bin
new file mode 100755
index 0000000..c91e96c
Binary files /dev/null and b/F1:F103/I2C_tiny/I2Ctiny.bin differ
diff --git a/F1:F103/I2C_tiny/I2Ctiny.cflags b/F1:F103/I2C_tiny/I2Ctiny.cflags
new file mode 100644
index 0000000..68d5165
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.cflags
@@ -0,0 +1 @@
+-std=c17
\ No newline at end of file
diff --git a/F1:F103/I2C_tiny/I2Ctiny.config b/F1:F103/I2C_tiny/I2Ctiny.config
new file mode 100644
index 0000000..55bd3a6
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.config
@@ -0,0 +1,4 @@
+#define EBUG
+#define STM32F1
+#define STM32F103x6
+#define STM32F10X_LD
diff --git a/F1:F103/I2C_tiny/I2Ctiny.creator b/F1:F103/I2C_tiny/I2Ctiny.creator
new file mode 100644
index 0000000..e94cbbd
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.creator
@@ -0,0 +1 @@
+[General]
diff --git a/F1:F103/I2C_tiny/I2Ctiny.creator.user b/F1:F103/I2C_tiny/I2Ctiny.creator.user
new file mode 100644
index 0000000..9e47618
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.creator.user
@@ -0,0 +1,183 @@
+
+
+
+
+
+ EnvironmentId
+ {7bd84e39-ca37-46d3-be9d-99ebea85bc0d}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ KOI8-R
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ 0
+ false
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ false
+ 1
+ true
+ false
+ true
+ *.md, *.MD, Makefile
+ false
+ true
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+
+ false
+
+
+ 0
+ true
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 8
+ true
+
+
+
+ true
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop
+ Desktop
+ {65a14f9e-e008-4c1b-89df-4eaa4774b6e3}
+ 0
+ 0
+ 0
+
+ /Big/Data/00__Electronics/STM32/F1-nolib/I2C_tiny
+
+
+
+ all
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+
+ clean
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Default
+ GenericProjectManager.GenericBuildConfiguration
+
+ 1
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+ true
+ true
+ 0
+ true
+
+
+ 2
+
+ false
+ -e cpu-cycles --call-graph dwarf,4096 -F 250
+
+ ProjectExplorer.CustomExecutableRunConfiguration
+
+ false
+ true
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/F1:F103/I2C_tiny/I2Ctiny.cxxflags b/F1:F103/I2C_tiny/I2Ctiny.cxxflags
new file mode 100644
index 0000000..6435dfc
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.cxxflags
@@ -0,0 +1 @@
+-std=c++17
\ No newline at end of file
diff --git a/F1:F103/I2C_tiny/I2Ctiny.files b/F1:F103/I2C_tiny/I2Ctiny.files
new file mode 100644
index 0000000..e3c0509
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.files
@@ -0,0 +1,12 @@
+hardware.c
+hardware.h
+i2c.c
+i2c.h
+i2ctiny.h
+main.c
+usb_lib.c
+usb_lib.h
+usbdev.c
+usbdev.h
+usbhw.c
+usbhw.h
diff --git a/F1:F103/I2C_tiny/I2Ctiny.includes b/F1:F103/I2C_tiny/I2Ctiny.includes
new file mode 100644
index 0000000..06d1130
--- /dev/null
+++ b/F1:F103/I2C_tiny/I2Ctiny.includes
@@ -0,0 +1,6 @@
+.
+../inc
+../inc/Fx
+../inc/cm
+../inc/ld
+../inc/startup
diff --git a/F1:F103/I2C_tiny/Makefile b/F1:F103/I2C_tiny/Makefile
new file mode 100644
index 0000000..a0d7d1c
--- /dev/null
+++ b/F1:F103/I2C_tiny/Makefile
@@ -0,0 +1,9 @@
+BINARY := I2Ctiny
+# MCU code
+MCU ?= F103x6
+# change this linking script depending on particular MCU model,
+LDSCRIPT ?= stm32f103x6.ld
+DEFINES := -DSTM32F10X_LD
+
+include ../makefile.f1
+include ../../makefile.stm32
diff --git a/F1:F103/I2C_tiny/Readme b/F1:F103/I2C_tiny/Readme
new file mode 100644
index 0000000..4cb730f
--- /dev/null
+++ b/F1:F103/I2C_tiny/Readme
@@ -0,0 +1 @@
+Scan I2C bus, read/write registers
diff --git a/F1:F103/I2C_tiny/hardware.c b/F1:F103/I2C_tiny/hardware.c
new file mode 100644
index 0000000..a7a900e
--- /dev/null
+++ b/F1:F103/I2C_tiny/hardware.c
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the I2Cscan project.
+ * Copyright 2020 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "hardware.h"
+
+static inline void gpio_setup(){
+ // Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG
+ RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
+ // turn off SWJ/JTAG
+// AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE;
+ AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15
+ // Set led as opendrain output
+ GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW);
+ // USB pullup (PA15) - pushpull output
+ GPIOA->CRH = CRH(15, CNF_PPOUTPUT|MODE_SLOW);
+}
+
+void hw_setup(){
+ gpio_setup();
+}
+
+#ifndef EBUG
+void iwdg_setup(){
+ uint32_t tmout = 16000000;
+ RCC->CSR |= RCC_CSR_LSION;
+ while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */
+ IWDG->KR = IWDG_START;
+ IWDG->KR = IWDG_WRITE_ACCESS;
+ IWDG->PR = IWDG_PR_PR_1;
+ IWDG->RLR = 1250;
+ tmout = 16000000;
+ while(IWDG->SR){if(--tmout == 0) break;}
+ IWDG->KR = IWDG_REFRESH;
+}
+#endif
+
diff --git a/F1:F103/I2C_tiny/hardware.h b/F1:F103/I2C_tiny/hardware.h
new file mode 100644
index 0000000..d4f123f
--- /dev/null
+++ b/F1:F103/I2C_tiny/hardware.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the I2Cscan project.
+ * Copyright 2020 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#ifndef __HARDWARE_H__
+#define __HARDWARE_H__
+
+#include "stm32f1.h"
+
+// LED0 - PC13 (bluepill), blinking each second
+#define LED0_port GPIOC
+#define LED0_pin (1<<13)
+
+// USB pullup (not present in bluepill, should be soldered) - PA15
+#define USBPU_port GPIOA
+#define USBPU_pin (1<<15)
+#define USBPU_ON() pin_set(USBPU_port, USBPU_pin)
+#define USBPU_OFF() pin_clear(USBPU_port, USBPU_pin)
+
+#define LED_blink(x) pin_toggle(x ## _port, x ## _pin)
+#define LED_on(x) pin_clear(x ## _port, x ## _pin)
+#define LED_off(x) pin_set(x ## _port, x ## _pin)
+
+void hw_setup();
+void iwdg_setup();
+
+
+#endif // __HARDWARE_H__
diff --git a/F1:F103/I2C_tiny/i2c.c b/F1:F103/I2C_tiny/i2c.c
new file mode 100644
index 0000000..4831473
--- /dev/null
+++ b/F1:F103/I2C_tiny/i2c.c
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the I2Cscan project.
+ * Copyright 2021 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "hardware.h"
+#include "i2c.h"
+
+#ifdef EBUG
+#undef DBG
+#define DBG(x)
+#endif
+
+extern volatile uint32_t Tms;
+
+// current addresses for read/write (should be set with i2c_set_addr7)
+static uint8_t addr7r = 0, addr7w = 0;
+
+/*
+ * PB10/PB6 - I2C_SCL, PB11/PB7 - I2C_SDA or remap @ PB8 & PB9
+ */
+void i2c_setup(){
+ RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN;
+ I2C1->CR1 = I2C_CR1_SWRST;
+ I2C1->SR1 = 0;
+ RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
+ GPIOB->CRL = (GPIOB->CRL & ~(GPIO_CRL_CNF6 | GPIO_CRL_CNF7)) |
+ CRL(6, CNF_AFOD | MODE_NORMAL) | CRL(7, CNF_AFOD | MODE_NORMAL);
+ RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
+ I2C1->CR2 = 8; // FREQR=8MHz, T=125ns
+ I2C1->TRISE = 9; // (9-1)*125 = 1mks
+ I2C1->CCR = 40; // normal mode, 8MHz/2/40 = 100kHz
+ I2C1->CR1 = I2C_CR1_PE; // enable periph
+}
+
+void i2c_set_addr7(uint8_t addr){
+ addr7w = addr << 1;
+ addr7r = addr7w | 1;
+}
+
+// wait for event evt no more than 2 ms
+#define I2C_WAIT(evt) do{ register uint32_t wait4 = Tms + 2; \
+ while(Tms < wait4 && !(evt)) IWDG->KR = IWDG_REFRESH; \
+ if(!(evt)){ret = I2C_TMOUT; goto eotr;}}while(0)
+// wait for !busy
+#define I2C_LINEWAIT() do{ register uint32_t wait4 = Tms + 2; \
+ while(Tms < wait4 && (I2C1->SR2 & I2C_SR2_BUSY)) IWDG->KR = IWDG_REFRESH; \
+ if(I2C1->SR2 & I2C_SR2_BUSY){I2C1->CR1 |= I2C_CR1_SWRST; return I2C_LINEBUSY;}\
+ }while(0)
+
+// start writing
+static i2c_status i2c_7bit_startw(){
+ i2c_status ret = I2C_LINEBUSY;
+ if(I2C1->CR1 != I2C_CR1_PE) i2c_setup();
+ if(I2C1->SR1) I2C1->SR1 = 0; // clear NACK and other problems
+ (void) I2C1->SR2;
+ I2C_LINEWAIT();
+ DBG("linew\n");
+ I2C1->CR1 |= I2C_CR1_START; // generate start sequence
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_SB); // wait for SB
+ DBG("SB\n");
+ (void) I2C1->SR1; // clear SB
+ I2C1->DR = addr7w; // set address
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_ADDR); // wait for ADDR flag (timeout @ NACK)
+ DBG("ADDR\n");
+ if(I2C1->SR1 & I2C_SR1_AF){ // NACK
+ return I2C_NACK;
+ }
+ DBG("ACK\n");
+ (void) I2C1->SR2; // clear ADDR
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+/**
+ * send one byte in 7bit address mode
+ * @param data - data to write
+ * @param stop - ==1 to send stop event
+ * @return status
+ */
+i2c_status i2c_7bit_send_onebyte(uint8_t data, uint8_t stop){
+ i2c_status ret = i2c_7bit_startw();
+ if(ret != I2C_OK){
+ I2C1->CR1 |= I2C_CR1_STOP;
+ goto eotr;
+ }
+ I2C1->DR = data; // init data send register
+ DBG("TxE\n");
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_TXE); // wait for TxE (timeout when NACK)
+ ret = I2C_OK;
+ DBG("OK\n");
+ if(stop){
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_BTF); // wait for BTF
+ DBG("BTF\n");
+ }
+eotr:
+ if(stop){
+ I2C1->CR1 |= I2C_CR1_STOP; // generate stop event
+ }
+ return ret;
+}
+
+// send data array
+i2c_status i2c_7bit_send(const uint8_t *data, int datalen){
+ i2c_status ret = i2c_7bit_startw();
+ if(ret != I2C_OK){
+ DBG("NACK!\n");
+ I2C1->CR1 |= I2C_CR1_STOP;
+ goto eotr;
+ }
+ for(int i = 0; i < datalen; ++i){
+ I2C1->DR = data[i];
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_TXE);
+ }
+ DBG("GOOD\n");
+ ret = I2C_OK;
+ if(datalen) I2C_WAIT(I2C1->SR1 & I2C_SR1_BTF);
+eotr:
+ I2C1->CR1 |= I2C_CR1_STOP;
+ return ret;
+}
+
+i2c_status i2c_7bit_receive_onebyte(uint8_t *data, uint8_t stop){
+ i2c_status ret = I2C_LINEBUSY;
+ //I2C_LINEWAIT();
+ I2C1->CR1 |= I2C_CR1_START; // generate start sequence
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_SB); // wait for SB
+ DBG("Rx SB\n");
+ (void) I2C1->SR1; // clear SB
+ I2C1->DR = addr7r; // set address
+ DBG("Rx addr\n");
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_ADDR); // wait for ADDR flag
+ DBG("Rx ack\n");
+ I2C1->CR1 &= ~I2C_CR1_ACK; // clear ACK
+ if(I2C1->SR1 & I2C_SR1_AF){ // NACK
+ DBG("Rx nak\n");
+ ret = I2C_NACK;
+ goto eotr;
+ }
+ (void) I2C1->SR2; // clear ADDR
+ DBG("Rx stop\n");
+ if(stop) I2C1->CR1 |= I2C_CR1_STOP; // program STOP
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_RXNE); // wait for RxNE
+ DBG("Rx OK\n");
+ *data = I2C1->DR; // read data & clear RxNE
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+i2c_status i2c_7bit_receive_twobytes(uint8_t *data){
+ i2c_status ret = I2C_LINEBUSY;
+ //I2C_LINEWAIT();
+ I2C1->CR1 |= I2C_CR1_START | I2C_CR1_POS | I2C_CR1_ACK; // generate start sequence, set pos & ack
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_SB); // wait for SB
+ DBG("2 Rx sb\n");
+ (void) I2C1->SR1; // clear SB
+ I2C1->DR = addr7r; // set address
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_ADDR); // wait for ADDR flag
+ DBG("2 ADDR\n");
+ if(I2C1->SR1 & I2C_SR1_AF){ // NACK
+ ret = I2C_NACK;
+ goto eotr;
+ }
+ DBG("2 ACK\n");
+ (void) I2C1->SR2; // clear ADDR
+ I2C1->CR1 &= ~I2C_CR1_ACK; // clear ACK
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_BTF); // wait for BTF
+ DBG("2 BTF\n");
+ I2C1->CR1 |= I2C_CR1_STOP; // program STOP
+ *data++ = I2C1->DR; *data = I2C1->DR; // read data & clear RxNE
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+
+
+// receive any amount of bytes
+
+i2c_status i2c_7bit_receive(uint8_t *data, uint16_t nbytes){
+ if(nbytes == 0) return I2C_HWPROBLEM;
+ I2C1->SR1 = 0; // clear previous NACK flag & other error flags
+ if(nbytes == 1) return i2c_7bit_receive_onebyte(data, 1);
+ else if(nbytes == 2) return i2c_7bit_receive_twobytes(data);
+ i2c_status ret = I2C_LINEBUSY;
+ //I2C_LINEWAIT();
+ I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK; // generate start sequence, set pos & ack
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_SB); // wait for SB
+ DBG("got SB\n");
+ (void) I2C1->SR1; // clear SB
+ I2C1->DR = addr7r; // set address
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_ADDR); // wait for ADDR flag
+ DBG("send addr\n");
+ if(I2C1->SR1 & I2C_SR1_AF){ // NACK
+ DBG("NACKed\n");
+ ret = I2C_NACK;
+ goto eotr;
+ }
+ DBG("ACKed\n");
+ (void) I2C1->SR2; // clear ADDR
+ for(uint16_t x = nbytes - 3; x > 0; --x){
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_RXNE); // wait next byte
+ *data++ = I2C1->DR; // get data
+ }
+ DBG("three left\n");
+ // three bytes remain to be read
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_RXNE); // wait dataN-2
+ DBG("dataN-2\n");
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_BTF); // wait for BTF
+ DBG("BTF\n");
+ I2C1->CR1 &= ~I2C_CR1_ACK; // clear ACK
+ *data++ = I2C1->DR; // read dataN-2
+ I2C1->CR1 |= I2C_CR1_STOP; // program STOP
+ *data++ = I2C1->DR; // read dataN-1
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_RXNE); // wait next byte
+ *data = I2C1->DR; // read dataN
+ DBG("got it\n");
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+#if 0
+i2c_status i2c_start(){
+ i2c_status ret = I2C_LINEBUSY;
+ I2C_LINEWAIT();
+ I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK; // generate start sequence, set pos & ack
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_SB); // wait for SB
+ (void) I2C1->SR1; // clear SB
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+i2c_status i2c_sendaddr(uint8_t addr, uint8_t nread){
+ addr <<= 1;
+ if(nread) addr |= 1;
+ i2c_status ret = I2C_LINEBUSY;
+ I2C1->DR = addr7; // set address
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_ADDR); // wait for ADDR flag
+ if(I2C1->SR1 & I2C_SR1_AF){ // NACK
+ ret = I2C_NACK;
+ goto eotr;
+ }
+ if(nread == 1) I2C1->CR1 &= ~I2C_CR1_ACK; // clear ACK
+ else if(nread >= 2) I2C1->CR1 |= I2C_CR1_ACK;
+ (void) I2C1->SR2; // clear ADDR
+ if(nread == 1) I2C1->CR1 |= I2C_CR1_STOP;
+eotr:
+ return ret;
+}
+
+i2c_status i2c_sendbyte(uint8_t data){
+ i2c_status ret = I2C_LINEBUSY;
+ I2C1->DR = data; // init data send register
+ DBG("TxE\n");
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_TXE); // wait for TxE (timeout when NACK)
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+
+i2c_status i2c_readbyte(uint8_t *data){
+ i2c_status ret = I2C_LINEBUSY;
+ I2C_WAIT(I2C1->SR1 & I2C_SR1_RXNE); // wait for RxNE
+ *data = I2C1->DR; // read data & clear RxNE
+ ret = I2C_OK;
+eotr:
+ return ret;
+}
+#endif
diff --git a/F1:F103/I2C_tiny/i2c.h b/F1:F103/I2C_tiny/i2c.h
new file mode 100644
index 0000000..16ff85f
--- /dev/null
+++ b/F1:F103/I2C_tiny/i2c.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the I2Ctiny project.
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+typedef enum{
+ I2C_OK = 0,
+ I2C_LINEBUSY,
+ I2C_TMOUT,
+ I2C_NOADDR,
+ I2C_NACK,
+ I2C_HWPROBLEM,
+} i2c_status;
+
+void i2c_setup();
+void i2c_set_addr7(uint8_t addr);
+i2c_status i2c_7bit_send_onebyte(uint8_t data, uint8_t stop);
+i2c_status i2c_7bit_send(const uint8_t *data, int datalen);
+i2c_status i2c_7bit_receive_onebyte(uint8_t *data, uint8_t stop);
+i2c_status i2c_7bit_receive_twobytes(uint8_t *data);
+i2c_status i2c_7bit_receive(uint8_t *data, uint16_t nbytes);
+
diff --git a/F1:F103/I2C_tiny/i2ctiny.h b/F1:F103/I2C_tiny/i2ctiny.h
new file mode 100644
index 0000000..28e29ec
--- /dev/null
+++ b/F1:F103/I2C_tiny/i2ctiny.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the I2Ctiny project.
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* commands from USB, must e.g. match command ids in kernel driver */
+#define CMD_ECHO 0
+#define CMD_GET_FUNC 1
+#define CMD_SET_DELAY 2
+#define CMD_GET_STATUS 3
+
+#define CMD_I2C_IO 4
+#define CMD_I2C_BEGIN 1 // flag fo I2C_IO
+#define CMD_I2C_END 2 // flag fo I2C_IO
+
+/* linux kernel flags */
+#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
+#define I2C_M_RD 0x01
+#define I2C_M_NOSTART 0x4000
+#define I2C_M_REV_DIR_ADDR 0x2000
+#define I2C_M_IGNORE_NAK 0x1000
+#define I2C_M_NO_RD_ACK 0x0800
+
+/* To determine what functionality is present */
+#define I2C_FUNC_I2C 0x00000001
+#define I2C_FUNC_10BIT_ADDR 0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_HWPEC_CALC 0x00000008 /* SMBus 2.0 */
+#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */
+#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC 0x00000800 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC 0x00001000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_PROC_CALL_PEC 0x00002000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC 0x00004000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK 0x00010000
+#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
+#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
+#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
+#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000 /* I2C-like block xfer */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC 0x40000000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */
+
+#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \
+I2C_FUNC_SMBUS_WRITE_BYTE
+#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA
+#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
+#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
+
+#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \
+ I2C_FUNC_SMBUS_BYTE | \
+ I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | \
+ I2C_FUNC_SMBUS_PROC_CALL | \
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
+ I2C_FUNC_SMBUS_I2C_BLOCK
+
+#define STATUS_IDLE 0
+#define STATUS_ADDRESS_ACK 1
+#define STATUS_ADDRESS_NACK 2
diff --git a/F1:F103/I2C_tiny/main.c b/F1:F103/I2C_tiny/main.c
new file mode 100644
index 0000000..1efd4c2
--- /dev/null
+++ b/F1:F103/I2C_tiny/main.c
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the I2Cscan project.
+ * Copyright 2020 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "i2c.h"
+#include "hardware.h"
+#include "usb_lib.h"
+
+#define USBBUFSZ 127
+
+volatile uint32_t Tms = 0;
+
+/* Called when systick fires */
+void sys_tick_handler(void){
+ ++Tms;
+}
+
+#define STRLEN (256)
+
+int main(void){
+ uint32_t lastT = 0;
+ StartHSE();
+ hw_setup();
+ USBPU_OFF();
+ SysTick_Config(72000);
+ USB_setup();
+ i2c_setup();
+#ifndef EBUG
+ iwdg_setup();
+#endif
+ USBPU_ON();
+
+ while (1){
+ IWDG->KR = IWDG_REFRESH; // refresh watchdog
+ if(lastT > Tms || Tms - lastT > 499){
+ LED_blink(LED0);
+ lastT = Tms;
+ }
+ }
+ return 0;
+}
diff --git a/F1:F103/I2C_tiny/openocd.cfg b/F1:F103/I2C_tiny/openocd.cfg
new file mode 100644
index 0000000..cae485c
--- /dev/null
+++ b/F1:F103/I2C_tiny/openocd.cfg
@@ -0,0 +1,6 @@
+# STM32F103x6
+
+set FLASH_SIZE 0x8000
+
+source [find interface/stlink-v2-1.cfg]
+source [find target/stm32f1x.cfg]
diff --git a/F1:F103/I2C_tiny/usb_lib.c b/F1:F103/I2C_tiny/usb_lib.c
new file mode 100644
index 0000000..5a5f087
--- /dev/null
+++ b/F1:F103/I2C_tiny/usb_lib.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "usbdev.h"
+#include "usb_lib.h"
+#include "usbhw.h"
+
+static ep_t endpoints[STM32ENDPOINTS];
+
+static uint16_t USB_Addr = 0;
+static uint8_t ep0databuf[EP0DATABUF_SIZE], setupdatabuf[EP0DATABUF_SIZE];
+static uint8_t ep0dbuflen = 0;
+static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
+volatile uint8_t usbON = 0; // device is configured and active
+
+// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
+#define bcdUSB_L 0x10
+#define bcdUSB_H 0x01
+#define bDeviceClass 0xff
+#define bDeviceSubClass 0x00
+#define bDeviceProtocol 0x00
+#define bNumConfigurations 1
+
+static const uint8_t USB_DeviceDescriptor[] = {
+ 18, // bLength
+ 0x01, // bDescriptorType - Device descriptor
+ bcdUSB_L, // bcdUSB_L
+ bcdUSB_H, // bcdUSB_H
+ bDeviceClass, // bDeviceClass - USB_COMM
+ bDeviceSubClass, // bDeviceSubClass
+ bDeviceProtocol, // bDeviceProtocol
+ USB_EP0BUFSZ, // bMaxPacketSize
+ 0x03, // idVendor_L: VID=0x0403, PID=0xc631
+ 0x04, // idVendor_H
+ 0x31, // idProduct_L
+ 0xc6, // idProduct_H
+ 0x05, // bcdDevice_Ver_L
+ 0x02, // bcdDevice_Ver_H
+ iMANUFACTURER_DESCR, // iManufacturer
+ iPRODUCT_DESCR, // iProduct
+ iSERIAL_DESCR, // iSerialNumber
+ bNumConfigurations // bNumConfigurations
+};
+
+static const uint8_t USB_DeviceQualifierDescriptor[] = {
+ 10, //bLength
+ 0x06, // bDescriptorType - Device qualifier
+ bcdUSB_L, // bcdUSB_L
+ bcdUSB_H, // bcdUSB_H
+ bDeviceClass, // bDeviceClass
+ bDeviceSubClass, // bDeviceSubClass
+ bDeviceProtocol, // bDeviceProtocol
+ USB_EP0BUFSZ, // bMaxPacketSize0
+ bNumConfigurations, // bNumConfigurations
+ 0x00 // Reserved
+};
+
+static const uint8_t USB_ConfigDescriptor[] = {
+ /*Configuration Descriptor*/
+ 0x09, /* bLength: Configuration Descriptor size */
+ 0x02, /* bDescriptorType: Configuration */
+ 39, /* wTotalLength:no of returned bytes */
+ 0x00,
+ 0x01, /* bNumInterfaces: 1 interface */
+ 0x01, /* bConfigurationValue: Configuration value */
+ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
+ 0x80, /* bmAttributes - Bus powered */
+ 0x32, /* MaxPower 100 mA */
+
+ /*---------------------------------------------------------------------------*/
+
+ /*Interface Descriptor */
+ 0x09, /* bLength: Interface Descriptor size */
+ 0x04, /* bDescriptorType: Interface */
+ 0x00, /* bInterfaceNumber: Number of Interface */
+ 0x00, /* bAlternateSetting: Alternate setting */
+ 0x01, /* bNumEndpoints: only 1 used */
+ 0x00, /* bInterfaceClass */
+ 0x00, /* bInterfaceSubClass */
+ 0x00, /* bInterfaceProtocol */
+ iINTERFACE_DESCR, /* iInterface: */
+///////////////////////////////////////////////////
+ /*Endpoint 1 Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ 0x05, /* bDescriptorType: Endpoint */
+ 0x81, /* bEndpointAddress IN1 */
+ 0x03, /* bmAttributes: Interrupt */
+ USB_EP1BUFSZ, /* wMaxPacketSize LO: */
+ 0x00, /* wMaxPacketSize HI: */
+ 0x20, /* bInterval: */
+};
+
+_USB_LANG_ID_(LD, LANG_US);
+_USB_STRING_(SD, u"0.0.1");
+_USB_STRING_(MD, u"eddy@sao");
+_USB_STRING_(PD, u"USB-I2C Controller");
+_USB_STRING_(ID, u"i2c-stm32f1-usb");
+static void const *StringDescriptor[iDESCR_AMOUNT] = {
+ [iLANGUAGE_DESCR] = &LD,
+ [iMANUFACTURER_DESCR] = &MD,
+ [iPRODUCT_DESCR] = &PD,
+ [iSERIAL_DESCR] = &SD,
+ [iINTERFACE_DESCR] = &ID
+};
+
+static void wr0(const uint8_t *buf, uint16_t size){
+ if(setup_packet->wLength < size) size = setup_packet->wLength; // shortened request
+ if(size < endpoints[0].txbufsz){
+ EP_WriteIRQ(0, buf, size);
+ return;
+ }
+ while(size){
+ uint16_t l = size;
+ if(l > endpoints[0].txbufsz) l = endpoints[0].txbufsz;
+ EP_WriteIRQ(0, buf, l);
+ buf += l;
+ size -= l;
+ uint8_t needzlp = (l == endpoints[0].txbufsz) ? 1 : 0;
+ if(size || needzlp){ // send last data buffer
+ uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]);
+ // keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
+ USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
+ ^ USB_EPnR_STAT_TX;
+ uint32_t ctr = 1000000;
+ while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
+ if((USB->ISTR & USB_ISTR_CTR) == 0){
+ return;
+ }
+ if(needzlp) EP_WriteIRQ(0, (uint8_t*)0, 0);
+ }
+ }
+}
+
+static inline void get_descriptor(){
+ uint8_t descrtype = setup_packet->wValue >> 8,
+ descridx = setup_packet->wValue & 0xff;
+ switch(descrtype){
+ case DEVICE_DESCRIPTOR:
+ wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
+ break;
+ case CONFIGURATION_DESCRIPTOR:
+ wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
+ break;
+ case STRING_DESCRIPTOR:
+ if(descridx < iDESCR_AMOUNT) wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]));
+ else EP_WriteIRQ(0, (uint8_t*)0, 0);
+ break;
+ case DEVICE_QUALIFIER_DESCRIPTOR:
+ wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint16_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
+static inline void std_d2h_req(){
+ uint16_t st = 0;
+ switch(setup_packet->bRequest){
+ case GET_DESCRIPTOR:
+ get_descriptor();
+ break;
+ case GET_STATUS:
+ EP_WriteIRQ(0, (uint8_t *)&st, 2); // send status: Bus Powered
+ break;
+ case GET_CONFIGURATION:
+ EP_WriteIRQ(0, (uint8_t*)&configuration, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void std_h2d_req(){
+ switch(setup_packet->bRequest){
+ case SET_ADDRESS:
+ // new address will be assigned later - after acknowlegement or request to host
+ USB_Addr = setup_packet->wValue;
+ break;
+ case SET_CONFIGURATION:
+ // Now device configured
+ configuration = setup_packet->wValue;
+ set_configuration(configuration);
+ usbON = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+void WEAK usb_standard_request(){
+ uint8_t recipient = REQUEST_RECIPIENT(setup_packet->bmRequestType);
+ uint8_t dev2host = (setup_packet->bmRequestType & 0x80) ? 1 : 0;
+ switch(recipient){
+ case REQ_RECIPIENT_DEVICE:
+ if(dev2host){
+ std_d2h_req();
+ return;
+ }else{
+ std_h2d_req();
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ }
+ break;
+ case REQ_RECIPIENT_INTERFACE:
+ //EP_WriteIRQ(0, (const uint8_t*)"epi", 4);
+ break;
+ case REQ_RECIPIENT_ENDPOINT:
+ if(setup_packet->bRequest == CLEAR_FEATURE){
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ }else{
+ //EP_WriteIRQ(0, (const uint8_t*)"epr", 4);
+ }
+ break;
+ default:
+ //EP_WriteIRQ(0, (const uint8_t*)"epd", 4);
+ break;
+ }
+}
+
+void WEAK usb_class_request(config_pack_t _U_ *req, uint8_t _U_ *data, unsigned int _U_ datalen){
+ EP_WriteIRQ(0, (const uint8_t*)"cls", 4);
+}
+
+void WEAK usb_vendor_request(config_pack_t _U_ *packet, uint8_t _U_ *data, unsigned int _U_ datalen){
+ EP_WriteIRQ(0, (const uint8_t*)"vnd", 4);
+}
+
+/*
+bmRequestType: 76543210
+7 direction: 0 - host->device, 1 - device->host
+65 type: 0 - standard, 1 - class, 2 - vendor
+4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
+*/
+/**
+ * Endpoint0 (control) handler
+ */
+static void EP0_Handler(){
+ uint16_t epstatus = USB->EPnR[0]; // EP0R on input -> return this value after modifications
+ uint8_t reqtype = REQUEST_TYPE(setup_packet->bmRequestType);
+ //char x[] = "EP0Hx";
+ int rxflag = RX_FLAG(epstatus);
+ if(rxflag && SETUP_FLAG(epstatus)){
+ switch(reqtype){
+ case REQ_TYPE_STANDARD:
+ usb_standard_request();
+ break;
+ case REQ_TYPE_CLASS:
+ usb_class_request(setup_packet, NULL, 0);
+ break;
+ case REQ_TYPE_VENDOR:
+ usb_vendor_request(setup_packet, NULL, 0);
+ break;
+ default:
+ //x[4] = reqtype;
+ //EP_WriteIRQ(0, (const uint8_t*)x, 5);
+ break;
+ }
+ }else if(rxflag){ // got data over EP0 or host acknowlegement
+ if(ep0dbuflen){
+ switch(reqtype){
+ case REQ_TYPE_CLASS:
+ usb_class_request(setup_packet, ep0databuf, ep0dbuflen);
+ break;
+ case REQ_TYPE_VENDOR:
+ usb_vendor_request(setup_packet, ep0databuf, ep0dbuflen);
+ break;
+ }
+ ep0dbuflen = 0;
+ }
+ }else if(TX_FLAG(epstatus)){ // package transmitted
+ // now we can change address after enumeration
+ if ((USB->DADDR & USB_DADDR_ADD) != USB_Addr){
+ USB->DADDR = USB_DADDR_EF | USB_Addr;
+ usbON = 0;
+ }
+ }
+ epstatus = KEEP_DTOG(USB->EPnR[0]);
+ if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP/data transmission
+ else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged
+ // keep DTOGs, clear CTR_RX,TX, set RX VALID
+ USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX;
+}
+
+/**
+ * Write data to EP buffer (called from IRQ handler)
+ * @param number - EP number
+ * @param *buf - array with data
+ * @param size - its size
+ */
+void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
+ if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
+ uint16_t N2 = (size + 1) >> 1;
+ // the buffer is 16-bit, so we should copy data as it would be uint16_t
+ uint16_t *buf16 = (uint16_t *)buf;
+#if defined USB1_16
+ // very bad: what if `size` is odd?
+ uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
+ for(int i = 0; i < N2; ++i, ++out){
+ *out = buf16[i];
+ }
+#elif defined USB2_16
+ // use memcpy instead?
+ for(int i = 0; i < N2; i++){
+ endpoints[number].tx_buf[i] = buf16[i];
+ }
+#else
+#error "Define USB1_16 or USB2_16"
+#endif
+ USB_BTABLE->EP[number].USB_COUNT_TX = size;
+}
+
+/**
+ * Write data to EP buffer (called outside IRQ handler)
+ * @param number - EP number
+ * @param *buf - array with data
+ * @param size - its size
+ */
+void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
+ EP_WriteIRQ(number, buf, size);
+ uint16_t epstatus = KEEP_DTOG(USB->EPnR[number]);
+ // keep DTOGs, clear CTR_TX & set TX VALID to start transmission
+ USB->EPnR[number] = (epstatus & ~(USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_TX;
+}
+
+/*
+ * Copy data from EP buffer into user buffer area
+ * @param *buf - user array for data
+ * @return amount of data read
+ */
+int EP_Read(uint8_t number, uint8_t *buf){
+ int sz = endpoints[number].rx_cnt;
+ if(!sz) return 0;
+ endpoints[number].rx_cnt = 0;
+#if defined USB1_16
+ int n = (sz + 1) >> 1;
+ uint32_t *in = (uint32_t*)endpoints[number].rx_buf;
+ uint16_t *out = (uint16_t*)buf;
+ for(int i = 0; i < n; ++i, ++in)
+ out[i] = *(uint16_t*)in;
+#elif defined USB2_16
+ // use memcpy instead?
+ for(int i = 0; i < sz; ++i)
+ buf[i] = endpoints[number].rx_buf[i];
+#else
+#error "Define USB1_16 or USB2_16"
+#endif
+ return sz;
+}
+
+
+static uint16_t lastaddr = LASTADDR_DEFAULT;
+/**
+ * Endpoint initialisation
+ * @param number - EP num (0...7)
+ * @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
+ * @param txsz - transmission buffer size @ USB/CAN buffer
+ * @param rxsz - reception buffer size @ USB/CAN buffer
+ * @param uint16_t (*func)(ep_t *ep) - EP handler function
+ * @return 0 if all OK
+ */
+int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
+ if(number >= STM32ENDPOINTS) return 4; // out of configured amount
+ if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
+ if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable
+ USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
+ USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
+ if(rxsz & 1 || rxsz > USB_BTABLE_SIZE) return 3; // wrong rx buffer size
+ uint16_t countrx = 0;
+ if(rxsz < 64) countrx = rxsz / 2;
+ else{
+ if(rxsz & 0x1f) return 3; // should be multiple of 32
+ countrx = 31 + rxsz / 32;
+ }
+ USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
+ endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
+ endpoints[number].txbufsz = txsz;
+ lastaddr += txsz;
+ USB_BTABLE->EP[number].USB_COUNT_TX = 0;
+ USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
+ endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
+ lastaddr += rxsz;
+ USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
+ endpoints[number].func = func;
+ return 0;
+}
+
+// standard IRQ handler
+void USB_IRQ(){
+ if(USB->ISTR & USB_ISTR_RESET){
+ usbON = 0;
+ // Reinit registers
+ USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
+ // Endpoint 0 - CONTROL
+ // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
+ lastaddr = LASTADDR_DEFAULT;
+ // clear address, leave only enable bit
+ USB->DADDR = USB_DADDR_EF;
+ if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){
+ return;
+ }
+ USB->ISTR = ~USB_ISTR_RESET;
+ }
+ if(USB->ISTR & USB_ISTR_CTR){
+ // EP number
+ uint8_t n = USB->ISTR & USB_ISTR_EPID;
+ // copy status register
+ uint16_t epstatus = USB->EPnR[n];
+ // copy received bytes amount
+ endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
+ // check direction
+ if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
+ if(n == 0){ // control endpoint
+ if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
+ EP_Read(0, setupdatabuf);
+ // interrupt handler will be called later
+ }else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
+ ep0dbuflen = EP_Read(0, ep0databuf);
+ }
+ }
+ }
+ // call EP handler
+ if(endpoints[n].func) endpoints[n].func(endpoints[n]);
+ }
+ if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
+ usbON = 0;
+#ifndef STM32F0
+ USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE;
+#else
+ USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE;
+#endif
+ USB->ISTR = ~USB_ISTR_SUSP;
+ }
+ if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
+#ifndef STM32F0
+ USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE); // clear suspend flags
+#else
+ USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE);
+#endif
+ USB->ISTR = ~USB_ISTR_WKUP;
+ }
+}
+
+#if defined STM32F3
+void usb_lp_isr() __attribute__ ((alias ("USB_IRQ")));
+#elif defined STM32F1
+void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ")));
+#elif defined STM32F0
+void usb_isr() __attribute__ ((alias ("USB_IRQ")));
+#endif
diff --git a/F1:F103/I2C_tiny/usb_lib.h b/F1:F103/I2C_tiny/usb_lib.h
new file mode 100644
index 0000000..2d6dacd
--- /dev/null
+++ b/F1:F103/I2C_tiny/usb_lib.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include
+#include "usbhw.h"
+
+#define EP0DATABUF_SIZE (64)
+#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
+
+// bmRequestType & 0x80 == dev2host (1) or host2dev (0)
+// recipient: bmRequestType & 0x1f
+#define REQUEST_RECIPIENT(b) (b & 0x1f)
+#define REQ_RECIPIENT_DEVICE 0
+#define REQ_RECIPIENT_INTERFACE 1
+#define REQ_RECIPIENT_ENDPOINT 2
+#define REQ_RECIPIENT_OTHER 3
+// type: [bmRequestType & 0x60 >> 5]
+#define REQUEST_TYPE(b) ((b&0x60)>>5)
+#define REQ_TYPE_STANDARD 0
+#define REQ_TYPE_CLASS 1
+#define REQ_TYPE_VENDOR 2
+#define REQ_TYPE_RESERVED 3
+
+// deprecated defines:
+#define STANDARD_DEVICE_REQUEST_TYPE 0
+#define STANDARD_INTERFACE_REQUEST_TYPE 1
+#define STANDARD_ENDPOINT_REQUEST_TYPE 2
+#define STANDARD_OTHER_REQUEST_TYPE 3
+#define VENDOR_REQUEST_TYPE 0x40
+#define CONTROL_REQUEST_TYPE 0x21
+
+// bRequest, standard; for bmRequestType == 0x80
+#define GET_STATUS 0x00
+#define GET_DESCRIPTOR 0x06
+#define GET_CONFIGURATION 0x08
+// for bmRequestType == 0
+#define CLEAR_FEATURE 0x01
+#define SET_FEATURE 0x03
+#define SET_ADDRESS 0x05
+#define SET_DESCRIPTOR 0x07
+#define SET_CONFIGURATION 0x09
+// for bmRequestType == 0x81, 1 or 0xB2
+#define GET_INTERFACE 0x0A
+#define SET_INTERFACE 0x0B
+#define SYNC_FRAME 0x0C
+#define VENDOR_REQUEST 0x01
+
+// string descriptors
+enum{
+ iLANGUAGE_DESCR,
+ iMANUFACTURER_DESCR,
+ iPRODUCT_DESCR,
+ iSERIAL_DESCR,
+ iINTERFACE_DESCR,
+ iDESCR_AMOUNT
+};
+
+// Types of descriptors
+#define DEVICE_DESCRIPTOR 0x01
+#define CONFIGURATION_DESCRIPTOR 0x02
+#define STRING_DESCRIPTOR 0x03
+#define DEVICE_QUALIFIER_DESCRIPTOR 0x06
+
+#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX)
+#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX)
+#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP)
+
+// EPnR bits manipulation
+#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
+#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
+
+// EP types
+#define EP_TYPE_BULK 0x00
+#define EP_TYPE_CONTROL 0x01
+#define EP_TYPE_ISO 0x02
+#define EP_TYPE_INTERRUPT 0x03
+
+#define LANG_US (uint16_t)0x0409
+
+#define _USB_STRING_(name, str) \
+static const struct name \
+{ \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint16_t bString[(sizeof(str) - 2) / 2]; \
+ \
+} \
+name = {sizeof(name), 0x03, str}
+
+#define _USB_LANG_ID_(name, lng_id) \
+ \
+static const struct name \
+{ \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint16_t bString; \
+ \
+} \
+name = {0x04, 0x03, lng_id}
+
+// EP0 configuration packet
+typedef struct {
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} config_pack_t;
+
+// endpoints state
+typedef struct{
+ uint16_t *tx_buf; // transmission buffer address
+ uint16_t txbufsz; // transmission buffer size
+ uint8_t *rx_buf; // reception buffer address
+ void (*func)(); // endpoint action function
+ unsigned rx_cnt : 10; // received data counter
+} ep_t;
+
+extern volatile uint8_t usbON;
+
+int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)());
+void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
+void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
+int EP_Read(uint8_t number, uint8_t *buf);
+
+// could be [re]defined in usbdev.c
+extern void usb_class_request(config_pack_t *packet, uint8_t *data, unsigned int datalen);
+extern void usb_vendor_request(config_pack_t *packet, uint8_t *data, unsigned int datalen);
+extern void set_configuration(uint16_t configuration);
diff --git a/F1:F103/I2C_tiny/usbdev.c b/F1:F103/I2C_tiny/usbdev.c
new file mode 100644
index 0000000..52e54e4
--- /dev/null
+++ b/F1:F103/I2C_tiny/usbdev.c
@@ -0,0 +1,243 @@
+/*
+ * This file is part of the I2Ctiny project.
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "i2c.h"
+#include "i2ctiny.h"
+#include "usbdev.h"
+
+static const uint32_t func = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART;
+static uint8_t status = STATUS_IDLE;
+
+// interrupt IN handler - never used
+static void EP1_Handler(){
+ uint16_t epstatus = KEEP_DTOG_STAT(USB->EPnR[1]);
+ if(RX_FLAG(epstatus)) epstatus = (epstatus & ~USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_RX; // set valid RX
+ else epstatus = epstatus & ~(USB_EPnR_STAT_TX|USB_EPnR_STAT_RX);
+ // clear CTR
+ epstatus = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX));
+ USB->EPnR[1] = epstatus;
+}
+
+void set_configuration(uint16_t _U_ configuration){
+ EP_Init(1, EP_TYPE_INTERRUPT, USB_EP1BUFSZ, 0, EP1_Handler);
+}
+
+static void usb_i2c_io(config_pack_t *req, uint8_t *buf, size_t *len){
+ static uint8_t iobuf[256] = "1234567890abcdefghijclmnop";
+ //uint8_t cmd = req->bRequest;
+ uint8_t size = req->wLength;
+ //i2c_set_addr7(req->wIndex);
+ i2c_status stat = I2C_NACK;
+ // ignore NOSTART and STOP!
+ if(req->wValue & I2C_M_RD){ // read
+ //stat = i2c_7bit_receive(buf, size);
+ if(len && *len) memcpy(buf, iobuf, *len);
+ stat = I2C_OK;
+ *len = size;
+ }else{ // write
+ //stat = i2c_7bit_send(buf, size);
+ if(len && *len) memcpy(iobuf, buf, *len);
+ stat = I2C_OK;
+ *len = 0;
+ }
+ if(stat == I2C_OK){
+ status = STATUS_ADDRESS_ACK;
+ }else{
+ //i2c_setup();
+ *len = 0;
+ status = STATUS_ADDRESS_NACK;
+ }
+}
+
+void usb_class_request(config_pack_t *req, uint8_t *data, unsigned int datalen){
+ uint8_t buf[USB_EP0BUFSZ];
+ size_t len = 0;
+ //uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
+ if((req->bmRequestType & 0x80) == 0){ // OUT - setters
+ switch(req->bRequest){
+ case CMD_I2C_IO:
+ case CMD_I2C_IO | CMD_I2C_BEGIN:
+ case CMD_I2C_IO | CMD_I2C_END:
+ case CMD_I2C_IO | CMD_I2C_BEGIN | CMD_I2C_END: // write
+ if(req->wValue & I2C_M_RD) break;
+ len = datalen;
+ usb_i2c_io(req, data, &len);
+ break;
+ default:
+ break;
+ }
+ EP_WriteIRQ(0, 0, 0);
+ return;
+ }
+ switch(req->bRequest){
+ case CMD_ECHO:
+ memcpy(buf, &req->wValue, sizeof(req->wValue));
+ len = sizeof(req->wValue);
+ break;
+ case CMD_GET_FUNC:
+ /* Report our capabilities */
+ bzero(buf, req->wLength);
+ memcpy(buf, &func, sizeof(func));
+ len = req->wLength;
+ break;
+ case CMD_I2C_IO:
+ case CMD_I2C_IO | CMD_I2C_BEGIN:
+ case CMD_I2C_IO | CMD_I2C_END:
+ case CMD_I2C_IO | CMD_I2C_BEGIN | CMD_I2C_END: // read
+ if(req->wValue & I2C_M_RD){
+ len = req->wLength;
+ usb_i2c_io(req, buf, &len);
+ }
+ break;
+ case CMD_GET_STATUS:
+ memcpy(buf, &status, sizeof(status));
+ len = sizeof(status);
+ break;
+ default:
+ break;
+ }
+ EP_WriteIRQ(0, buf, len); // write ZLP if nothing received
+}
+
+void usb_vendor_request(config_pack_t *req, uint8_t *data, unsigned int datalen){
+ uint8_t buf[USB_EP0BUFSZ];
+ size_t len = 0;
+ //uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
+ if((req->bmRequestType & 0x80) == 0){ // OUT - setters
+ switch(req->bRequest){
+ case CMD_I2C_IO:
+ case CMD_I2C_IO | CMD_I2C_BEGIN:
+ case CMD_I2C_IO | CMD_I2C_END:
+ case CMD_I2C_IO | CMD_I2C_BEGIN | CMD_I2C_END: // write
+ if(req->wValue & I2C_M_RD) break;
+ if(!data || !datalen) break; // omit for next stage - when data received
+ len = datalen;
+ usb_i2c_io(req, data, &len);
+ break;
+ default:
+ break;
+ }
+ EP_WriteIRQ(0, 0, 0);
+ return;
+ }
+ switch(req->bRequest){
+ case CMD_ECHO:
+ memcpy(buf, &req->wValue, sizeof(req->wValue));
+ len = sizeof(req->wValue);
+ break;
+ case CMD_GET_FUNC:
+ /* Report our capabilities */
+ bzero(buf, req->wLength);
+ memcpy(buf, &func, sizeof(func));
+ len = req->wLength;
+ break;
+ case CMD_I2C_IO:
+ case CMD_I2C_IO | CMD_I2C_BEGIN:
+ case CMD_I2C_IO | CMD_I2C_END:
+ case CMD_I2C_IO | CMD_I2C_BEGIN | CMD_I2C_END: // read
+ if(req->wValue & I2C_M_RD){
+ len = req->wLength;
+ usb_i2c_io(req, buf, &len);
+ }
+ break;
+ case CMD_GET_STATUS:
+ memcpy(buf, &status, sizeof(status));
+ len = sizeof(status);
+ break;
+ default:
+ break;
+ }
+ EP_WriteIRQ(0, buf, len); // write ZLP if nothing received
+}
+#if 0
+// handler of vendor requests
+void usb_vendor_request(config_pack_t *req){
+ uint8_t buf[USB_EP0BUFSZ];
+ size_t len = 0;
+ if((req->bmRequestType & 0x80) == 0){ // OUT
+ EP_WriteIRQ(0, 0, 0);
+ return;
+ }
+ switch(req->bRequest){
+ case CMD_ECHO:
+ memcpy(buf, &req->wValue, sizeof(req->wValue));
+ len = sizeof(req->wValue);
+ break;
+ case CMD_GET_FUNC:
+ /* Report our capabilities */
+ memcpy(buf, &func, sizeof(func));
+ len = sizeof(func);
+ break;
+/* case CMD_I2C_IO:
+ case CMD_I2C_IO | CMD_I2C_BEGIN:
+ case CMD_I2C_IO | CMD_I2C_END:
+ case CMD_I2C_IO | CMD_I2C_BEGIN | CMD_I2C_END:
+ if(req->wValue & I2C_M_RD) bptr = buf;
+ else{
+ bptr = data;
+ len = datalen;
+ }
+ usb_i2c_io(req, bptr, &len);
+ break;*/
+ case CMD_GET_STATUS:
+ memcpy(buf, &status, sizeof(status));
+ len = sizeof(status);
+ break;
+ default:
+ break;
+ }
+ EP_WriteIRQ(0, buf, len); // write ZLP if nothing received
+#if 0
+ buf[0] = 'D';
+ buf[1] = 'V';
+ switch(recipient){
+ case REQ_RECIPIENT_DEVICE:
+ buf[2] = 'D';
+ //return;
+ break;
+ case REQ_RECIPIENT_INTERFACE:
+ buf[2] = 'I';
+ break;
+ case REQ_RECIPIENT_ENDPOINT:
+ buf[2] = 'E';
+ break;
+ default:
+ buf[2] = 'O';
+ return;
+ }
+ EP_WriteIRQ(0, buf, 3);
+ return;
+ switch(recipient){
+ case REQ_RECIPIENT_DEVICE:
+ break;
+ default:
+ return;
+ /*
+ case REQ_RECIPIENT_INTERFACE:
+ break;
+ case REQ_RECIPIENT_ENDPOINT:
+ break;
+ default:
+ break;*/
+ }
+#endif
+}
+#endif
diff --git a/F1:F103/I2C_tiny/usbdev.h b/F1:F103/I2C_tiny/usbdev.h
new file mode 100644
index 0000000..b4a4cec
--- /dev/null
+++ b/F1:F103/I2C_tiny/usbdev.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the I2Ctiny project.
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include "usb_lib.h"
+
+#ifndef _U_
+#define _U_ __attribute__((__unused__))
+#endif
+
+
diff --git a/F1:F103/I2C_tiny/usbhw.c b/F1:F103/I2C_tiny/usbhw.c
new file mode 100644
index 0000000..01d4d77
--- /dev/null
+++ b/F1:F103/I2C_tiny/usbhw.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "usb_lib.h"
+
+// here we suppose that all PIN settings done in hw_setup earlier
+void USB_setup(){
+#if defined STM32F3
+ NVIC_DisableIRQ(USB_LP_IRQn);
+ // remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
+ RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable tacting of SYSCFG
+ SYSCFG->CFGR1 |= SYSCFG_CFGR1_USB_IT_RMP;
+#elif defined STM32F1
+ NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
+ NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
+#elif defined STM32F0
+ NVIC_DisableIRQ(USB_IRQn);
+ RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
+ RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
+ RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48
+ uint32_t tmout = 16000000;
+ while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break;}
+ FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
+ CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
+ CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source
+ CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
+ CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
+ RCC->CFGR |= RCC_CFGR_SW;
+#endif
+ RCC->APB1ENR |= RCC_APB1ENR_USBEN;
+ //??
+ USB->CNTR = USB_CNTR_FRES; // Force USB Reset
+ for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
+ USB->CNTR = 0;
+ USB->BTABLE = 0;
+ USB->DADDR = 0;
+ USB->ISTR = 0;
+ USB->CNTR = USB_CNTR_RESETM | USB_CNTR_WKUPM; // allow only wakeup & reset interrupts
+#if defined STM32F3
+ NVIC_EnableIRQ(USB_LP_IRQn);
+#elif defined STM32F1
+ NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
+#elif defined STM32F0
+ USB->BCDR |= USB_BCDR_DPPU;
+ NVIC_EnableIRQ(USB_IRQn);
+#endif
+}
+
+
diff --git a/F1:F103/I2C_tiny/usbhw.h b/F1:F103/I2C_tiny/usbhw.h
new file mode 100644
index 0000000..d243720
--- /dev/null
+++ b/F1:F103/I2C_tiny/usbhw.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#if defined STM32F0
+#include
+#elif defined STM32F1
+#include
+// there's no this define in standard header
+#define USB_BASE ((uint32_t)0x40005C00)
+#elif defined STM32F3
+#include
+#endif
+
+// max endpoints number
+#define STM32ENDPOINTS 8
+/**
+ * Buffers size definition
+ **/
+
+// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series
+#if !defined USB1_16 && !defined USB2_16
+#if defined STM32F0
+#define USB2_16
+#elif defined STM32F1
+#define USB1_16
+#else
+#error "Can't determine USB1_16 or USB2_16, define by hands"
+#endif
+#endif
+
+// BTABLE_SIZE FOR STM32F3:
+// In STM32F303/302xB/C, 512 bytes SRAM is not shared with CAN.
+// In STM32F302x6/x8 and STM32F30xxD/E, 726 bytes dedicated SRAM and 256 bytes shared SRAM with CAN i.e.
+// 1Kbytes dedicated SRAM in case CAN is disabled.
+// remember, that USB_BTABLE_SIZE will be divided by ACCESSZ, so don't divide it twice for 32-bit addressing
+
+#ifdef NOCAN
+#if defined STM32F0
+#define USB_BTABLE_SIZE 1024
+#elif defined STM32F3
+#define USB_BTABLE_SIZE 512
+#warning "Please, check real buffer size due to docs"
+#else
+#error "define STM32F0 or STM32F3"
+#endif
+#else // !NOCAN: F0/F3 with CAN or F1 (can't simultaneously run CAN and USB)
+#if defined STM32F0
+#define USB_BTABLE_SIZE 768
+#elif defined STM32F3
+#define USB_BTABLE_SIZE 512
+#warning "Please, check real buffer size due to docs"
+#else // STM32F103: 1024 bytes but with 32-bit addressing
+#define USB_BTABLE_SIZE 1024
+#endif
+#endif // NOCAN
+
+// first 64 bytes of USB_BTABLE are registers!
+// for USB FS EP0 buffers are from 8 to 64 bytes long
+#define USB_EP0BUFSZ 64
+// EP1 - interrupt - buffer size
+#define USB_EP1BUFSZ 8
+
+#define USB_BTABLE_BASE 0x40006000
+#define USB ((USB_TypeDef *) USB_BASE)
+
+#ifdef USB_BTABLE
+#undef USB_BTABLE
+#endif
+#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
+#define USB_ISTR_EPID 0x0000000F
+#define USB_FNR_LSOF_0 0x00000800
+#define USB_FNR_lSOF_1 0x00001000
+#define USB_LPMCSR_BESL_0 0x00000010
+#define USB_LPMCSR_BESL_1 0x00000020
+#define USB_LPMCSR_BESL_2 0x00000040
+#define USB_LPMCSR_BESL_3 0x00000080
+#define USB_EPnR_CTR_RX 0x00008000
+#define USB_EPnR_DTOG_RX 0x00004000
+#define USB_EPnR_STAT_RX 0x00003000
+#define USB_EPnR_STAT_RX_0 0x00001000
+#define USB_EPnR_STAT_RX_1 0x00002000
+#define USB_EPnR_SETUP 0x00000800
+#define USB_EPnR_EP_TYPE 0x00000600
+#define USB_EPnR_EP_TYPE_0 0x00000200
+#define USB_EPnR_EP_TYPE_1 0x00000400
+#define USB_EPnR_EP_KIND 0x00000100
+#define USB_EPnR_CTR_TX 0x00000080
+#define USB_EPnR_DTOG_TX 0x00000040
+#define USB_EPnR_STAT_TX 0x00000030
+#define USB_EPnR_STAT_TX_0 0x00000010
+#define USB_EPnR_STAT_TX_1 0x00000020
+#define USB_EPnR_EA 0x0000000F
+#define USB_COUNTn_RX_BLSIZE 0x00008000
+#define USB_COUNTn_NUM_BLOCK 0x00007C00
+#define USB_COUNTn_RX 0x0000003F
+
+#define USB_TypeDef USB_TypeDef_custom
+
+typedef struct {
+ __IO uint32_t EPnR[STM32ENDPOINTS];
+ __IO uint32_t RESERVED[STM32ENDPOINTS];
+ __IO uint32_t CNTR;
+ __IO uint32_t ISTR;
+ __IO uint32_t FNR;
+ __IO uint32_t DADDR;
+ __IO uint32_t BTABLE;
+#ifdef STM32F0
+ __IO uint32_t LPMCSR;
+ __IO uint32_t BCDR;
+#endif
+} USB_TypeDef;
+
+// F303 D/E have 2x16 access scheme
+typedef struct{
+#if defined USB2_16
+ __IO uint16_t USB_ADDR_TX;
+ __IO uint16_t USB_COUNT_TX;
+ __IO uint16_t USB_ADDR_RX;
+ __IO uint16_t USB_COUNT_RX;
+#define ACCESSZ (1)
+#define BUFTYPE uint8_t
+#elif defined USB1_16
+ __IO uint32_t USB_ADDR_TX;
+ __IO uint32_t USB_COUNT_TX;
+ __IO uint32_t USB_ADDR_RX;
+ __IO uint32_t USB_COUNT_RX;
+#define ACCESSZ (2)
+#define BUFTYPE uint16_t
+#else
+#error "Define USB1_16 or USB2_16"
+#endif
+} USB_EPDATA_TypeDef;
+
+
+typedef struct{
+ __IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
+} USB_BtableDef;
+
+void USB_setup();
diff --git a/F1:F103/I2C_tiny/version.inc b/F1:F103/I2C_tiny/version.inc
new file mode 100644
index 0000000..d3275ef
--- /dev/null
+++ b/F1:F103/I2C_tiny/version.inc
@@ -0,0 +1,2 @@
+#define BUILD_NUMBER "1"
+#define BUILD_DATE "2024-11-27"