Compare commits
34 Commits
master
...
hdmi_firew
Author | SHA1 | Date |
---|---|---|
King Kévin | 0b87be5fd7 | |
King Kévin | 20372fb78c | |
King Kévin | f94f1bd065 | |
King Kévin | b895c75621 | |
King Kévin | eb4e30f79a | |
King Kévin | 73a55e2a3f | |
King Kévin | 3c1b0ca7ea | |
King Kévin | e988151465 | |
King Kévin | 7cc0901d33 | |
King Kévin | e34fec4221 | |
King Kévin | ae158d3098 | |
King Kévin | 523153fcf0 | |
King Kévin | 03d4874605 | |
King Kévin | c68bf6a324 | |
King Kévin | 1d78baf925 | |
King Kévin | 07e2df9fde | |
King Kévin | 3e9a10ac08 | |
King Kévin | a66a60fbad | |
King Kévin | aa5828e22b | |
King Kévin | 28cf5160d8 | |
King Kévin | a8f80dc3fd | |
King Kévin | 098d439471 | |
King Kévin | d7bc187e37 | |
King Kévin | 64c97740e1 | |
King Kévin | 2bd3c4c0b3 | |
King Kévin | 55b9e429fa | |
King Kévin | a710db6915 | |
King Kévin | 729e73e705 | |
King Kévin | 6eca312035 | |
King Kévin | 8a7b6d3cc3 | |
King Kévin | 19f3e27cd6 | |
King Kévin | 05a9cb09f4 | |
King Kévin | 45322fd017 | |
King Kévin | 257b29c908 |
12
Makefile
12
Makefile
|
@ -1,15 +1,21 @@
|
|||
CC := sdcc
|
||||
CFLAGS := -mstm8 --std-c99 --opt-code-size --Werror
|
||||
LDFLAGS = -mstm8 --out-fmt-ihx -lstm8
|
||||
FIRMWARE := main
|
||||
SRC_FILES := main.c softi2c_master.c eeprom_blockprog.c
|
||||
OBJ_FILES := $(patsubst %.c,%.rel,$(SRC_FILES))
|
||||
|
||||
all: $(FIRMWARE).ihx
|
||||
|
||||
%.ihx: %.c stm8s.h
|
||||
$(CC) $(CFLAGS) --out-fmt-ihx $<
|
||||
$(FIRMWARE).ihx: $(OBJ_FILES)
|
||||
$(CC) $(LDFLAGS) $^ -o $@
|
||||
size $@
|
||||
|
||||
%.rel: %.c %.h
|
||||
$(CC) $(CFLAGS) --compile-only $<
|
||||
|
||||
flash: $(FIRMWARE).ihx
|
||||
stm8flash -c stlinkv2 -p stm8s103f3 -w $<
|
||||
|
||||
clean:
|
||||
rm -f $(FIRMWARE).asm $(FIRMWARE).ihx $(FIRMWARE).cdb $(FIRMWARE).lst $(FIRMWARE).map $(FIRMWARE).lk $(FIRMWARE).rel $(FIRMWARE).rst $(FIRMWARE).sym
|
||||
rm -f $(FIRMWARE).asm $(FIRMWARE).ihx $(FIRMWARE).cdb $(FIRMWARE).lst $(FIRMWARE).map $(FIRMWARE).lk $(FIRMWARE).rel $(FIRMWARE).rst $(FIRMWARE).sym $(OBJ_FILES)
|
||||
|
|
2
README
2
README
|
@ -1,2 +0,0 @@
|
|||
firmware template for ST STM8S micro-controller.
|
||||
includes register definitions using macros and structures.
|
|
@ -0,0 +1,42 @@
|
|||
This is the firmware for the [HDMI firewall programmer](https://git.cuvoodoo.info/kingkevin/board/src/branch/hdmi_firewall_programmer).
|
||||
The HDMI firewall programmer copies EDID information onto the [HDMI firewall](https://git.cuvoodoo.info/kingkevin/board/src/branch/hdmi_firewall).
|
||||
|
||||
usage
|
||||
=====
|
||||
|
||||
see [hardware description](https://git.cuvoodoo.info/kingkevin/board/src/branch/hdmi_firewall_programmer/README.md).
|
||||
|
||||
mode of operation
|
||||
=================
|
||||
|
||||
the EDID can be accessed using the I²C lines on the HDMI port.
|
||||
the firmware reads and writes the data from slave device address 0x50.
|
||||
|
||||
connections
|
||||
===========
|
||||
|
||||
the programmer can be flashed using the micro-USB port (not USB signals):
|
||||
|
||||
- the SWIM signal is on USB pin 2 D-
|
||||
- the NRST signal is on USB pin 4 ID
|
||||
|
||||
there is also debugging output using UART (115200 8N1) on STM8S pin 2 PD5/UART_TX.
|
||||
|
||||
code
|
||||
====
|
||||
|
||||
the source code is for a STM8S103F3 micro-controller.
|
||||
|
||||
compile
|
||||
=======
|
||||
|
||||
requirement: SDCC, make
|
||||
|
||||
to compile the source code into a firmware, run `make`.
|
||||
|
||||
flash
|
||||
=====
|
||||
|
||||
requirement: stm8flash, a ST-LINK/V2 programmer
|
||||
|
||||
to flash the firmware on the micro-controller, run `make flash`.
|
|
@ -0,0 +1,69 @@
|
|||
/** library to program EEPROM using block programming
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2021
|
||||
* @warning functions need to be put in and run from RAM (because block programming is used)
|
||||
*/
|
||||
|
||||
// RAM code-putting from https://lujji.github.io/blog/executing-code-from-ram-on-stm8/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean types
|
||||
|
||||
#include "stm8s.h" // STM8S definitions
|
||||
#include "eeprom_blockprog.h" // own definitions
|
||||
|
||||
// start address of EEPROM
|
||||
#define EEPROM_ADDR 0x4000
|
||||
// block size from low-density devices (including STM8S103)
|
||||
#define DATA_BLOCK_SIZE 64U
|
||||
|
||||
#pragma codeseg RAM_SEG
|
||||
bool eeprom_blockprog(const uint8_t* data, uint16_t length)
|
||||
{
|
||||
if (0 == length) {
|
||||
return true; // nothing to do
|
||||
}
|
||||
if (!data) {
|
||||
return false; // data missing
|
||||
}
|
||||
if (0 != (length % DATA_BLOCK_SIZE)) {
|
||||
return false; // we can only program whole blocks
|
||||
}
|
||||
|
||||
// disable DATA (e.g. EEPROM) write protection
|
||||
// don't check if it is locked this it does not save that much time and uses memory)
|
||||
FLASH_DUKR = FLASH_DUKR_KEY1;
|
||||
FLASH_DUKR = FLASH_DUKR_KEY2;
|
||||
// don't verify if unlock succeeded to save memory
|
||||
// if (!(FLASH_IAPSR & FLASH_IAPSR_DUL)) { // un-protecting failed
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
// program data
|
||||
uint8_t* eeprom = (uint8_t*)(EEPROM_ADDR);
|
||||
while (length) {
|
||||
// enable standard block programming
|
||||
FLASH_CR2 |= FLASH_CR2_PRG;
|
||||
FLASH_NCR2 &= ~FLASH_NCR2_NPRG;
|
||||
// program block
|
||||
for (uint8_t i = 0; i < DATA_BLOCK_SIZE; i++) {
|
||||
*(eeprom++) = *(data++);
|
||||
}
|
||||
length -= DATA_BLOCK_SIZE;
|
||||
// wait until program completed
|
||||
while (FLASH_CR2 & FLASH_CR2_PRG);
|
||||
// check if programming failed
|
||||
// we don't check for WR_PG_DIS (while checking EOP) because EEPROM isn't (and can't be) write protected
|
||||
if (!(FLASH_IAPSR & FLASH_IAPSR_EOP)) {
|
||||
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/** library to program EEPROM using block programming
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2021
|
||||
* @warning functions need to be put in and run from RAM (because block programming is used)
|
||||
*/
|
||||
|
||||
/** program EEPROM using block programming
|
||||
* @param[in] data data to be programmed
|
||||
* @param[in] length length of data to be programmed (must be a multiple of the block length)
|
||||
* @return if program succeeded
|
||||
*/
|
||||
bool eeprom_blockprog(const uint8_t* data, uint16_t length);
|
|
@ -0,0 +1,469 @@
|
|||
/** library to communicate using I²C as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2021
|
||||
* @note the I²C peripheral is not well specified and does not cover all cases. The following complexity is the best I could do to cope with it
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean types
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "stm8s.h" // STM8S definitions
|
||||
#include "i2c_master.h" // I²C header and definitions
|
||||
|
||||
bool i2c_master_setup(uint16_t freq_khz)
|
||||
{
|
||||
// configure I²C peripheral
|
||||
I2C_CR1 &= ~I2C_CR1_PE; // disable I²C peripheral to configure it
|
||||
/*
|
||||
if (!i2c_master_check_signals()) { // check the signal lines
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
I2C_FREQR = 16; // the peripheral frequency (must match CPU frequency)
|
||||
if (freq_khz > 100) {
|
||||
uint16_t ccr = (I2C_FREQR * 1000) / (3 * freq_khz);
|
||||
if (ccr > 0x0fff) {
|
||||
ccr = 0x0fff;
|
||||
}
|
||||
I2C_CCRL = (ccr & 0xff); // set SCL at 320 kHz (for less error)
|
||||
I2C_CCRH = ((ccr >> 8) & 0x0f) | (I2C_CCRH_FS); // set fast speed mode
|
||||
I2C_TRISER = ((I2C_FREQR * 3 / 10) + 1); // set rise time
|
||||
} else {
|
||||
uint16_t ccr = (I2C_FREQR * 1000) / (2 * freq_khz);
|
||||
if (ccr > 0x0fff) {
|
||||
ccr = 0x0fff;
|
||||
}
|
||||
I2C_CCRL = (ccr & 0xff); // set SCL at 320 kHz (for less error)
|
||||
I2C_CCRH = ((ccr >> 8) & 0x0f); // set fast speed mode
|
||||
I2C_TRISER = (I2C_FREQR + 1); // set rise time
|
||||
}
|
||||
I2C_CR1 |= I2C_CR1_PE; // enable I²C peripheral
|
||||
return true;
|
||||
}
|
||||
|
||||
void i2c_master_release(void)
|
||||
{
|
||||
I2C_CR1 &= ~I2C_CR1_PE; // disable I²C peripheral
|
||||
}
|
||||
|
||||
bool i2c_master_check_signals(void)
|
||||
{
|
||||
i2c_master_release(); // ensure PB4/PB5 are not used as alternate function
|
||||
GPIO_PB->CR1.reg &= ~(PB4 | PB5); // operate in open-drain mode
|
||||
GPIO_PB->DDR.reg |= (PB4 | PB5); // set SCL/SDA as output to test pull-up
|
||||
GPIO_PB->ODR.reg |= PB4; // ensure SCL is high
|
||||
GPIO_PB->ODR.reg &= ~PB5; // set SDA low (start condition)
|
||||
for (volatile uint8_t t = 0; t < 10; t++); // wait a bit to be sure signal is low
|
||||
GPIO_PB->ODR.reg |= PB5; // set SDA high (stop condition)
|
||||
GPIO_PB->DDR.reg &= ~(PB4 | PB5); // set SCL/SDA as input before it is used as alternate function by the peripheral
|
||||
for (volatile uint8_t t = 0; t < 50; t++); // wait 10 us for pull-up to take effect
|
||||
|
||||
return ((GPIO_PB->IDR.reg & PB4) && (GPIO_PB->IDR.reg & PB5)); // test if both lines are up
|
||||
}
|
||||
|
||||
void i2c_master_reset(void)
|
||||
{
|
||||
I2C_CR2 |= I2C_CR2_STOP; // release lines
|
||||
// don't check if BUSY is cleared since its state might be erroneous
|
||||
// rewriting I2C_CR2 before I2C_CR2_STOP is cleared might cause a second STOP, but at this point we don't care
|
||||
I2C_CR2 |= I2C_CR2_SWRST; // reset peripheral, in case we got stuck and the dog bit
|
||||
// be sure a watchdog is present as this can take forever
|
||||
while ((0 == (GPIO_PB->IDR.reg & PB4) && (0 == (GPIO_PB->IDR.reg & PB5)))); // wait for SDA/SCL line to be released
|
||||
I2C_CR2 &= ~I2C_CR2_SWRST; // release reset
|
||||
I2C_CR1 &= ~I2C_CR1_PE; // disable I²C peripheral to clear some bits
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_start(void)
|
||||
{
|
||||
// send (re-)start condition
|
||||
if (I2C_CR2 & (I2C_CR2_START | I2C_CR2_STOP)) { // ensure start or stop operations are not in progress
|
||||
return I2C_MASTER_RC_START_STOP_IN_PROGESS;
|
||||
}
|
||||
|
||||
// don't check BUSY flag as this might be for a re-start
|
||||
I2C_CR2 |= I2C_CR2_START; // sent start condition
|
||||
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
while ((I2C_CR2 & I2C_CR2_START) || !(I2C_SR1 & I2C_SR1_SB) || !(I2C_SR3 & I2C_SR3_MSL)) { // wait until start condition has been accepted, send, and we are in aster mode
|
||||
if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
/** wait until stop is sent and bus is released
|
||||
* @return I²C return code
|
||||
*/
|
||||
static enum i2c_master_rc i2c_master_wait_stop(void)
|
||||
{
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
while (I2C_CR2 & I2C_CR2_STOP) { // wait until stop condition is accepted and cleared
|
||||
if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
// there is no interrupt flag we can use here
|
||||
}
|
||||
// this time we can't use I2C_CR2_STOP to check for timeout
|
||||
if (I2C_SR3 & I2C_SR3_MSL) { // ensure we are not in master mode anymore
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_SR3 & I2C_SR3_BUSY) { // ensure bus is released
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
/*
|
||||
if (!i2c_master_check_signals()) { // ensure lines are released
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
*/
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_stop(void)
|
||||
{
|
||||
// sanity check
|
||||
if (!(I2C_SR3 & I2C_SR3_BUSY)) { // ensure bus is not already released
|
||||
return I2C_MASTER_RC_NONE; // bus has probably already been released
|
||||
}
|
||||
if (I2C_CR2 & (I2C_CR2_START | I2C_CR2_STOP)) { // ensure start or stop operations are not in progress
|
||||
return I2C_MASTER_RC_START_STOP_IN_PROGESS;
|
||||
}
|
||||
|
||||
I2C_CR2 |= I2C_CR2_STOP; // send stop to release bus
|
||||
return i2c_master_wait_stop();
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_select_slave(uint16_t slave, bool address_10bit, bool write)
|
||||
{
|
||||
if (!(I2C_SR1 & I2C_SR1_SB)) { // start condition has not been sent yet
|
||||
enum i2c_master_rc rc = i2c_master_start(); // send start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
|
||||
// select slave
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
if (!address_10bit) { // 7-bit address
|
||||
I2C_DR = (slave << 1) | (write ? 0 : 1); // select slave, with read/write flag
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR)) { // wait until address is transmitted (or error)
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
} else if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable relevant I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
} else { // 10-bit address
|
||||
// send first part of address
|
||||
I2C_DR = 11110000 | (((slave >> 8 ) & 0x3) << 1); // send first header (11110xx0, where xx are 2 MSb of slave address)
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
while (!(I2C_SR1 & I2C_SR1_ADD10)) { // wait until address is transmitted (or error)
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
} else if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable relevant I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
// send second part of address
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
I2C_DR = (slave & 0xff); // send remaining of address
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR)) { // wait until address is transmitted (or error)
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
} else if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable relevant I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
// go into receive mode if necessary
|
||||
if (!write) {
|
||||
enum i2c_master_rc rc = i2c_master_start(); // send start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
// send first part of address with receive flag
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
I2C_DR = 11110001 | (((slave >> 8) & 0x3) << 1); // send header (11110xx1, where xx are 2 MSb of slave address)
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR)) { // wait until address is transmitted (or error)
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
} else if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable relevant I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
}
|
||||
}
|
||||
// I2C_SR3_TRA should be set after I2C_SR1_ADDR is cleared (end of address transmission), but this is not the case and the TRM/errata does not provide more info
|
||||
// verify if we are in the right mode
|
||||
// final check
|
||||
if (write && !(I2C_SR3 & I2C_SR3_TRA)) {
|
||||
return I2C_MASTER_RC_NOT_TRANSMIT;
|
||||
} else if (!write && (I2C_SR3 & I2C_SR3_TRA)) {
|
||||
return I2C_MASTER_RC_NOT_RECEIVE;
|
||||
}
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_read(uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return I2C_MASTER_RC_OTHER; // we indicate an error because we don't send a stop
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
// we can't check if the address phase it over since ADDR has been cleared when checking for mode
|
||||
if (I2C_SR3 & I2C_SR3_TRA) { // ensure we are in receive mode
|
||||
return I2C_MASTER_RC_NOT_RECEIVE;
|
||||
}
|
||||
|
||||
// read data
|
||||
I2C_CR2 |= I2C_CR2_ACK; // enable ACK by default
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
for (uint16_t i = 0; i < data_size; i++) { // read bytes
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
// set (N)ACK (EV6_3, EV6_1)
|
||||
if (1 == (data_size - i)) { // prepare to sent NACK for last byte
|
||||
I2C_CR2 &= ~(I2C_CR2_ACK); // disable ACK
|
||||
I2C_CR2 |= I2C_CR2_STOP; // prepare to send the stop
|
||||
}
|
||||
rim(); // enable interrupts
|
||||
while (!(I2C_SR1 & I2C_SR1_RXNE)) { // wait until data is received (or error)
|
||||
if (I2C_SR2) { // an error occurred
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITBUFEN | I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable all I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
data[i] = I2C_DR; // read the received byte
|
||||
}
|
||||
|
||||
return i2c_master_wait_stop();
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_write(const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return I2C_MASTER_RC_NONE; // we don't indicate an error because the stop is done separately
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
// we can't check if the address phase it over since ADDR has been cleared when checking for mode
|
||||
if (!(I2C_SR3 & I2C_SR3_TRA)) { // ensure we are in transmit mode
|
||||
return I2C_MASTER_RC_NOT_TRANSMIT;
|
||||
}
|
||||
|
||||
// write data
|
||||
for (uint16_t i = 0; i < data_size; i++) { // write bytes
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
(void)(I2C_SR1 & I2C_SR1_BTF); // clear BTF (when followed by write) in case the clock is stretched because there was no data to send on the next transmission slot
|
||||
I2C_DR = data[i]; // send byte
|
||||
I2C_SR2 = 0; // clear error flags
|
||||
rim(); // enable interrupts
|
||||
while (!(I2C_SR1 & I2C_SR1_TXE)) { // wait until byte has been transmitted
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // data has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
} else if (I2C_SR2) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
I2C_ITR = (I2C_ITR_ITBUFEN | I2C_ITR_ITEVTEN | I2C_ITR_ITERREN); // enable all I²C interrupts
|
||||
wfi(); // got to sleep to prevent EMI causing glitches
|
||||
}
|
||||
}
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_slave_read(uint16_t slave, bool address_10bit, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, false); // select slave to read
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // only read data if needed
|
||||
rc = i2c_master_read(data, data_size); // read data (includes stop)
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went well
|
||||
error:
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_slave_write(uint16_t slave, bool address_10bit, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // write data only is some is available
|
||||
rc = i2c_master_write(data, data_size); // write data
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went well
|
||||
error:
|
||||
i2c_master_stop(); // sent stop condition
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_address_read(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// write address
|
||||
if (NULL != address && address_size > 0) {
|
||||
rc = i2c_master_write(address, address_size); // send memory address
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
// read data
|
||||
if (NULL != data && data_size > 0) {
|
||||
rc = i2c_master_start(); // send re-start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, false); // select slave to read
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
rc = i2c_master_read(data, data_size); // read memory (includes stop)
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE;
|
||||
error:
|
||||
if (I2C_MASTER_RC_NONE != rc) { // only send stop on error
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_address_write(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (UINT16_MAX - address_size < data_size) { // prevent integer overflow
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
if (address_size > 0 && NULL == address) {
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
if (data_size > 0 && NULL == data) {
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
|
||||
enum i2c_master_rc rc; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (address_size && address) {
|
||||
rc = i2c_master_write(address, address_size); // send memory address
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (data_size && data) {
|
||||
rc = i2c_master_write(data, data_size); // send memory data
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = i2c_master_stop(); // sent stop condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went fine
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void i2c_master_isr(void) __interrupt(IRQ_I2C) // I²C event or error happened
|
||||
{
|
||||
I2C_ITR = 0; // disable all interrupt sources to stop looping in ISR and let current loop check the right status flags
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/** library to communicate using I²C as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2021
|
||||
* @warning the I²C peripheral is very glitchy (sending random clock pulses), thus prefer the software implementation alternative, which is simpler, more flexible, smaller, and very stable (it just draws more energy)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** I²C return codes */
|
||||
enum i2c_master_rc {
|
||||
I2C_MASTER_RC_NONE = 0, /**< no error */
|
||||
I2C_MASTER_RC_START_STOP_IN_PROGESS, /**< a start or stop condition is already in progress */
|
||||
I2C_MASTER_RC_NOT_MASTER, /**< not in master mode */
|
||||
I2C_MASTER_RC_NOT_TRANSMIT, /**< not in transmit mode */
|
||||
I2C_MASTER_RC_NOT_RECEIVE, /**< not in receive mode */
|
||||
I2C_MASTER_RC_NOT_READY, /**< slave is not read (previous operations has been NACKed) */
|
||||
I2C_MASTER_RC_NAK, /**< not acknowledge received */
|
||||
I2C_MASTER_RC_BUS_ERROR, /**< an error on the I²C bus occurred */
|
||||
I2C_MASTER_RC_TIMEOUT, /**< a timeout has occurred because an operation has not completed in the expected time */
|
||||
I2C_MASTER_RC_OTHER, /** any other error (does not have to be I²C related) */
|
||||
};
|
||||
|
||||
/** setup I²C peripheral
|
||||
* @param[in] freq_khz desired clock frequency, in kHz
|
||||
* @return if I²C bus is ready to be used (same as i2c_master_check_signals)
|
||||
*/
|
||||
bool i2c_master_setup(uint16_t freq_khz);
|
||||
/** release I²C peripheral */
|
||||
void i2c_master_release(void);
|
||||
/** reset I²C peripheral, fixing any locked state
|
||||
* @warning the I²C peripheral needs to be re-setup
|
||||
* @note to be used after failed start or stop, and bus error
|
||||
*/
|
||||
void i2c_master_reset(void);
|
||||
/** check if SDA and SCL signals are pulled high
|
||||
* @return if SDA and SCL signals are pulled high
|
||||
*/
|
||||
bool i2c_master_check_signals(void);
|
||||
/** send start condition
|
||||
* @return I2C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_start(void);
|
||||
/** select I²C slave device
|
||||
* @warning a start condition should be sent before this operation
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] write this transaction will be followed by a read (false) or write (true) operation
|
||||
* @return I²C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_select_slave(uint16_t slave, bool address_10bit, bool write);
|
||||
/** read data over I²C
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note a stop condition will be sent at the end (I²C does not permit multiple reads, and this is necessary for 1-byte transfer)
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_read(uint8_t* data, uint16_t data_size);
|
||||
/** write data over I²C
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note no stop condition is sent at the end, allowing multiple writes
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_write(const uint8_t* data, uint16_t data_size);
|
||||
/** sent stop condition
|
||||
* @param[in] i2c I²C base address
|
||||
* @return I²C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_stop(void);
|
||||
/** read data from slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_slave_read(uint16_t slave, bool address_10bit, uint8_t* data, uint16_t data_size);
|
||||
/** write data to slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_slave_write(uint16_t slave, bool address_10bit, const uint8_t* data, uint16_t data_size);
|
||||
/** read data at specific address from an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] address memory address of slave to read from
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_address_read(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size);
|
||||
/** write data at specific address on an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] address memory address of slave to write to
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_address_write(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size);
|
||||
/** interrupt service routine used to wake up
|
||||
* @note not sure why the declaration need to be in main for it to work
|
||||
*/
|
||||
void i2c_master_isr(void) __interrupt(IRQ_I2C);
|
||||
|
427
main.c
427
main.c
|
@ -1,19 +1,263 @@
|
|||
/* firmware template for STM8S microcontroller
|
||||
* Copyright (C) 2019-2020 King Kévin <kingkevin@cuvoodoo.info>
|
||||
/* firmware for STM8S-microcontroller-based HDMI firewall programmer
|
||||
* Copyright (C) 2019-2021 King Kévin <kingkevin@cuvoodoo.info>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "stm8s.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// get length of array
|
||||
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#include "stm8s.h"
|
||||
#include "main.h"
|
||||
#include "softi2c_master.h"
|
||||
#include "eeprom_blockprog.h"
|
||||
|
||||
// blink RUN LED to show error
|
||||
static bool led_error = false;
|
||||
|
||||
// set when the button is pressed
|
||||
static volatile bool rw_button_pressed = false;
|
||||
|
||||
// AWU tick count
|
||||
static volatile uint8_t awu_tick = 0;
|
||||
|
||||
// to store the current E-EDID (EDID + extension)
|
||||
static uint8_t edid[256];
|
||||
|
||||
// function RAM (code in RAM)
|
||||
uint8_t f_ram[112 + 5]; // use RAM_SEG size in main.map (plus some margin)
|
||||
|
||||
// function for saving EDID + extension to EEPROM, to put in RAM
|
||||
bool (*ram_eeprom_blockprog)(const uint8_t* data, uint16_t length);
|
||||
|
||||
// size of RAM segment
|
||||
volatile uint8_t RAM_SEG_LEN;
|
||||
|
||||
// get the size of the RAM segment
|
||||
static inline void get_ram_section_length() {
|
||||
__asm__("mov _RAM_SEG_LEN, #l_RAM_SEG");
|
||||
}
|
||||
|
||||
// copy functions to RAM
|
||||
static bool ram_cpy() {
|
||||
get_ram_section_length();
|
||||
if (RAM_SEG_LEN > ARRAY_LENGTH(f_ram)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t i = 0; i < RAM_SEG_LEN; i++) {
|
||||
f_ram[i] = ((uint8_t *)eeprom_blockprog)[i];
|
||||
}
|
||||
ram_eeprom_blockprog = (bool (*)(const uint8_t* data, uint16_t length)) &f_ram;
|
||||
return true;
|
||||
}
|
||||
|
||||
// blocking wait (in 10 us steps, up to UINT32_MAX / 10)
|
||||
static void wait_10us(uint32_t us10)
|
||||
{
|
||||
us10 = ((us10 / (1 << CLK->CKDIVR.fields.HSIDIV)) * 1000) / 206; // calibrated for 1 ms
|
||||
for (volatile uint32_t t = 0; t < us10; t++); // burn energy
|
||||
while (us10--); // burn energy
|
||||
}
|
||||
|
||||
void putc(char c)
|
||||
{
|
||||
while (!UART1->SR.fields.TXE); // wait until TX buffer is empty
|
||||
UART1->DR.reg = c; // put character in buffer to be transmitted
|
||||
// don't wait until the transmission is complete
|
||||
}
|
||||
|
||||
void puts(const char* s)
|
||||
{
|
||||
if (NULL == s) {
|
||||
return;
|
||||
}
|
||||
while (*s) {
|
||||
putc(*s++);
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
}
|
||||
}
|
||||
|
||||
void putn(uint8_t n)
|
||||
{
|
||||
n &= 0x0f; // ensure it's a nibble
|
||||
if (n < 0xa) {
|
||||
putc('0' + n);
|
||||
} else {
|
||||
putc('a' + (n - 0x0a));
|
||||
}
|
||||
}
|
||||
|
||||
void puth(uint8_t h)
|
||||
{
|
||||
putn(h >> 4);
|
||||
putn(h & 0x0f);
|
||||
}
|
||||
|
||||
// verify (E-)EDID checksums
|
||||
// the sum of the bytes (including checksum at the end) must be 0
|
||||
static bool checksum_ok(const uint8_t* data, uint16_t length)
|
||||
{
|
||||
uint8_t checksum = 0;
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
return (0 == checksum);
|
||||
}
|
||||
|
||||
// return if the current EDID is valid
|
||||
/* from HDMI 1.3a specification
|
||||
* All Sinks shall contain an CEA-861-D compliant E-EDID data structure.
|
||||
* A Source shall read the EDID 1.3 and first CEA Extension to determine the capabilities supported
|
||||
by the Sink.
|
||||
* The first 128 bytes of the E-EDID shall contain an EDID 1.3 structure. The contents of this
|
||||
structure shall also meet the requirements of CEA-861-D.
|
||||
*
|
||||
* HDMI 2.1 uses CTA-861-G
|
||||
* this uses EDID 1.4 structure
|
||||
* EDID 1.3/1.4 is 128 bytes long, and can point to 128 bytes extension
|
||||
* EDID 2.0 with its 256 bytes does not seem to be used in HDMI at all
|
||||
* DisplayID with its variable-length structure meant to replace E-EDID only seems to be used in DisplayPort
|
||||
* I have no idea how more than 1 extension is supported since technically the ROM is limited to 256 bytes
|
||||
*/
|
||||
static uint16_t edid_length(void)
|
||||
{
|
||||
puts("EDID check: ");
|
||||
// check EDID 1.3/1.4 fixed pattern header
|
||||
if (0x00 != edid[0] || 0xff != edid[1] || 0xff != edid[2] || 0xff != edid[3] || 0xff != edid[4] || 0xff != edid[5] || 0xff != edid[6] || 0x00 != edid[7]) {
|
||||
puts("invalid header\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (1 == edid[18]) { // EDID 1.3/1.4 128-byte structure
|
||||
if (checksum_ok(&edid[0], 128)) {
|
||||
if (0 == edid[126]) { // no extension
|
||||
puts("128 bytes\r\n");
|
||||
return 128;
|
||||
} else { // extension available
|
||||
// the usual extension is CEA EDID Timing Extension (with extension tag 02), but we allow others
|
||||
// no idea how more than 1 extension is supported
|
||||
if (checksum_ok(&edid[128], 128)) {
|
||||
puts("256 bytes (with extension)\r\n");
|
||||
return 256;
|
||||
} else {
|
||||
puts("extension CRC error\r\n");
|
||||
return 0; // EDID is broken
|
||||
}
|
||||
}
|
||||
} else {
|
||||
puts("CRC error\r\n");
|
||||
return 0;
|
||||
}
|
||||
} else if (2 == edid[18]) { // EDID 2.0 256-byte structure
|
||||
if (checksum_ok(&edid[0], 256)) {
|
||||
puts("256 bytes (no extension)\r\n");
|
||||
return 256;
|
||||
} else {
|
||||
puts("CRC error\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load EDID + extension from EEPROM
|
||||
static void load_edid(void)
|
||||
{
|
||||
for (uint16_t i = 0; i < ARRAY_LENGTH(edid); i++) {
|
||||
edid[i] = *(uint8_t*)(EEPROM_ADDR + i);
|
||||
}
|
||||
}
|
||||
|
||||
// save EDID + extension in EEPROM
|
||||
static bool save_edid(void)
|
||||
{
|
||||
// modify EDID to include the character indicating the firewall
|
||||
const char firewall_indicator = '|'; // sun character to indicate the firewall
|
||||
// ensure descriptor 4 is for Display name
|
||||
if ((0 == edid[108]) && (0 == edid[109]) && (0 == edid[110]) && (0xfc == edid[111]) && (0 == edid[112])) { // ensure descriptor 4 is for Display name
|
||||
uint8_t last_c; // position of last character
|
||||
for (last_c = 113; last_c < 126 && edid[last_c] != '\n'; last_c++); // find position for inserting our character
|
||||
if (firewall_indicator != edid[last_c - 1]) { // the last character is not yet the sun
|
||||
if (last_c > 125) { // ensure we insert as the last possible character
|
||||
last_c = 125;
|
||||
}
|
||||
edid[last_c++] = firewall_indicator; // insert sun
|
||||
if (last_c < 126) {
|
||||
edid[last_c++] = '\n'; // insert LF to terminate string
|
||||
}
|
||||
while (last_c < 126) {
|
||||
edid[last_c++] = ' '; // insert padding space
|
||||
}
|
||||
// calculate new checksum
|
||||
uint8_t checksum = 0;
|
||||
for (uint8_t i = 0; i < 127; i++) {
|
||||
checksum += edid[i];
|
||||
}
|
||||
edid[127] = (256 - checksum);
|
||||
}
|
||||
}
|
||||
|
||||
return ram_eeprom_blockprog(edid, edid_length());
|
||||
}
|
||||
|
||||
// size in byte of a page in the I²C EEPROM (for faster page write)
|
||||
#define I2C_EEPROM_PAGE_SIZE 16U
|
||||
|
||||
// read EDID from I²C memory
|
||||
// return if succeeded
|
||||
static bool read_edid(void)
|
||||
{
|
||||
if (!softi2c_master_setup(10)) {
|
||||
puts("I²C setup failed");
|
||||
return false;
|
||||
}
|
||||
// read all pages
|
||||
// in theory I could read the whole address space at once, but in practice short clock pulses appear after some bytes, corrupting the data flow. I have no idea what the cause of theses glitches is (I even used WFI to reduce EMI as recommended in the errata)
|
||||
for (uint16_t i = 0; i < ARRAY_LENGTH(edid); i += I2C_EEPROM_PAGE_SIZE) {
|
||||
const uint8_t address[] = {i}; // address of page to read
|
||||
uint8_t data[I2C_EEPROM_PAGE_SIZE]; // data from page
|
||||
bool same = false; // if the data read is the same
|
||||
while (!same) { // read until the data is the same
|
||||
const bool rc = softi2c_master_address_read(I2C_SLAVE, address, ARRAY_LENGTH(address), data, ARRAY_LENGTH(data)); // read I²C EEPROM data
|
||||
if (!rc) {
|
||||
puts("I²C read failed");
|
||||
return false;
|
||||
}
|
||||
// check if the data is the same and copy it to EDID
|
||||
same = true;
|
||||
for (uint16_t j = 0; j < I2C_EEPROM_PAGE_SIZE; j++) {
|
||||
if (data[j] != edid[i + j]) {
|
||||
same = false;
|
||||
}
|
||||
edid[i + j] = data[j]; // save last read data
|
||||
}
|
||||
}
|
||||
}
|
||||
softi2c_master_release(); // release I²C again
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// write EDID to I²C memory
|
||||
// return if succeeded
|
||||
static bool write_edid(void)
|
||||
{
|
||||
if (!softi2c_master_setup(10)) {
|
||||
puts("I²C setup failed");
|
||||
return false;
|
||||
}
|
||||
// write all page
|
||||
const uint16_t length = edid_length();
|
||||
for (uint16_t i = 0; i < length; i += I2C_EEPROM_PAGE_SIZE) {
|
||||
const uint8_t address[] = {i};
|
||||
const bool rc = softi2c_master_address_write(I2C_SLAVE, address, ARRAY_LENGTH(address), &edid[i], I2C_EEPROM_PAGE_SIZE); // write I²C EEPROM page
|
||||
if (!rc) {
|
||||
puts("I²C write failed");
|
||||
return false;
|
||||
}
|
||||
wait_10us((5 + 1) * 100); // wait 5 ms for the page write to complete
|
||||
}
|
||||
softi2c_master_release(); // release I²C again
|
||||
return true;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
|
@ -24,6 +268,24 @@ void main(void)
|
|||
CLK->CKDIVR.fields.CPUDIV = CLK_CKDIVR_CPUDIV_DIV0; // don't divide CPU frequency to 16 MHz
|
||||
while (!CLK->ICKR.fields.HSIRDY); // wait for internal oscillator to be ready
|
||||
|
||||
// save power by disabling unused peripheral
|
||||
CLK_PCKENR1 = CLK_PCKENR1_I2C | CLK_PCKENR1_UART1234; // keep I²C and UART
|
||||
CLK_PCKENR2 = CLK_PCKENR2_AWU; // keep AWU
|
||||
|
||||
// configure LEDs
|
||||
EDID_LED_PORT->DDR.reg |= EDID_LED_PIN; // switch pin to output
|
||||
EDID_LED_PORT->CR1.reg &= ~EDID_LED_PIN; // use in open-drain mode
|
||||
edid_led_off(); // start with LED off
|
||||
RUN_LED_PORT->DDR.reg |= RUN_LED_PIN; // switch pin to output
|
||||
RUN_LED_PORT->CR1.reg &= ~RUN_LED_PIN; // use in open-drain mode
|
||||
run_led_off(); // start with LED off
|
||||
|
||||
// configure read/write button
|
||||
RW_BUTTON_PORT->DDR.reg &= ~RW_BUTTON_PIN; // switch pin to input
|
||||
RW_BUTTON_PORT->CR1.reg |= RW_BUTTON_PIN; // pull up
|
||||
RW_BUTTON_PORT->CR2.reg |= RW_BUTTON_PIN; // enable external interrupt
|
||||
EXTI->CR1.fields.PDIS = EXTI_FALLING_EDGE; // interrupt only on falling edges (WARNING hard coded port)
|
||||
|
||||
// configure auto-wakeup (AWU) to be able to refresh the watchdog
|
||||
// 128 kHz LSI used by default in option bytes CKAWUSEL
|
||||
// we skip measuring the LS clock frequency since there is no need to be precise
|
||||
|
@ -31,6 +293,16 @@ void main(void)
|
|||
AWU->APR.fields.APR = 0x3e; // set time to 256 ms
|
||||
AWU_CSR |= AWU_CSR_AWUEN; // enable AWU (start only when entering wait or active halt mode)
|
||||
|
||||
// configure UART for debug output
|
||||
UART1->CR1.fields.M = 0; // 8 data bits
|
||||
UART1->CR3.fields.STOP = 0; // 1 stop bit
|
||||
UART1->BRR2.reg = 0x0B; // set baud rate to 115200 (at 16 MHz)
|
||||
UART1->BRR1.reg = 0x08; // set baud rate to 115200 (at 16 MHz)
|
||||
UART1->CR2.fields.TEN = 1; // enable TX
|
||||
|
||||
// load function in RAM
|
||||
ram_cpy();
|
||||
|
||||
// configure independent watchdog (very loose, just it case the firmware hangs)
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_ENABLE; // start watchdog
|
||||
|
@ -38,14 +310,149 @@ void main(void)
|
|||
IWDG->PR.fields.PR = IWDG_PR_DIV256; // set prescale to longest time (1.02s)
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
|
||||
puts("\r\nCuVoodoo HDMI firewall programmer ready\r\n");
|
||||
|
||||
// erase saved EDID when button is pressed on boot
|
||||
if (0 == (RW_BUTTON_PORT->IDR.reg & RW_BUTTON_PIN)) { // button is pressed while booting
|
||||
for (uint16_t i = 0; i < ARRAY_LENGTH(edid); i++) {
|
||||
edid[i] = 0; // create empty EDID
|
||||
}
|
||||
ram_eeprom_blockprog(edid, ARRAY_LENGTH(edid)); // erase EDID
|
||||
puts("EEPROM EDID erased\r\n");
|
||||
}
|
||||
load_edid(); // load EDID from EEPROM
|
||||
bool edid_valid = (0 != edid_length()); // verify if EDID is valid
|
||||
if (edid_valid) {
|
||||
puts("EEPROM EDID valid\r\n");
|
||||
} else {
|
||||
puts("EEPROM EDID not valid\r\n");
|
||||
}
|
||||
|
||||
rim(); // re-enable interrupts
|
||||
bool action = false; // if an action has been performed
|
||||
while (true) {
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
// handle button press
|
||||
if (rw_button_pressed) {
|
||||
wait_10us(10000); // wait 100 ms for the noise to be gone
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
if (0 == (RW_BUTTON_PORT->IDR.reg & RW_BUTTON_PIN)) { // ensure the button is pressed (the pull-up is really weak)
|
||||
run_led_off(); // clear RUN LED to see result at the end
|
||||
uint8_t press_duration = 1; // start counting how long the button is pressed
|
||||
while (0 == (RW_BUTTON_PORT->IDR.reg & RW_BUTTON_PIN)) { // wait until button is depressed
|
||||
wait_10us(10000); // wait for 100 ms
|
||||
press_duration++;
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
if (press_duration >= 30) { // 3 seconds already passed
|
||||
break; // stop waiting for button to be released
|
||||
}
|
||||
}
|
||||
led_error = false; // reset error state
|
||||
if (press_duration < 30) { // less than 3 sec
|
||||
puts("read I²C EDID: ");
|
||||
run_led_on(); // indicate we started
|
||||
if (read_edid()) { // read EDID from I²C
|
||||
puts(" OK\r\n");
|
||||
edid_valid = (0 != edid_length()); // verify if EDID is valid
|
||||
if (edid_valid) { // read EDID is valid
|
||||
puts("I²C EDID valid\r\n");
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
|
||||
/*
|
||||
puts("EDID data:\r\n");
|
||||
for (uint16_t i = 0; i < ARRAY_LENGTH(edid); i++) {
|
||||
puth(edid[i]);
|
||||
putc(' ');
|
||||
}
|
||||
puts("\r\n");
|
||||
*/
|
||||
|
||||
if (save_edid()) {; // save to EEPROM
|
||||
puts("I²C EDID saved to EEPROM\r\n");
|
||||
} else {
|
||||
led_error = true; // indicate write error
|
||||
puts("could not save EDID to EEPROM\r\n");
|
||||
load_edid(); // re-load EDID from EEPROM
|
||||
edid_valid = (0 != edid_length()); // verify if EDID is valid
|
||||
}
|
||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
} else { // read EDID is not valid
|
||||
puts("I²C EDID not valid, reloading from EEPROM\r\n");
|
||||
led_error = true; // indicate read error
|
||||
load_edid(); // re-load EDID from EEPROM
|
||||
edid_valid = (0 != edid_length()); // verify if EDID is valid
|
||||
}
|
||||
} else { // read error
|
||||
puts("\r\n"); // error should have been printed
|
||||
led_error = true; // indicate read error
|
||||
load_edid(); // re-load EDID from EEPROM
|
||||
edid_valid = (0 != edid_length()); // verify if EDID is valid
|
||||
}
|
||||
} else { // button pressed > 3s
|
||||
run_led_on(); // indicate we started
|
||||
if (edid_valid) { // the current EDID we have is valid and is ready to be written
|
||||
led_error = true; // error indication will be cleared if everything succeeded
|
||||
puts("writing I²C EDID: ");
|
||||
if (write_edid()) { // write EDID to I²C EEPROM succeeded
|
||||
puts("OK\r\n");
|
||||
const uint16_t target_length = edid_length(); // remember the length of the EDID we programmed
|
||||
puts("reading back EDID: ");
|
||||
if (read_edid()) { // read EDID back from I²C
|
||||
puts("OK\r\n");
|
||||
if (edid_length() == target_length) { // ensure the EDID length between what we programmed and read is the same
|
||||
puts("verifying EDID: ");
|
||||
bool identical_edid = true; // to find out if EDID is identical
|
||||
for (uint16_t i = 0; i < target_length; i++) { // compare EDID
|
||||
if (edid[i] != *(uint8_t*)(EEPROM_ADDR + i)) { // ensure the data is the same
|
||||
identical_edid = false; // EDID is not identical
|
||||
break; // stop comparing
|
||||
}
|
||||
}
|
||||
if (identical_edid) { // EDID has been successfully programmed
|
||||
puts("OK\r\n");
|
||||
led_error = false; // no error happened
|
||||
} else {
|
||||
puts("failed (wrong data)\r\n");
|
||||
}
|
||||
} else {
|
||||
puts("failed (not length)\r\n");
|
||||
}
|
||||
} else {
|
||||
puts("\r\n"); // error should have been printed
|
||||
}
|
||||
puts("reloading EEPROM EDID\r\n");
|
||||
load_edid(); // re-load EDID from EEPROM after it has been overwritten from the read
|
||||
edid_valid = (0 != edid_length()); // verify if EDID is valid (should be as before
|
||||
} else {
|
||||
puts("\r\n"); // error should have been printed
|
||||
}
|
||||
} else {
|
||||
puts("\r\n"); // error should have been printed
|
||||
led_error = true; // we can't program an invalid EDID
|
||||
}
|
||||
}
|
||||
}
|
||||
action = true; // remember we performed an action
|
||||
rw_button_pressed = false; // clear flag
|
||||
}
|
||||
// update LED
|
||||
if ((awu_tick & 0x7) < 4) { // on period of blink (every second)
|
||||
edid_led_on(); // on period
|
||||
if (led_error) {
|
||||
run_led_on(); // start blinking
|
||||
}
|
||||
} else {
|
||||
if (!edid_valid) { // EDID not valid
|
||||
edid_led_off(); // blink to let user know
|
||||
}
|
||||
if (led_error) {
|
||||
run_led_off(); // blink to indicate error
|
||||
}
|
||||
}
|
||||
if (action) { // something has been performed, check if other flags have been set meanwhile
|
||||
action = false; // clear flag
|
||||
} else { // nothing down
|
||||
wfi(); // go to sleep (wait for any interrupt, including periodic AWU)
|
||||
wfi(); // go to sleep (wait for any interrupt, also starting AWU)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,5 +460,11 @@ void main(void)
|
|||
void awu(void) __interrupt(IRQ_AWU) // auto wakeup
|
||||
{
|
||||
volatile uint8_t awuf = AWU_CSR; // clear interrupt flag by reading it (reading is required, and volatile prevents compiler optimization)
|
||||
awu_tick++; // increment tick count
|
||||
// let the main loop kick the dog
|
||||
}
|
||||
|
||||
void rw_button_isr(void) __interrupt(IRQ_EXTI3) // button pressed
|
||||
{
|
||||
rw_button_pressed = true; // notify main loop
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// get length of array
|
||||
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
// LED to indicate valid EDID
|
||||
// sink to witch on
|
||||
// off = firmware not running
|
||||
// on = valid EDID
|
||||
// blink = no valid EDID
|
||||
#define EDID_LED_PORT GPIO_PD
|
||||
#define EDID_LED_PIN PD4
|
||||
#define edid_led_on() {EDID_LED_PORT->ODR.reg &= ~EDID_LED_PIN;}
|
||||
#define edid_led_off() {EDID_LED_PORT->ODR.reg |= EDID_LED_PIN;}
|
||||
#define edid_led_toggle() {EDID_LED_PORT->ODR.reg ^= EDID_LED_PIN;}
|
||||
|
||||
// LED to indicate operation read/write result
|
||||
// sink to switch on
|
||||
// off = no operation started
|
||||
// on = operation succeeded
|
||||
// blink = operation failed
|
||||
#define RUN_LED_PORT GPIO_PD
|
||||
#define RUN_LED_PIN PD6
|
||||
#define run_led_on() {RUN_LED_PORT->ODR.reg &= ~RUN_LED_PIN;}
|
||||
#define run_led_off() {RUN_LED_PORT->ODR.reg |= RUN_LED_PIN;}
|
||||
#define run_led_toggle() {RUN_LED_PORT->ODR.reg ^= RUN_LED_PIN;
|
||||
|
||||
// button to start read/write operation
|
||||
// press shorts it to ground
|
||||
// short press = read EDID
|
||||
// long press (> 3s) = write EDID
|
||||
#define RW_BUTTON_PORT GPIO_PD
|
||||
#define RW_BUTTON_PIN PD3
|
||||
|
||||
// start address of EEPROM
|
||||
#define EEPROM_ADDR 0x4000
|
||||
|
||||
// address of I²C EEPROM slave device containing EDID information
|
||||
#define I2C_SLAVE 0x50
|
||||
|
||||
/** print character
|
||||
* @param[in] c character to print
|
||||
*/
|
||||
void putc(char c);
|
||||
/** print string
|
||||
* @param[in] s string to print
|
||||
*/
|
||||
void puts(const char* s);
|
|
@ -0,0 +1,412 @@
|
|||
/** library to communicate using I²C as master, implemented in software
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2021
|
||||
* @note I implemented I²C in software because the hardware peripheral is hard to use, and buggy (I was not able to get rid of clock glitches corrupting the communication, undetected)
|
||||
* @note some methods copied from Wikipedia https://en.wikipedia.org/wiki/I%C2%B2C
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean types
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "stm8s.h" // STM8S definitions
|
||||
#include "softi2c_master.h" // software I²C header and definitions
|
||||
|
||||
// half period to wait for I²C clock
|
||||
static uint16_t period = 0;
|
||||
|
||||
// port for data line
|
||||
#define SDA_PORT GPIO_PB
|
||||
// pin for data line
|
||||
#define SDA_PIN PB5
|
||||
// port for clock line
|
||||
#define SCL_PORT GPIO_PB
|
||||
// pin for clock line
|
||||
#define SCL_PIN PB4
|
||||
// operation timeout (in half period)
|
||||
#define TIMEOUT 10U
|
||||
|
||||
// delay for half a period
|
||||
static void I2C_delay(void)
|
||||
{
|
||||
for (volatile uint16_t i = 0; i < period; i++);
|
||||
}
|
||||
|
||||
// Return current level of SCL line, 0 or 1
|
||||
static inline bool read_SCL(void)
|
||||
{
|
||||
return (SCL_PORT->IDR.reg & SCL_PIN);
|
||||
}
|
||||
|
||||
// Return current level of SDA line, 0 or 1
|
||||
static inline bool read_SDA(void)
|
||||
{
|
||||
return (SDA_PORT->IDR.reg & SDA_PIN);
|
||||
}
|
||||
|
||||
// Do not drive SCL (set pin high-impedance)
|
||||
static inline void set_SCL(void)
|
||||
{
|
||||
SCL_PORT->ODR.reg |= SCL_PIN;
|
||||
}
|
||||
|
||||
// Actively drive SCL signal low
|
||||
static inline void clear_SCL(void)
|
||||
{
|
||||
SCL_PORT->ODR.reg &= ~SCL_PIN;
|
||||
}
|
||||
|
||||
// Do not drive SDA (set pin high-impedance)
|
||||
static inline void set_SDA(void)
|
||||
{
|
||||
SDA_PORT->ODR.reg |= SDA_PIN;
|
||||
}
|
||||
|
||||
// Actively drive SDA signal low
|
||||
static inline void clear_SDA(void)
|
||||
{
|
||||
SDA_PORT->ODR.reg &= ~SDA_PIN;
|
||||
}
|
||||
|
||||
bool softi2c_master_setup(uint16_t freq_khz)
|
||||
{
|
||||
// enforce minimal frequency
|
||||
if (0 == freq_khz) {
|
||||
freq_khz = 1;
|
||||
}
|
||||
// calculated period from frequency (hand tuned value using 16 MHz clock)
|
||||
period = 589 / (1 << CLK->CKDIVR.fields.HSIDIV) / (1 << CLK->CKDIVR.fields.CPUDIV) / freq_khz;
|
||||
|
||||
// switch pins to open drain
|
||||
SCL_PORT->ODR.reg |= SCL_PIN; // ensure clock is high
|
||||
SCL_PORT->DDR.reg |= SCL_PIN; // switch pin to output
|
||||
SCL_PORT->CR1.reg &= ~SCL_PIN; // use in open-drain mode
|
||||
SDA_PORT->ODR.reg |= SDA_PIN; // ensure data is high
|
||||
SDA_PORT->DDR.reg |= SDA_PIN; // switch pin to output
|
||||
SDA_PORT->CR1.reg &= ~SDA_PIN; // use in open-drain mode
|
||||
|
||||
I2C_delay(); // give time to get high
|
||||
return (read_SCL() && read_SDA()); // line is ready when the two lines are high
|
||||
}
|
||||
|
||||
void softi2c_master_release(void)
|
||||
{
|
||||
SCL_PORT->DDR.reg &= ~SCL_PIN; // switch pin to input
|
||||
SDA_PORT->DDR.reg &= ~SDA_PIN; // switch pin to input
|
||||
}
|
||||
|
||||
// if transaction has already started
|
||||
static bool started = false;
|
||||
|
||||
bool softi2c_master_start(void)
|
||||
{
|
||||
if (started) {
|
||||
// if started, do a restart condition
|
||||
// set SDA to 1
|
||||
set_SDA();
|
||||
I2C_delay();
|
||||
set_SCL();
|
||||
uint8_t timeout = TIMEOUT;
|
||||
while (read_SCL() == 0 && timeout) { // Clock stretching
|
||||
I2C_delay();
|
||||
timeout--;
|
||||
}
|
||||
if (0 == timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Repeated start setup time, minimum 4.7us
|
||||
I2C_delay();
|
||||
}
|
||||
|
||||
if (read_SDA() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SCL is high, set SDA from 1 to 0.
|
||||
clear_SDA();
|
||||
I2C_delay();
|
||||
clear_SCL();
|
||||
started = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool softi2c_master_stop(void)
|
||||
{
|
||||
// set SDA to 0
|
||||
clear_SDA();
|
||||
I2C_delay();
|
||||
|
||||
set_SCL();
|
||||
uint8_t timeout = TIMEOUT;
|
||||
while (read_SCL() == 0 && timeout) { // Clock stretching
|
||||
I2C_delay();
|
||||
timeout--;
|
||||
}
|
||||
if (0 == timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
I2C_delay(); // Stop bit setup time, minimum 4us
|
||||
|
||||
// SCL is high, set SDA from 0 to 1
|
||||
set_SDA();
|
||||
I2C_delay();
|
||||
|
||||
if (read_SDA() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
started = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write a bit to I²C bus
|
||||
static bool softi2c_master_write_bit(bool bit) {
|
||||
// set data bit
|
||||
if (bit) {
|
||||
set_SDA();
|
||||
} else {
|
||||
clear_SDA();
|
||||
}
|
||||
I2C_delay(); // SDA change propagation delay
|
||||
set_SCL(); // Set SCL high to indicate a new valid SDA value is available
|
||||
I2C_delay(); // Wait for SDA value to be read by slave, minimum of 4us for standard mode
|
||||
uint8_t timeout = TIMEOUT;
|
||||
while (read_SCL() == 0 && timeout) { // Clock stretching
|
||||
I2C_delay();
|
||||
timeout--;
|
||||
}
|
||||
if (0 == timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SCL is high, now data is valid
|
||||
if (bit && (read_SDA() == 0)) { // If SDA is high, check that nobody else is driving SDA
|
||||
return false;
|
||||
}
|
||||
clear_SCL(); // Clear the SCL to low in preparation for next change
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read a bit from I²C bus
|
||||
static bool softi2c_master_read_bit(void) {
|
||||
set_SDA(); // Let the slave drive data
|
||||
I2C_delay(); // Wait for SDA value to be written by slave, minimum of 4us for standard mode
|
||||
set_SCL(); // Set SCL high to indicate a new valid SDA value is available
|
||||
uint8_t timeout = TIMEOUT;
|
||||
while (read_SCL() == 0 && timeout) { // Clock stretching
|
||||
I2C_delay();
|
||||
timeout--;
|
||||
}
|
||||
if (0 == timeout) {
|
||||
return false;
|
||||
}
|
||||
I2C_delay(); // Wait for SDA value to be written by slave, minimum of 4us for standard mode
|
||||
const bool bit = read_SDA(); // SCL is high, read out bit
|
||||
clear_SCL(); // Set SCL low in preparation for next operation
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
// Write a byte to I2C bus. Return true if ACK by the slave.
|
||||
static bool softi2c_master_write_byte(uint8_t byte)
|
||||
{
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
softi2c_master_write_bit((byte & 0x80) != 0);
|
||||
byte <<= 1;
|
||||
}
|
||||
|
||||
const bool nack = softi2c_master_read_bit();
|
||||
|
||||
return !nack;
|
||||
}
|
||||
|
||||
// Read a byte from I²C bus
|
||||
static uint8_t softi2c_master_read_byte(bool nack)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
byte = (byte << 1) | softi2c_master_read_bit();
|
||||
}
|
||||
|
||||
softi2c_master_write_bit(nack);
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
bool softi2c_master_select_slave(uint8_t slave, bool write)
|
||||
{
|
||||
if (!softi2c_master_start()) { // send (re-)start condition
|
||||
return false;
|
||||
}
|
||||
const uint8_t byte = (slave << 1) | (write ? 0 : 1); // select slave, with read/write flag
|
||||
return softi2c_master_write_byte(byte); // select slave
|
||||
}
|
||||
|
||||
bool softi2c_master_read(uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < data_size; i++) { // read bytes
|
||||
IWDG->KR.fields.KEY = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||
if (1 == (data_size - i)) { // last byte
|
||||
data[i] = softi2c_master_read_byte(true); // NACK after reading byte
|
||||
} else {
|
||||
data[i] = softi2c_master_read_byte(false); // ACK after reading byte
|
||||
}
|
||||
}
|
||||
|
||||
return softi2c_master_stop();
|
||||
}
|
||||
|
||||
bool softi2c_master_write(const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return true; // we don't indicate an error because the stop is done separately
|
||||
}
|
||||
|
||||
// write data
|
||||
for (uint16_t i = 0; i < data_size; i++) { // write bytes
|
||||
if (!softi2c_master_write_byte(data[i])) { // write byte
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool softi2c_master_slave_read(uint8_t slave, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data && data_size > 0) { // no data to read
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!softi2c_master_select_slave(slave, false)) { // select slave to read
|
||||
softi2c_master_stop();
|
||||
return false;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // only read data if needed
|
||||
if (!softi2c_master_read(data, data_size)) { // read data (includes stop)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool softi2c_master_slave_write(uint8_t slave, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data && data_size > 0) { // no data to read
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rc = false;
|
||||
if (!softi2c_master_select_slave(slave, true)) { // select slave to write
|
||||
goto error;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // write data only is some is available
|
||||
if (!softi2c_master_write(data, data_size)) { // write data
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = true; // all went well
|
||||
error:
|
||||
rc = softi2c_master_stop() && rc; // sent stop condition
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool softi2c_master_address_read(uint8_t slave, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (address_size > 0 && NULL == address) {
|
||||
return false;
|
||||
}
|
||||
if (data_size > 0 && NULL == data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rc = false;
|
||||
rc = softi2c_master_select_slave(slave, true); // select slave to write
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// write address
|
||||
if (NULL != address && address_size > 0) {
|
||||
rc = softi2c_master_write(address, address_size); // send memory address
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
// read data
|
||||
if (NULL != data && data_size > 0) {
|
||||
rc = softi2c_master_select_slave(slave, false); // re-select slave to read
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
rc = softi2c_master_read(data, data_size); // read memory (includes stop)
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
softi2c_master_stop(); // sent stop condition
|
||||
}
|
||||
|
||||
rc = true;
|
||||
error:
|
||||
if (!rc) { // only send stop on error
|
||||
softi2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool softi2c_master_address_write(uint8_t slave, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (address_size > 0 && NULL == address) {
|
||||
return false;
|
||||
}
|
||||
if (data_size > 0 && NULL == data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rc = false;
|
||||
rc = softi2c_master_select_slave(slave, true); // select slave to write
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (address_size && address) {
|
||||
rc = softi2c_master_write(address, address_size); // send memory address
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (data_size && data) {
|
||||
rc = softi2c_master_write(data, data_size); // send memory data
|
||||
if (!rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = softi2c_master_stop(); // sent stop condition
|
||||
if (!rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = true; // all went fine
|
||||
error:
|
||||
if (!rc) {
|
||||
softi2c_master_stop(); // send stop on error
|
||||
}
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/** library to communicate using I²C as master, implemented in software
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2021
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** setup I²C peripheral
|
||||
* @param[in] freq_khz desired clock frequency, in kHz
|
||||
* @return if I²C bus is ready
|
||||
*/
|
||||
bool softi2c_master_setup(uint16_t freq_khz);
|
||||
/** release I²C peripheral */
|
||||
void softi2c_master_release(void);
|
||||
/** send start condition
|
||||
* @return if start sent (else arbitration lost)
|
||||
*/
|
||||
bool softi2c_master_start(void);
|
||||
/** sent stop condition
|
||||
* @param[in] i2c I²C base address
|
||||
* @return if stop sent (else arbitration lost)
|
||||
*/
|
||||
bool softi2c_master_stop(void);
|
||||
/** select I²C slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] write this transaction will be followed by a read (false) or write (true) operation
|
||||
* @return if slave ACKed
|
||||
* @note includes (re-)start condition
|
||||
*/
|
||||
bool softi2c_master_select_slave(uint8_t slave, bool write);
|
||||
/** read data over I²C
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return if read succeeded (else arbitration lost)
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note includes sending stop (after having NACKed last received byte)
|
||||
*/
|
||||
bool softi2c_master_read(uint8_t* data, uint16_t data_size);
|
||||
/** write data over I²C
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return if write succeeded (else data has been NACKed)
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note no stop condition is sent at the end, allowing multiple writes
|
||||
*/
|
||||
bool softi2c_master_write(const uint8_t* data, uint16_t data_size);
|
||||
/** read data from slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return if read succeeded (else arbitration has been lost)
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
bool softi2c_master_slave_read(uint8_t slave, uint8_t* data, uint16_t data_size);
|
||||
/** write data to slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return if write succeeded
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
bool softi2c_master_slave_write(uint8_t slave, const uint8_t* data, uint16_t data_size);
|
||||
/** read data at specific address from an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address memory address of slave to read from
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return if read succeeded
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
bool softi2c_master_address_read(uint8_t slave, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size);
|
||||
/** write data at specific address on an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address memory address of slave to write to
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return if write succeeded
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
bool softi2c_master_address_write(uint8_t slave, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size);
|
582
stm8s.h
582
stm8s.h
|
@ -94,14 +94,14 @@ typedef struct {
|
|||
#define PA_DDR (*(volatile uint8_t *)(PA_BASE + 0x02))
|
||||
#define PA_CR1 (*(volatile uint8_t *)(PA_BASE + 0x03))
|
||||
#define PA_CR2 (*(volatile uint8_t *)(PA_BASE + 0x04))
|
||||
#define PA0 (1 << 0)
|
||||
#define PA1 (1 << 1)
|
||||
#define PA2 (1 << 2)
|
||||
#define PA3 (1 << 3)
|
||||
#define PA4 (1 << 4)
|
||||
#define PA5 (1 << 5)
|
||||
#define PA6 (1 << 6)
|
||||
#define PA7 ((uint8_t)(1 << 7))
|
||||
#define PA0 (1U << 0)
|
||||
#define PA1 (1U << 1)
|
||||
#define PA2 (1U << 2)
|
||||
#define PA3 (1U << 3)
|
||||
#define PA4 (1U << 4)
|
||||
#define PA5 (1U << 5)
|
||||
#define PA6 (1U << 6)
|
||||
#define PA7 ((uint8_t)(1U << 7))
|
||||
// Block: Port B
|
||||
#define PB_BASE 0x5005
|
||||
#define GPIO_PB ((GPIO_type*)PB_BASE)
|
||||
|
@ -110,14 +110,14 @@ typedef struct {
|
|||
#define PB_DDR (*(volatile uint8_t *)(PB_BASE + 0x02))
|
||||
#define PB_CR1 (*(volatile uint8_t *)(PB_BASE + 0x03))
|
||||
#define PB_CR2 (*(volatile uint8_t *)(PB_BASE + 0x04))
|
||||
#define PB0 (1 << 0)
|
||||
#define PB1 (1 << 1)
|
||||
#define PB2 (1 << 2)
|
||||
#define PB3 (1 << 3)
|
||||
#define PB4 (1 << 4)
|
||||
#define PB5 (1 << 5)
|
||||
#define PB6 (1 << 6)
|
||||
#define PB7 ((uint8_t)(1 << 7))
|
||||
#define PB0 (1U << 0)
|
||||
#define PB1 (1U << 1)
|
||||
#define PB2 (1U << 2)
|
||||
#define PB3 (1U << 3)
|
||||
#define PB4 (1U << 4)
|
||||
#define PB5 (1U << 5)
|
||||
#define PB6 (1U << 6)
|
||||
#define PB7 ((uint8_t)(1U << 7))
|
||||
// Block: Port C
|
||||
#define PC_BASE 0x500A
|
||||
#define GPIO_PC ((GPIO_type*)PC_BASE)
|
||||
|
@ -126,14 +126,14 @@ typedef struct {
|
|||
#define PC_DDR (*(volatile uint8_t *)(PC_BASE + 0x02))
|
||||
#define PC_CR1 (*(volatile uint8_t *)(PC_BASE + 0x03))
|
||||
#define PC_CR2 (*(volatile uint8_t *)(PC_BASE + 0x04))
|
||||
#define PC0 (1 << 0)
|
||||
#define PC1 (1 << 1)
|
||||
#define PC2 (1 << 2)
|
||||
#define PC3 (1 << 3)
|
||||
#define PC4 (1 << 4)
|
||||
#define PC5 (1 << 5)
|
||||
#define PC6 (1 << 6)
|
||||
#define PC7 ((uint8_t)(1 << 7))
|
||||
#define PC0 (1U << 0)
|
||||
#define PC1 (1U << 1)
|
||||
#define PC2 (1U << 2)
|
||||
#define PC3 (1U << 3)
|
||||
#define PC4 (1U << 4)
|
||||
#define PC5 (1U << 5)
|
||||
#define PC6 (1U << 6)
|
||||
#define PC7 ((uint8_t)(1U << 7))
|
||||
// Block: Port D
|
||||
#define PD_BASE 0x500F
|
||||
#define GPIO_PD ((GPIO_type*)PD_BASE)
|
||||
|
@ -142,14 +142,14 @@ typedef struct {
|
|||
#define PD_DDR (*(volatile uint8_t *)(PD_BASE + 0x02))
|
||||
#define PD_CR1 (*(volatile uint8_t *)(PD_BASE + 0x03))
|
||||
#define PD_CR2 (*(volatile uint8_t *)(PD_BASE + 0x04))
|
||||
#define PD0 (1 << 0)
|
||||
#define PD1 (1 << 1)
|
||||
#define PD2 (1 << 2)
|
||||
#define PD3 (1 << 3)
|
||||
#define PD4 (1 << 4)
|
||||
#define PD5 (1 << 5)
|
||||
#define PD6 (1 << 6)
|
||||
#define PD7 ((uint8_t)(1 << 7))
|
||||
#define PD0 (1U << 0)
|
||||
#define PD1 (1U << 1)
|
||||
#define PD2 (1U << 2)
|
||||
#define PD3 (1U << 3)
|
||||
#define PD4 (1U << 4)
|
||||
#define PD5 (1U << 5)
|
||||
#define PD6 (1U << 6)
|
||||
#define PD7 ((uint8_t)(1U << 7))
|
||||
// Block: Port E
|
||||
#define PE_BASE 0x5014
|
||||
#define GPIO_PE ((GPIO_type*)PE_BASE)
|
||||
|
@ -158,14 +158,14 @@ typedef struct {
|
|||
#define PE_DDR (*(volatile uint8_t *)(PE_BASE + 0x02))
|
||||
#define PE_CR1 (*(volatile uint8_t *)(PE_BASE + 0x03))
|
||||
#define PE_CR2 (*(volatile uint8_t *)(PE_BASE + 0x04))
|
||||
#define PE0 (1 << 0)
|
||||
#define PE1 (1 << 1)
|
||||
#define PE2 (1 << 2)
|
||||
#define PE3 (1 << 3)
|
||||
#define PE4 (1 << 4)
|
||||
#define PE5 (1 << 5)
|
||||
#define PE6 (1 << 6)
|
||||
#define PE7 ((uint8_t)(1 << 7))
|
||||
#define PE0 (1U << 0)
|
||||
#define PE1 (1U << 1)
|
||||
#define PE2 (1U << 2)
|
||||
#define PE3 (1U << 3)
|
||||
#define PE4 (1U << 4)
|
||||
#define PE5 (1U << 5)
|
||||
#define PE6 (1U << 6)
|
||||
#define PE7 ((uint8_t)(1U << 7))
|
||||
// Block: Port F
|
||||
#define PF_BASE 0x5019
|
||||
#define GPIO_PF ((GPIO_type*)PF_BASE)
|
||||
|
@ -174,14 +174,14 @@ typedef struct {
|
|||
#define PF_DDR (*(volatile uint8_t *)(PF_BASE + 0x02))
|
||||
#define PF_CR1 (*(volatile uint8_t *)(PF_BASE + 0x03))
|
||||
#define PF_CR2 (*(volatile uint8_t *)(PF_BASE + 0x04))
|
||||
#define PF0 (1 << 0)
|
||||
#define PF1 (1 << 1)
|
||||
#define PF2 (1 << 2)
|
||||
#define PF3 (1 << 3)
|
||||
#define PF4 (1 << 4)
|
||||
#define PF5 (1 << 5)
|
||||
#define PF6 (1 << 6)
|
||||
#define PF7 ((uint8_t)(1 << 7))
|
||||
#define PF0 (1U << 0)
|
||||
#define PF1 (1U << 1)
|
||||
#define PF2 (1U << 2)
|
||||
#define PF3 (1U << 3)
|
||||
#define PF4 (1U << 4)
|
||||
#define PF5 (1U << 5)
|
||||
#define PF6 (1U << 6)
|
||||
#define PF7 ((uint8_t)(1U << 7))
|
||||
|
||||
// General hardware register map
|
||||
// Block: Flash
|
||||
|
@ -285,48 +285,48 @@ typedef struct {
|
|||
#define FLASH_BASE 0x505A
|
||||
#define FLASH ((FLASH_type*)FLASH_BASE)
|
||||
#define FLASH_CR1 (*(volatile uint8_t *)(FLASH_BASE + 0x00))
|
||||
#define FLASH_CR1_FIX (1 << 0)
|
||||
#define FLASH_CR1_IE (1 << 1)
|
||||
#define FLASH_CR1_AHALT (1 << 2)
|
||||
#define FLASH_CR1_HALT (1 << 3)
|
||||
#define FLASH_CR1_FIX (1U << 0)
|
||||
#define FLASH_CR1_IE (1U << 1)
|
||||
#define FLASH_CR1_AHALT (1U << 2)
|
||||
#define FLASH_CR1_HALT (1U << 3)
|
||||
#define FLASH_CR2 (*(volatile uint8_t *)(FLASH_BASE + 0x01))
|
||||
#define FLASH_CR2_PRG (1 << 0)
|
||||
#define FLASH_CR2_FPRG (1 << 4)
|
||||
#define FLASH_CR2_ERASE (1 << 5)
|
||||
#define FLASH_CR2_WPRG (1 << 6)
|
||||
#define FLASH_CR2_OPT (1 << 7)
|
||||
#define FLASH_CR2_PRG (1U << 0)
|
||||
#define FLASH_CR2_FPRG (1U << 4)
|
||||
#define FLASH_CR2_ERASE (1U << 5)
|
||||
#define FLASH_CR2_WPRG (1U << 6)
|
||||
#define FLASH_CR2_OPT (1U << 7)
|
||||
#define FLASH_NCR2 (*(volatile uint8_t *)(FLASH_BASE + 0x02))
|
||||
#define FLASH_NCR2_NPRG (1 << 0)
|
||||
#define FLASH_NCR2_NFPRG (1 << 4)
|
||||
#define FLASH_NCR2_NERASE (1 << 5)
|
||||
#define FLASH_NCR2_NWPRG (1 << 6)
|
||||
#define FLASH_NCR2_NOPT (1 << 7)
|
||||
#define FLASH_NCR2_NPRG (1U << 0)
|
||||
#define FLASH_NCR2_NFPRG (1U << 4)
|
||||
#define FLASH_NCR2_NERASE (1U << 5)
|
||||
#define FLASH_NCR2_NWPRG (1U << 6)
|
||||
#define FLASH_NCR2_NOPT (1U << 7)
|
||||
#define FLASH_FPR (*(volatile uint8_t *)(FLASH_BASE + 0x03))
|
||||
#define FLASH_FPR_WPB0 (1 << 0)
|
||||
#define FLASH_FPR_WPB1 (1 << 1)
|
||||
#define FLASH_FPR_WPB2 (1 << 2)
|
||||
#define FLASH_FPR_WPB3 (1 << 3)
|
||||
#define FLASH_FPR_WPB4 (1 << 4)
|
||||
#define FLASH_FPR_WPB5 (1 << 5)
|
||||
#define FLASH_FPR_WPB0 (1U << 0)
|
||||
#define FLASH_FPR_WPB1 (1U << 1)
|
||||
#define FLASH_FPR_WPB2 (1U << 2)
|
||||
#define FLASH_FPR_WPB3 (1U << 3)
|
||||
#define FLASH_FPR_WPB4 (1U << 4)
|
||||
#define FLASH_FPR_WPB5 (1U << 5)
|
||||
#define FLASH_NFPR (*(volatile uint8_t *)(FLASH_BASE + 0x04))
|
||||
#define FLASH_NFPR_NWPB0 (1 << 0)
|
||||
#define FLASH_NFPR_NWPB1 (1 << 1)
|
||||
#define FLASH_NFPR_NWPB2 (1 << 2)
|
||||
#define FLASH_NFPR_NWPB3 (1 << 3)
|
||||
#define FLASH_NFPR_NWPB4 (1 << 4)
|
||||
#define FLASH_NFPR_NWPB5 (1 << 5)
|
||||
#define FLASH_NFPR_NWPB0 (1U << 0)
|
||||
#define FLASH_NFPR_NWPB1 (1U << 1)
|
||||
#define FLASH_NFPR_NWPB2 (1U << 2)
|
||||
#define FLASH_NFPR_NWPB3 (1U << 3)
|
||||
#define FLASH_NFPR_NWPB4 (1U << 4)
|
||||
#define FLASH_NFPR_NWPB5 (1U << 5)
|
||||
#define FLASH_IAPSR (*(volatile uint8_t *)(FLASH_BASE + 0x05))
|
||||
#define FLASH_IAPSR_WR_PG_DIS (1 << 0)
|
||||
#define FLASH_IAPSR_PUL (1 << 1)
|
||||
#define FLASH_IAPSR_EOP (1 << 2)
|
||||
#define FLASH_IAPSR_DUL (1 << 3)
|
||||
#define FLASH_IAPSR_HVOFF (1 << 6)
|
||||
#define FLASH_IAPSR_WR_PG_DIS (1U << 0)
|
||||
#define FLASH_IAPSR_PUL (1U << 1)
|
||||
#define FLASH_IAPSR_EOP (1U << 2)
|
||||
#define FLASH_IAPSR_DUL (1U << 3)
|
||||
#define FLASH_IAPSR_HVOFF (1U << 6)
|
||||
#define FLASH_PUKR (*(volatile uint8_t *)(FLASH_BASE + 0x08))
|
||||
#define FLASH_PUKR_KEY1 0x56
|
||||
#define FLASH_PUKR_KEY2 0xAE
|
||||
#define FLASH_DUKR (*(volatile uint8_t *)(FLASH_BASE + 0x0A))
|
||||
#define FLASH_DUKR_KEY1 0x56
|
||||
#define FLASH_DUKR_KEY2 0xAE
|
||||
#define FLASH_DUKR_KEY1 0xAE
|
||||
#define FLASH_DUKR_KEY2 0x56
|
||||
|
||||
// Block: ITC
|
||||
|
||||
|
@ -367,7 +367,7 @@ typedef struct {
|
|||
#define EXTI_CR2 (*(volatile uint8_t *)(EXTI_BASE + 0x01))
|
||||
#define EXTI_CR2_PEIS_OFFSET 0
|
||||
#define EXTI_CR2_PEIS_MASK 0x3
|
||||
#define EXTI_CR2_TLIS (1 << 2)
|
||||
#define EXTI_CR2_TLIS (1U << 2)
|
||||
#define EXTI_FALLING_EDGE_LOW_LEVEL 0
|
||||
#define EXTI_RISING_EDGE 1
|
||||
#define EXTI_FALLING_EDGE 2
|
||||
|
@ -392,11 +392,11 @@ typedef struct {
|
|||
#define RST_BASE 0x50B3
|
||||
#define RST ((RST_type*)RST_BASE)
|
||||
#define RST_SR (*(volatile uint8_t *)(RST_BASE + 0x00))
|
||||
#define RST_SR_WWDGF (1 << 0)
|
||||
#define RST_SR_IWDGF (1 << 1)
|
||||
#define RST_SR_ILLOPF (1 << 2)
|
||||
#define RST_SR_SWIMF (1 << 3)
|
||||
#define RST_SR_EMCF (1 << 4)
|
||||
#define RST_SR_WWDGF (1U << 0)
|
||||
#define RST_SR_IWDGF (1U << 1)
|
||||
#define RST_SR_ILLOPF (1U << 2)
|
||||
#define RST_SR_SWIMF (1U << 3)
|
||||
#define RST_SR_EMCF (1U << 4)
|
||||
|
||||
// Block: CLK
|
||||
typedef union {
|
||||
|
@ -533,15 +533,15 @@ typedef struct {
|
|||
#define CLK_BASE 0x50C0
|
||||
#define CLK ((CLK_type*)CLK_BASE)
|
||||
#define CLK_ICKR (*(volatile uint8_t *)(CLK_BASE + 0x00))
|
||||
#define CLK_ICKR_HSIEN (1 << 0)
|
||||
#define CLK_ICKR_HSIRDY (1 << 1)
|
||||
#define CLK_ICKR_FHW (1 << 2)
|
||||
#define CLK_ICKR_LSIEN (1 << 3)
|
||||
#define CLK_ICKR_LSIRDY (1 << 4)
|
||||
#define CLK_ICKR_REGAH (1 << 5)
|
||||
#define CLK_ICKR_HSIEN (1U << 0)
|
||||
#define CLK_ICKR_HSIRDY (1U << 1)
|
||||
#define CLK_ICKR_FHW (1U << 2)
|
||||
#define CLK_ICKR_LSIEN (1U << 3)
|
||||
#define CLK_ICKR_LSIRDY (1U << 4)
|
||||
#define CLK_ICKR_REGAH (1U << 5)
|
||||
#define CLK_ECKR (*(volatile uint8_t *)(CLK_BASE + 0x01))
|
||||
#define CLK_ECKR_HSEEN (1 << 0)
|
||||
#define CLK_ECKR_HSERDY (1 << 1)
|
||||
#define CLK_ECKR_HSEEN (1U << 0)
|
||||
#define CLK_ECKR_HSERDY (1U << 1)
|
||||
#define CLK_CMSR (*(volatile uint8_t *)(CLK_BASE + 0x03))
|
||||
#define CLK_CMSR_CKM_OFFSET 0
|
||||
#define CLK_CMSR_CKM_MASK 0xff
|
||||
|
@ -552,10 +552,10 @@ typedef struct {
|
|||
#define CLK_SWR_SWI_OFFSET 0
|
||||
#define CLK_SWR_SWI_MASK 0xff
|
||||
#define CLK_SWCR (*(volatile uint8_t *)(CLK_BASE + 0x05))
|
||||
#define CLK_SWCR_SWBSY (1 << 0)
|
||||
#define CLK_SWCR_SWEN (1 << 1)
|
||||
#define CLK_SWCR_SWIEN (1 << 2)
|
||||
#define CLK_SWCR_SWIF (1 << 3)
|
||||
#define CLK_SWCR_SWBSY (1U << 0)
|
||||
#define CLK_SWCR_SWEN (1U << 1)
|
||||
#define CLK_SWCR_SWIEN (1U << 2)
|
||||
#define CLK_SWCR_SWIF (1U << 3)
|
||||
#define CLK_CKDIVR (*(volatile uint8_t *)(CLK_BASE + 0x06))
|
||||
#define CLK_CKDIVR_CPUDIV_OFFSET 0
|
||||
#define CLK_CKDIVR_CPUDIV_MASK 0x7
|
||||
|
@ -574,20 +574,20 @@ typedef struct {
|
|||
#define CLK_CKDIVR_HSIDIV_DIV4 2
|
||||
#define CLK_CKDIVR_HSIDIV_DIV8 3
|
||||
#define CLK_PCKENR1 (*(volatile uint8_t *)(CLK_BASE + 0x07))
|
||||
#define CLK_PCKENR1_I2C (1 << 0)
|
||||
#define CLK_PCKENR1_SPI (1 << 1)
|
||||
#define CLK_PCKENR1_I2C (1U << 0)
|
||||
#define CLK_PCKENR1_SPI (1U << 1)
|
||||
#define CLK_PCKENR1_UART1234 (3 << 2)
|
||||
#define CLK_PCKENR1_TIM46 (1 << 4)
|
||||
#define CLK_PCKENR1_TIM25 (1 << 5)
|
||||
#define CLK_PCKENR1_TIM3 (1 << 6)
|
||||
#define CLK_PCKENR1_TIM1 (1 << 7)
|
||||
#define CLK_PCKENR1_TIM46 (1U << 4)
|
||||
#define CLK_PCKENR1_TIM25 (1U << 5)
|
||||
#define CLK_PCKENR1_TIM3 (1U << 6)
|
||||
#define CLK_PCKENR1_TIM1 (1U << 7)
|
||||
#define CLK_CSSR (*(volatile uint8_t *)(CLK_BASE + 0x08))
|
||||
#define CLK_CSSR_CSSEN (1 << 0)
|
||||
#define CLK_CSSR_AUX (1 << 1)
|
||||
#define CLK_CSSR_CSSDIE (1 << 2)
|
||||
#define CLK_CSSR_CSSD (1 << 3)
|
||||
#define CLK_CSSR_CSSEN (1U << 0)
|
||||
#define CLK_CSSR_AUX (1U << 1)
|
||||
#define CLK_CSSR_CSSDIE (1U << 2)
|
||||
#define CLK_CSSR_CSSD (1U << 3)
|
||||
#define CLK_CCOR (*(volatile uint8_t *)(CLK_BASE + 0x09))
|
||||
#define CLK_CCOR_CCOEN (1 << 0)
|
||||
#define CLK_CCOR_CCOEN (1U << 0)
|
||||
#define CLK_CCOR_CCOSEL_OFFSET 1
|
||||
#define CLK_CCOR_CCOSEL_MASK 0xf
|
||||
#define CLK_CCOR_CCOSEL_HSIDIV 0
|
||||
|
@ -602,17 +602,17 @@ typedef struct {
|
|||
#define CLK_CCOR_CCOSEL_CPU_DIV64 10
|
||||
#define CLK_CCOR_CCOSEL_HSI 11
|
||||
#define CLK_CCOR_CCOSEL_MASTER 12
|
||||
#define CLK_CCOR_CCORDY (1 << 5)
|
||||
#define CLK_CCOR_CCOBSY (1 << 6)
|
||||
#define CLK_CCOR_CCORDY (1U << 5)
|
||||
#define CLK_CCOR_CCOBSY (1U << 6)
|
||||
#define CLK_PCKENR2 (*(volatile uint8_t *)(CLK_BASE + 0x0A))
|
||||
#define CLK_PCKENR2_AWU (1 << 2)
|
||||
#define CLK_PCKENR2_ADC (1 << 3)
|
||||
#define CLK_PCKENR2_CAN (1 << 7)
|
||||
#define CLK_PCKENR2_AWU (1U << 2)
|
||||
#define CLK_PCKENR2_ADC (1U << 3)
|
||||
#define CLK_PCKENR2_CAN (1U << 7)
|
||||
#define CLK_HSITRIMR (*(volatile uint8_t *)(CLK_BASE + 0x0C))
|
||||
#define CLK_HSITRIMR_OFFSET 0
|
||||
#define CLK_HSITRIMR_MASK 0xf
|
||||
#define CLK_SWIMCCR (*(volatile uint8_t *)(CLK_BASE + 0x0D))
|
||||
#define CLK_SWIMCCR_SWIMCLK (1 << 0)
|
||||
#define CLK_SWIMCCR_SWIMCLK (1U << 0)
|
||||
|
||||
// Block: WWDG
|
||||
typedef union {
|
||||
|
@ -652,14 +652,14 @@ typedef struct {
|
|||
#define WWDG_CR (*(volatile uint8_t *)(WWDG_BASE + 0x00))
|
||||
#define WWDG_CR_T_OFFSET 0
|
||||
#define WWDG_CR_T_MASK 0x7f
|
||||
#define WWDG_CR_T0 (1 << 0)
|
||||
#define WWDG_CR_T1 (1 << 1)
|
||||
#define WWDG_CR_T2 (1 << 2)
|
||||
#define WWDG_CR_T3 (1 << 3)
|
||||
#define WWDG_CR_T4 (1 << 4)
|
||||
#define WWDG_CR_T5 (1 << 5)
|
||||
#define WWDG_CR_T6 (1 << 6)
|
||||
#define WWDG_CR_WDGA (1 << 7)
|
||||
#define WWDG_CR_T0 (1U << 0)
|
||||
#define WWDG_CR_T1 (1U << 1)
|
||||
#define WWDG_CR_T2 (1U << 2)
|
||||
#define WWDG_CR_T3 (1U << 3)
|
||||
#define WWDG_CR_T4 (1U << 4)
|
||||
#define WWDG_CR_T5 (1U << 5)
|
||||
#define WWDG_CR_T6 (1U << 6)
|
||||
#define WWDG_CR_WDGA (1U << 7)
|
||||
#define WWDG_WR (*(volatile uint8_t *)(WWDG_BASE + 0x01))
|
||||
#define WWDG_WR_W_OFFSET 0
|
||||
#define WWDG_WR_W_MASK 0x7f
|
||||
|
@ -742,9 +742,9 @@ typedef struct {
|
|||
#define AWU_BASE 0x50F0
|
||||
#define AWU ((AWU_type*)AWU_BASE)
|
||||
#define AWU_CSR (*(volatile uint8_t *)(AWU_BASE + 0x00))
|
||||
#define AWU_CSR_MSR (1 << 0)
|
||||
#define AWU_CSR_AWUEN (1 << 4)
|
||||
#define AWU_CSR_AWUF (1 << 5)
|
||||
#define AWU_CSR_MSR (1U << 0)
|
||||
#define AWU_CSR_AWUEN (1U << 4)
|
||||
#define AWU_CSR_AWUF (1U << 5)
|
||||
#define AWU_APR (*(volatile uint8_t *)(AWU_BASE + 0x01))
|
||||
#define AWU_TBR (*(volatile uint8_t *)(AWU_BASE + 0x02))
|
||||
|
||||
|
@ -1016,48 +1016,50 @@ typedef struct {
|
|||
#define I2C_BASE 0x5210
|
||||
#define I2C ((I2C_type*)I2C_BASE)
|
||||
#define I2C_CR1 (*(volatile uint8_t *)(I2C_BASE + 0x00))
|
||||
#define I2C_CR1_PE (1 << 0)
|
||||
#define I2C_CR1_ENGC (1 << 6)
|
||||
#define I2C_CR1_NOSTRETCH (1 << 7)
|
||||
#define I2C_CR1_PE (1U << 0)
|
||||
#define I2C_CR1_ENGC (1U << 6)
|
||||
#define I2C_CR1_NOSTRETCH (1U << 7)
|
||||
#define I2C_CR2 (*(volatile uint8_t *)(I2C_BASE + 0x01))
|
||||
#define I2C_CR2_START (1 << 0)
|
||||
#define I2C_CR2_STOP (1 << 1)
|
||||
#define I2C_CR2_ACK (1 << 2)
|
||||
#define I2C_CR2_POS (1 << 3)
|
||||
#define I2C_CR2_SWRST (1 << 7)
|
||||
#define I2C_CR2_START (1U << 0)
|
||||
#define I2C_CR2_STOP (1U << 1)
|
||||
#define I2C_CR2_ACK (1U << 2)
|
||||
#define I2C_CR2_POS (1U << 3)
|
||||
#define I2C_CR2_SWRST (1U << 7)
|
||||
#define I2C_FREQR (*(volatile uint8_t *)(I2C_BASE + 0x02))
|
||||
#define I2C_OARL (*(volatile uint8_t *)(I2C_BASE + 0x03))
|
||||
#define I2C_OARL_ARR0 (1 << 0)
|
||||
#define I2C_OARL_ARR0 (1U << 0)
|
||||
#define I2C_OARH (*(volatile uint8_t *)(I2C_BASE + 0x04))
|
||||
#define I2C_OARH_ADDCONF (1 << 6)
|
||||
#define I2C_OARH_ADDMODE (1 << 7)
|
||||
#define I2C_OARH_ADDCONF (1U << 6)
|
||||
#define I2C_OARH_ADDMODE (1U << 7)
|
||||
#define I2C_DR (*(volatile uint8_t *)(I2C_BASE + 0x06))
|
||||
#define I2C_SR1 (*(volatile uint8_t *)(I2C_BASE + 0x07))
|
||||
#define I2C_SR1_SB (1 << 0)
|
||||
#define I2C_SR1_ADDR (1 << 1)
|
||||
#define I2C_SR1_BTF (1 << 2)
|
||||
#define I2C_SR1_ADD10 (1 << 3)
|
||||
#define I2C_SR1_STOPF (1 << 4)
|
||||
#define I2C_SR1_RXNE (1 << 6)
|
||||
#define I2C_SR1_TXE (1 << 7)
|
||||
#define I2C_SR1_SB (1U << 0)
|
||||
#define I2C_SR1_ADDR (1U << 1)
|
||||
#define I2C_SR1_BTF (1U << 2)
|
||||
#define I2C_SR1_ADD10 (1U << 3)
|
||||
#define I2C_SR1_STOPF (1U << 4)
|
||||
#define I2C_SR1_RXNE (1U << 6)
|
||||
#define I2C_SR1_TXE (1U << 7)
|
||||
#define I2C_SR2 (*(volatile uint8_t *)(I2C_BASE + 0x08))
|
||||
#define I2C_SR2_BERR (1 << 0)
|
||||
#define I2C_SR2_ARLO (1 << 1)
|
||||
#define I2C_SR2_AF (1 << 2)
|
||||
#define I2C_SR2_OVR (1 << 3)
|
||||
#define I2C_SR2_WUFH (1 << 5)
|
||||
#define I2C_SR2_BERR (1U << 0)
|
||||
#define I2C_SR2_ARLO (1U << 1)
|
||||
#define I2C_SR2_AF (1U << 2)
|
||||
#define I2C_SR2_OVR (1U << 3)
|
||||
#define I2C_SR2_WUFH (1U << 5)
|
||||
#define I2C_SR3 (*(volatile uint8_t *)(I2C_BASE + 0x09))
|
||||
#define I2C_SR3_MSL (1 << 0)
|
||||
#define I2C_SR3_BUSY (1 << 1)
|
||||
#define I2C_SR3_TRA (1 << 2)
|
||||
#define I2C_SR3_GENCALL (1 << 4)
|
||||
#define I2C_SR3_DUALF (1 << 7)
|
||||
#define I2C_SR3_MSL (1U << 0)
|
||||
#define I2C_SR3_BUSY (1U << 1)
|
||||
#define I2C_SR3_TRA (1U << 2)
|
||||
#define I2C_SR3_GENCALL (1U << 4)
|
||||
#define I2C_SR3_DUALF (1U << 7)
|
||||
#define I2C_ITR (*(volatile uint8_t *)(I2C_BASE + 0x0A))
|
||||
#define I2C_ITR_ITERREN (1 << 0)
|
||||
#define I2C_ITR_ITEVTEN (1 << 1)
|
||||
#define I2C_ITR_ITBUFEN (1 << 2)
|
||||
#define I2C_ITR_ITERREN (1U << 0)
|
||||
#define I2C_ITR_ITEVTEN (1U << 1)
|
||||
#define I2C_ITR_ITBUFEN (1U << 2)
|
||||
#define I2C_CCRL (*(volatile uint8_t *)(I2C_BASE + 0x0B))
|
||||
#define I2C_CCRH (*(volatile uint8_t *)(I2C_BASE + 0x0C))
|
||||
#define I2C_CCRH_DUTY (1U << 6)
|
||||
#define I2C_CCRH_FS (1U << 7)
|
||||
#define I2C_TRISER (*(volatile uint8_t *)(I2C_BASE + 0x0D))
|
||||
#define I2C_PECR (*(volatile uint8_t *)(I2C_BASE + 0x0E))
|
||||
|
||||
|
@ -2384,36 +2386,36 @@ typedef struct {
|
|||
|
||||
// Block: TIM2
|
||||
#define TIM2_CR1 (*(volatile uint8_t *)(TIM2_BASE + 0x00))
|
||||
#define TIM2_CR1_CEN (1 << 0)
|
||||
#define TIM2_CR1_UDIS (1 << 1)
|
||||
#define TIM2_CR1_URS (1 << 2)
|
||||
#define TIM2_CR1_OPM (1 << 3)
|
||||
#define TIM2_CR1_APRE (1 << 7)
|
||||
#define TIM2_CR1_CEN (1U << 0)
|
||||
#define TIM2_CR1_UDIS (1U << 1)
|
||||
#define TIM2_CR1_URS (1U << 2)
|
||||
#define TIM2_CR1_OPM (1U << 3)
|
||||
#define TIM2_CR1_APRE (1U << 7)
|
||||
#define TIM2_IER (*(volatile uint8_t *)(TIM2_BASE + 0x03))
|
||||
#define TIM2_IER_UIE (1 << 0)
|
||||
#define TIM2_IER_CC1IE (1 << 1)
|
||||
#define TIM2_IER_CC2IE (1 << 2)
|
||||
#define TIM2_IER_CC3IE (1 << 3)
|
||||
#define TIM2_IER_TIE (1 << 6)
|
||||
#define TIM2_IER_UIE (1U << 0)
|
||||
#define TIM2_IER_CC1IE (1U << 1)
|
||||
#define TIM2_IER_CC2IE (1U << 2)
|
||||
#define TIM2_IER_CC3IE (1U << 3)
|
||||
#define TIM2_IER_TIE (1U << 6)
|
||||
#define TIM2_SR1 (*(volatile uint8_t *)(TIM2_BASE + 0x04))
|
||||
#define TIM2_SR1_UIF (1 << 0)
|
||||
#define TIM2_SR1_CC1IF (1 << 1)
|
||||
#define TIM2_SR1_CC2IF (1 << 2)
|
||||
#define TIM2_SR1_CC3IF (1 << 3)
|
||||
#define TIM2_SR1_TIF (1 << 6)
|
||||
#define TIM2_SR1_UIF (1U << 0)
|
||||
#define TIM2_SR1_CC1IF (1U << 1)
|
||||
#define TIM2_SR1_CC2IF (1U << 2)
|
||||
#define TIM2_SR1_CC3IF (1U << 3)
|
||||
#define TIM2_SR1_TIF (1U << 6)
|
||||
#define TIM2_SR2 (*(volatile uint8_t *)(TIM2_BASE + 0x05))
|
||||
#define TIM2_SR2_CC1OF (1 << 1)
|
||||
#define TIM2_SR2_CC2OF (1 << 2)
|
||||
#define TIM2_SR2_CC3OF (1 << 3)
|
||||
#define TIM2_SR2_CC1OF (1U << 1)
|
||||
#define TIM2_SR2_CC2OF (1U << 2)
|
||||
#define TIM2_SR2_CC3OF (1U << 3)
|
||||
#define TIM2_EGR (*(volatile uint8_t *)(TIM2_BASE + 0x06))
|
||||
#define TIM2_EGR_CC1G (1 << 1)
|
||||
#define TIM2_EGR_CC2G (1 << 2)
|
||||
#define TIM2_EGR_CC3G (1 << 3)
|
||||
#define TIM2_EGR_TG (1 << 6)
|
||||
#define TIM2_EGR_CC1G (1U << 1)
|
||||
#define TIM2_EGR_CC2G (1U << 2)
|
||||
#define TIM2_EGR_CC3G (1U << 3)
|
||||
#define TIM2_EGR_TG (1U << 6)
|
||||
#define TIM2_CCMR1 (*(volatile uint8_t *)(TIM2_BASE + 0x07))
|
||||
#define TIM2_CCMR1_CC1S_OFFSET 0
|
||||
#define TIM2_CCMR1_CC1S_MASK 0x3
|
||||
#define TIM2_CCMR1_CC1PE (1 << 3)
|
||||
#define TIM2_CCMR1_CC1PE (1U << 3)
|
||||
#define TIM2_CCMR1_OC1M_OFFSET 4
|
||||
#define TIM2_CCMR1_OC1M_MASK 0x7
|
||||
#define TIM2_CCMR1_CC1S_OFFSET 0
|
||||
|
@ -2425,7 +2427,7 @@ typedef struct {
|
|||
#define TIM2_CCMR2 (*(volatile uint8_t *)(TIM2_BASE + 0x08))
|
||||
#define TIM2_CCMR2_CC2S_OFFSET 0
|
||||
#define TIM2_CCMR2_CC2S_MASK 0x3
|
||||
#define TIM2_CCMR2_CC2PE (1 << 3)
|
||||
#define TIM2_CCMR2_CC2PE (1U << 3)
|
||||
#define TIM2_CCMR2_OC2M_OFFSET 4
|
||||
#define TIM2_CCMR2_OC2M_MASK 0x7
|
||||
#define TIM2_CCMR2_CC2S_OFFSET 0
|
||||
|
@ -2437,7 +2439,7 @@ typedef struct {
|
|||
#define TIM2_CCMR3 (*(volatile uint8_t *)(TIM2_BASE + 0x09))
|
||||
#define TIM2_CCMR3_CC3S_OFFSET 0
|
||||
#define TIM2_CCMR3_CC3S_MASK 0x3
|
||||
#define TIM2_CCMR3_CC3PE (1 << 3)
|
||||
#define TIM2_CCMR3_CC3PE (1U << 3)
|
||||
#define TIM2_CCMR3_OC3M_OFFSET 4
|
||||
#define TIM2_CCMR3_OC3M_MASK 0x7
|
||||
#define TIM2_CCMR3_CC3S_OFFSET 0
|
||||
|
@ -2447,13 +2449,13 @@ typedef struct {
|
|||
#define TIM2_CCMR3_IC3F_OFFSET 4
|
||||
#define TIM2_CCMR3_IC3F_MASK 0xf
|
||||
#define TIM2_CCER1 (*(volatile uint8_t *)(TIM2_BASE + 0x0A))
|
||||
#define TIM2_CCER1_CC1E (1 << 0)
|
||||
#define TIM2_CCER1_CC1P (1 << 1)
|
||||
#define TIM2_CCER1_CC2E (1 << 4)
|
||||
#define TIM2_CCER1_CC2P (1 << 5)
|
||||
#define TIM2_CCER1_CC1E (1U << 0)
|
||||
#define TIM2_CCER1_CC1P (1U << 1)
|
||||
#define TIM2_CCER1_CC2E (1U << 4)
|
||||
#define TIM2_CCER1_CC2P (1U << 5)
|
||||
#define TIM2_CCER2 (*(volatile uint8_t *)(TIM2_BASE + 0x0B))
|
||||
#define TIM2_CCER2_CC3E (1 << 0)
|
||||
#define TIM2_CCER2_CC3P (1 << 1)
|
||||
#define TIM2_CCER2_CC3E (1U << 0)
|
||||
#define TIM2_CCER2_CC3P (1U << 1)
|
||||
#define TIM2_CNTRH (*(volatile uint8_t *)(TIM2_BASE + 0x0C))
|
||||
#define TIM2_CNTRL (*(volatile uint8_t *)(TIM2_BASE + 0x0D))
|
||||
#define TIM2_PSCR (*(volatile uint8_t *)(TIM2_BASE + 0x0E))
|
||||
|
@ -2470,36 +2472,36 @@ typedef struct {
|
|||
|
||||
// Block: TIM3
|
||||
#define TIM3_CR1 (*(volatile uint8_t *)(TIM3_BASE + 0x00))
|
||||
#define TIM3_CR1_CEN (1 << 0)
|
||||
#define TIM3_CR1_UDIS (1 << 1)
|
||||
#define TIM3_CR1_URS (1 << 2)
|
||||
#define TIM3_CR1_OPM (1 << 3)
|
||||
#define TIM3_CR1_APRE (1 << 7)
|
||||
#define TIM3_CR1_CEN (1U << 0)
|
||||
#define TIM3_CR1_UDIS (1U << 1)
|
||||
#define TIM3_CR1_URS (1U << 2)
|
||||
#define TIM3_CR1_OPM (1U << 3)
|
||||
#define TIM3_CR1_APRE (1U << 7)
|
||||
#define TIM3_IER (*(volatile uint8_t *)(TIM3_BASE + 0x01))
|
||||
#define TIM3_IER_UIE (1 << 0)
|
||||
#define TIM3_IER_CC1IE (1 << 1)
|
||||
#define TIM3_IER_CC2IE (1 << 2)
|
||||
#define TIM3_IER_CC3IE (1 << 3)
|
||||
#define TIM3_IER_TIE (1 << 6)
|
||||
#define TIM3_IER_UIE (1U << 0)
|
||||
#define TIM3_IER_CC1IE (1U << 1)
|
||||
#define TIM3_IER_CC2IE (1U << 2)
|
||||
#define TIM3_IER_CC3IE (1U << 3)
|
||||
#define TIM3_IER_TIE (1U << 6)
|
||||
#define TIM3_SR1 (*(volatile uint8_t *)(TIM3_BASE + 0x02))
|
||||
#define TIM3_SR1_UIF (1 << 0)
|
||||
#define TIM3_SR1_CC1IF (1 << 1)
|
||||
#define TIM3_SR1_CC2IF (1 << 2)
|
||||
#define TIM3_SR1_CC3IF (1 << 3)
|
||||
#define TIM3_SR1_TIF (1 << 6)
|
||||
#define TIM3_SR1_UIF (1U << 0)
|
||||
#define TIM3_SR1_CC1IF (1U << 1)
|
||||
#define TIM3_SR1_CC2IF (1U << 2)
|
||||
#define TIM3_SR1_CC3IF (1U << 3)
|
||||
#define TIM3_SR1_TIF (1U << 6)
|
||||
#define TIM3_SR2 (*(volatile uint8_t *)(TIM3_BASE + 0x03))
|
||||
#define TIM3_SR2_CC1OF (1 << 1)
|
||||
#define TIM3_SR2_CC2OF (1 << 2)
|
||||
#define TIM3_SR2_CC3OF (1 << 3)
|
||||
#define TIM3_SR2_CC1OF (1U << 1)
|
||||
#define TIM3_SR2_CC2OF (1U << 2)
|
||||
#define TIM3_SR2_CC3OF (1U << 3)
|
||||
#define TIM3_EGR (*(volatile uint8_t *)(TIM3_BASE + 0x04))
|
||||
#define TIM3_EGR_CC1G (1 << 1)
|
||||
#define TIM3_EGR_CC2G (1 << 2)
|
||||
#define TIM3_EGR_CC3G (1 << 3)
|
||||
#define TIM3_EGR_TG (1 << 6)
|
||||
#define TIM3_EGR_CC1G (1U << 1)
|
||||
#define TIM3_EGR_CC2G (1U << 2)
|
||||
#define TIM3_EGR_CC3G (1U << 3)
|
||||
#define TIM3_EGR_TG (1U << 6)
|
||||
#define TIM3_CCMR1 (*(volatile uint8_t *)(TIM3_BASE + 0x05))
|
||||
#define TIM3_CCMR1_CC1S_OFFSET 0
|
||||
#define TIM3_CCMR1_CC1S_MASK 0x3
|
||||
#define TIM3_CCMR1_CC1PE (1 << 3)
|
||||
#define TIM3_CCMR1_CC1PE (1U << 3)
|
||||
#define TIM3_CCMR1_OC1M_OFFSET 4
|
||||
#define TIM3_CCMR1_OC1M_MASK 0x7
|
||||
#define TIM3_CCMR1_CC1S_OFFSET 0
|
||||
|
@ -2511,7 +2513,7 @@ typedef struct {
|
|||
#define TIM3_CCMR2 (*(volatile uint8_t *)(TIM3_BASE + 0x06))
|
||||
#define TIM3_CCMR2_CC2S_OFFSET 0
|
||||
#define TIM3_CCMR2_CC2S_MASK 0x3
|
||||
#define TIM3_CCMR2_CC2PE (1 << 3)
|
||||
#define TIM3_CCMR2_CC2PE (1U << 3)
|
||||
#define TIM3_CCMR2_OC2M_OFFSET 4
|
||||
#define TIM3_CCMR2_OC2M_MASK 0x7
|
||||
#define TIM3_CCMR2_CC2S_OFFSET 0
|
||||
|
@ -2521,10 +2523,10 @@ typedef struct {
|
|||
#define TIM3_CCMR2_IC2F_OFFSET 4
|
||||
#define TIM3_CCMR2_IC2F_MASK 0xf
|
||||
#define TIM3_CCER1 (*(volatile uint8_t *)(TIM3_BASE + 0x07))
|
||||
#define TIM3_CCER1_CC1E (1 << 0)
|
||||
#define TIM3_CCER1_CC1P (1 << 1)
|
||||
#define TIM3_CCER1_CC2E (1 << 4)
|
||||
#define TIM3_CCER1_CC2P (1 << 5)
|
||||
#define TIM3_CCER1_CC1E (1U << 0)
|
||||
#define TIM3_CCER1_CC1P (1U << 1)
|
||||
#define TIM3_CCER1_CC2E (1U << 4)
|
||||
#define TIM3_CCER1_CC2P (1U << 5)
|
||||
#define TIM3_CNTRH (*(volatile uint8_t *)(TIM3_BASE + 0x08))
|
||||
#define TIM3_CNTRL (*(volatile uint8_t *)(TIM3_BASE + 0x09))
|
||||
#define TIM3_PSCR (*(volatile uint8_t *)(TIM3_BASE + 0x0A))
|
||||
|
@ -2548,11 +2550,11 @@ typedef struct {
|
|||
|
||||
// Block: TIM5
|
||||
#define TIM5_CR1 (*(volatile uint8_t *)(TIM5_BASE + 0x00))
|
||||
#define TIM5_CR1_CEN (1 << 0)
|
||||
#define TIM5_CR1_UDIS (1 << 1)
|
||||
#define TIM5_CR1_URS (1 << 2)
|
||||
#define TIM5_CR1_OPM (1 << 3)
|
||||
#define TIM5_CR1_APRE (1 << 7)
|
||||
#define TIM5_CR1_CEN (1U << 0)
|
||||
#define TIM5_CR1_UDIS (1U << 1)
|
||||
#define TIM5_CR1_URS (1U << 2)
|
||||
#define TIM5_CR1_OPM (1U << 3)
|
||||
#define TIM5_CR1_APRE (1U << 7)
|
||||
#define TIM5_CR2 (*(volatile uint8_t *)(TIM5_BASE + 0x01))
|
||||
#define TIM5_CR2_MMS_OFFSET 4
|
||||
#define TIM5_CR2_MMS_MASK 0x07
|
||||
|
@ -2561,32 +2563,32 @@ typedef struct {
|
|||
#define TIM5_SMCR_SMS_MASK 0x07
|
||||
#define TIM5_SMCR_TS_OFFSET 4
|
||||
#define TIM5_SMCR_TS_MASK 0x07
|
||||
#define TIM5_SMCR_MSM (1 << 7)
|
||||
#define TIM5_SMCR_MSM (1U << 7)
|
||||
#define TIM5_IER (*(volatile uint8_t *)(TIM5_BASE + 0x03))
|
||||
#define TIM5_IER_UIE (1 << 0)
|
||||
#define TIM5_IER_CC1IE (1 << 1)
|
||||
#define TIM5_IER_CC2IE (1 << 2)
|
||||
#define TIM5_IER_CC3IE (1 << 3)
|
||||
#define TIM5_IER_TIE (1 << 6)
|
||||
#define TIM5_IER_UIE (1U << 0)
|
||||
#define TIM5_IER_CC1IE (1U << 1)
|
||||
#define TIM5_IER_CC2IE (1U << 2)
|
||||
#define TIM5_IER_CC3IE (1U << 3)
|
||||
#define TIM5_IER_TIE (1U << 6)
|
||||
#define TIM5_SR1 (*(volatile uint8_t *)(TIM5_BASE + 0x04))
|
||||
#define TIM5_SR1_UIF (1 << 0)
|
||||
#define TIM5_SR1_CC1IF (1 << 1)
|
||||
#define TIM5_SR1_CC2IF (1 << 2)
|
||||
#define TIM5_SR1_CC3IF (1 << 3)
|
||||
#define TIM5_SR1_TIF (1 << 6)
|
||||
#define TIM5_SR1_UIF (1U << 0)
|
||||
#define TIM5_SR1_CC1IF (1U << 1)
|
||||
#define TIM5_SR1_CC2IF (1U << 2)
|
||||
#define TIM5_SR1_CC3IF (1U << 3)
|
||||
#define TIM5_SR1_TIF (1U << 6)
|
||||
#define TIM5_SR2 (*(volatile uint8_t *)(TIM5_BASE + 0x05))
|
||||
#define TIM5_SR2_CC1OF (1 << 1)
|
||||
#define TIM5_SR2_CC2OF (1 << 2)
|
||||
#define TIM5_SR2_CC3OF (1 << 3)
|
||||
#define TIM5_SR2_CC1OF (1U << 1)
|
||||
#define TIM5_SR2_CC2OF (1U << 2)
|
||||
#define TIM5_SR2_CC3OF (1U << 3)
|
||||
#define TIM5_EGR (*(volatile uint8_t *)(TIM5_BASE + 0x06))
|
||||
#define TIM5_EGR_CC1G (1 << 1)
|
||||
#define TIM5_EGR_CC2G (1 << 2)
|
||||
#define TIM5_EGR_CC3G (1 << 3)
|
||||
#define TIM5_EGR_TG (1 << 6)
|
||||
#define TIM5_EGR_CC1G (1U << 1)
|
||||
#define TIM5_EGR_CC2G (1U << 2)
|
||||
#define TIM5_EGR_CC3G (1U << 3)
|
||||
#define TIM5_EGR_TG (1U << 6)
|
||||
#define TIM5_CCMR1 (*(volatile uint8_t *)(TIM5_BASE + 0x07))
|
||||
#define TIM5_CCMR1_CC1S_OFFSET 0
|
||||
#define TIM5_CCMR1_CC1S_MASK 0x3
|
||||
#define TIM5_CCMR1_CC1PE (1 << 3)
|
||||
#define TIM5_CCMR1_CC1PE (1U << 3)
|
||||
#define TIM5_CCMR1_OC1M_OFFSET 4
|
||||
#define TIM5_CCMR1_OC1M_MASK 0x7
|
||||
#define TIM5_CCMR1_CC1S_OFFSET 0
|
||||
|
@ -2598,7 +2600,7 @@ typedef struct {
|
|||
#define TIM5_CCMR2 (*(volatile uint8_t *)(TIM5_BASE + 0x08))
|
||||
#define TIM5_CCMR2_CC2S_OFFSET 0
|
||||
#define TIM5_CCMR2_CC2S_MASK 0x3
|
||||
#define TIM5_CCMR2_CC2PE (1 << 3)
|
||||
#define TIM5_CCMR2_CC2PE (1U << 3)
|
||||
#define TIM5_CCMR2_OC2M_OFFSET 4
|
||||
#define TIM5_CCMR2_OC2M_MASK 0x7
|
||||
#define TIM5_CCMR2_CC2S_OFFSET 0
|
||||
|
@ -2610,7 +2612,7 @@ typedef struct {
|
|||
#define TIM5_CCMR3 (*(volatile uint8_t *)(TIM5_BASE + 0x09))
|
||||
#define TIM5_CCMR3_CC3S_OFFSET 0
|
||||
#define TIM5_CCMR3_CC3S_MASK 0x3
|
||||
#define TIM5_CCMR3_CC3PE (1 << 3)
|
||||
#define TIM5_CCMR3_CC3PE (1U << 3)
|
||||
#define TIM5_CCMR3_OC3M_OFFSET 4
|
||||
#define TIM5_CCMR3_OC3M_MASK 0x7
|
||||
#define TIM5_CCMR3_CC3S_OFFSET 0
|
||||
|
@ -2620,13 +2622,13 @@ typedef struct {
|
|||
#define TIM5_CCMR3_IC3F_OFFSET 4
|
||||
#define TIM5_CCMR3_IC3F_MASK 0xf
|
||||
#define TIM5_CCER1 (*(volatile uint8_t *)(TIM5_BASE + 0x0A))
|
||||
#define TIM5_CCER1_CC1E (1 << 0)
|
||||
#define TIM5_CCER1_CC1P (1 << 1)
|
||||
#define TIM5_CCER1_CC2E (1 << 4)
|
||||
#define TIM5_CCER1_CC2P (1 << 5)
|
||||
#define TIM5_CCER1_CC1E (1U << 0)
|
||||
#define TIM5_CCER1_CC1P (1U << 1)
|
||||
#define TIM5_CCER1_CC2E (1U << 4)
|
||||
#define TIM5_CCER1_CC2P (1U << 5)
|
||||
#define TIM5_CCER2 (*(volatile uint8_t *)(TIM5_BASE + 0x0B))
|
||||
#define TIM5_CCER2_CC3E (1 << 0)
|
||||
#define TIM5_CCER2_CC3P (1 << 1)
|
||||
#define TIM5_CCER2_CC3E (1U << 0)
|
||||
#define TIM5_CCER2_CC3P (1U << 1)
|
||||
#define TIM5_CNTRH (*(volatile uint8_t *)(TIM5_BASE + 0x0C))
|
||||
#define TIM5_CNTRL (*(volatile uint8_t *)(TIM5_BASE + 0x0D))
|
||||
#define TIM5_PSCR (*(volatile uint8_t *)(TIM5_BASE + 0x0E))
|
||||
|
@ -2916,13 +2918,13 @@ typedef struct {
|
|||
#define CPU_SPH (*(volatile uint8_t *)(CPU_BASE + 0x08))
|
||||
#define CPU_SPL (*(volatile uint8_t *)(CPU_BASE + 0x09))
|
||||
#define CPU_CCR (*(volatile uint8_t *)(CPU_BASE + 0x0A))
|
||||
#define CPU_CCR_C (1 << 0)
|
||||
#define CPU_CCR_Z (1 << 1)
|
||||
#define CPU_CCR_N (1 << 2)
|
||||
#define CPU_CCR_I0 (1 << 3)
|
||||
#define CPU_CCR_H (1 << 4)
|
||||
#define CPU_CCR_I1 (1 << 5)
|
||||
#define CPU_CCR_V (1 << 7)
|
||||
#define CPU_CCR_C (1U << 0)
|
||||
#define CPU_CCR_Z (1U << 1)
|
||||
#define CPU_CCR_N (1U << 2)
|
||||
#define CPU_CCR_I0 (1U << 3)
|
||||
#define CPU_CCR_H (1U << 4)
|
||||
#define CPU_CCR_I1 (1U << 5)
|
||||
#define CPU_CCR_V (1U << 7)
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
|
@ -3394,46 +3396,46 @@ typedef struct {
|
|||
#define NUBC NOPT1
|
||||
#define OPT2 (*(volatile uint8_t *)(OPT_BASE + 0x03))
|
||||
#define AFR OPT2
|
||||
#define AFR_AFR0 (1 << 0)
|
||||
#define AFR_AFR1 (1 << 1)
|
||||
#define AFR_AFR2 (1 << 2)
|
||||
#define AFR_AFR3 (1 << 3)
|
||||
#define AFR_AFR4 (1 << 4)
|
||||
#define AFR_AFR5 (1 << 5)
|
||||
#define AFR_AFR6 (1 << 6)
|
||||
#define AFR_AFR7 (1 << 7)
|
||||
#define AFR_AFR0 (1U << 0)
|
||||
#define AFR_AFR1 (1U << 1)
|
||||
#define AFR_AFR2 (1U << 2)
|
||||
#define AFR_AFR3 (1U << 3)
|
||||
#define AFR_AFR4 (1U << 4)
|
||||
#define AFR_AFR5 (1U << 5)
|
||||
#define AFR_AFR6 (1U << 6)
|
||||
#define AFR_AFR7 (1U << 7)
|
||||
#define NOPT2 (*(volatile uint8_t *)(OPT_BASE + 0x04))
|
||||
#define NAFR NOPT2
|
||||
#define AFR_NAFR0 (1 << 0)
|
||||
#define AFR_NAFR1 (1 << 1)
|
||||
#define AFR_NAFR2 (1 << 2)
|
||||
#define AFR_NAFR3 (1 << 3)
|
||||
#define AFR_NAFR4 (1 << 4)
|
||||
#define AFR_NAFR5 (1 << 5)
|
||||
#define AFR_NAFR6 (1 << 6)
|
||||
#define AFR_NAFR7 (1 << 7)
|
||||
#define AFR_NAFR0 (1U << 0)
|
||||
#define AFR_NAFR1 (1U << 1)
|
||||
#define AFR_NAFR2 (1U << 2)
|
||||
#define AFR_NAFR3 (1U << 3)
|
||||
#define AFR_NAFR4 (1U << 4)
|
||||
#define AFR_NAFR5 (1U << 5)
|
||||
#define AFR_NAFR6 (1U << 6)
|
||||
#define AFR_NAFR7 (1U << 7)
|
||||
#define OPT3 (*(volatile uint8_t *)(OPT_BASE + 0x05))
|
||||
#define OPT3_WWDG_HALT (1 << 0)
|
||||
#define OPT3_WWDG_HW (1 << 1)
|
||||
#define OPT3_IWDG_HW (1 << 2)
|
||||
#define OPT3_LSI_EN (1 << 3)
|
||||
#define OPT3_HSI_TRIM (1 << 4)
|
||||
#define OPT3_WWDG_HALT (1U << 0)
|
||||
#define OPT3_WWDG_HW (1U << 1)
|
||||
#define OPT3_IWDG_HW (1U << 2)
|
||||
#define OPT3_LSI_EN (1U << 3)
|
||||
#define OPT3_HSI_TRIM (1U << 4)
|
||||
#define NOPT3 (*(volatile uint8_t *)(OPT_BASE + 0x06))
|
||||
#define NOPT3_NWWDG_HALT (1 << 0)
|
||||
#define NOPT3_NWWDG_HW (1 << 1)
|
||||
#define NOPT3_NIWDG_HW (1 << 2)
|
||||
#define NOPT3_NLSI_EN (1 << 3)
|
||||
#define NOPT3_NHSI_TRIM (1 << 4)
|
||||
#define NOPT3_NWWDG_HALT (1U << 0)
|
||||
#define NOPT3_NWWDG_HW (1U << 1)
|
||||
#define NOPT3_NIWDG_HW (1U << 2)
|
||||
#define NOPT3_NLSI_EN (1U << 3)
|
||||
#define NOPT3_NHSI_TRIM (1U << 4)
|
||||
#define OPT4 (*(volatile uint8_t *)(OPT_BASE + 0x07))
|
||||
#define OPT4_PRS_C0 (1 << 0)
|
||||
#define OPT4_PRS_C1 (1 << 1)
|
||||
#define OPT4_CKAWU_SEL (1 << 2)
|
||||
#define OPT4_EXT_CLK (1 << 3)
|
||||
#define OPT4_PRS_C0 (1U << 0)
|
||||
#define OPT4_PRS_C1 (1U << 1)
|
||||
#define OPT4_CKAWU_SEL (1U << 2)
|
||||
#define OPT4_EXT_CLK (1U << 3)
|
||||
#define NOPT4 (*(volatile uint8_t *)(OPT_BASE + 0x08))
|
||||
#define NOPT4_NPRS_C0 (1 << 0)
|
||||
#define NOPT4_NPRS_C1 (1 << 1)
|
||||
#define NOPT4_NCKAWU_SEL (1 << 2)
|
||||
#define NOPT4_NEXT_CLK (1 << 3)
|
||||
#define NOPT4_NPRS_C0 (1U << 0)
|
||||
#define NOPT4_NPRS_C1 (1U << 1)
|
||||
#define NOPT4_NCKAWU_SEL (1U << 2)
|
||||
#define NOPT4_NEXT_CLK (1U << 3)
|
||||
#define OPT5 (*(volatile uint8_t *)(OPT_BASE + 0x09))
|
||||
#define HSECNT OPT5
|
||||
#define NOPT5 (*(volatile uint8_t *)(OPT_BASE + 0x0A))
|
||||
|
|
Loading…
Reference in New Issue