From 446a25393664cdbb4e47ce4cdcbb6db6a4082320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Mon, 8 Aug 2022 22:30:19 +0200 Subject: [PATCH] Add I2C communication. Start writing DMX TX. Break generation already implemented --- .gitignore | 7 +++ Makefile | 8 +-- dmx.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++ i2c.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dmx.h | 14 +++++ include/i2c.h | 14 +++++ main.c | 55 +++++++++++++++++++- 7 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 dmx.c create mode 100644 i2c.c create mode 100644 include/dmx.h create mode 100644 include/i2c.h diff --git a/.gitignore b/.gitignore index d720423..0e0f15e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,10 @@ *.elf *.lss *.d + +*.jdebug +*.jdebug.user + +# ClangD Ignopre files +.cache/* +compile_commands.json \ No newline at end of file diff --git a/Makefile b/Makefile index dc5f3d5..29935f7 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,9 @@ endif #Add Files and Folders below######################################################### -CFILES = main.c setup/system_init.c startup/startup_stm32f0xx.c systick.c +CFILES := main.c setup/system_init.c startup/startup_stm32f0xx.c systick.c +CFILES += i2c.c +CFILES += dmx.c ASFILES = INCLUDEPATH = -Iinclude -Iinclude/cmsis @@ -35,11 +37,11 @@ OBJCOPY := $(CROSS_COMPILE)objcopy OBJDUMP := $(CROSS_COMPILE)objdump SIZE := $(CROSS_COMPILE)size -LFLAGS = -mlittle-endian -mthumb -mcpu=cortex-m0 -mthumb-interwork +LFLAGS = -mlittle-endian -mthumb -mcpu=cortex-m0 LFLAGS += -mfloat-abi=soft --disable-newlib-supplied-syscalls -nostartfiles LFLAGS += -Tstartup/stm32f030.ld -Wl,-Map=$(mapfile).map -Wl,--gc-sections -Wl,--print-memory-usage -g -CFLAGS = -c -fmessage-length=0 -mlittle-endian -mthumb -mcpu=cortex-m0 -mthumb-interwork +CFLAGS = -c -fmessage-length=0 -mlittle-endian -mthumb -mcpu=cortex-m0 CFLAGS += -mfloat-abi=soft -nostartfiles -Wall -g3 -O0 #################################################################################### diff --git a/dmx.c b/dmx.c new file mode 100644 index 0000000..7bfcce4 --- /dev/null +++ b/dmx.c @@ -0,0 +1,134 @@ +#include "stm32f030x6.h" +#include +#include +#include +#include +#include + +static GPIO_TypeDef *dmx_tx_port; +static uint8_t dmx_tx_pin; +static uint32_t dmx_universe_length; +static uint8_t *dmx_data_ptr; +static uint16_t dmx_tx_break_len; +static uint16_t dmx_tx_break_pause; + +enum dmx_tx_state { + DMX_TX_OFF, + DMX_TX_IDLE, + DMX_TX_BREAK, + DMX_TX_BREAK_PAUSE, + DMX_TX_NULLBYTE, + DMX_TX_DATA, +}; + +static volatile enum dmx_tx_state tx_state; + +/* USART1 TX is mapped on DMA Channel2 */ + +static void dmx_break(bool break_enable) +{ + uint32_t tmp; + + /* Force pin to low */ + dmx_tx_port->BSRR |= (1 << (dmx_tx_pin + 16)); + + /* Change pin mode from alternate function (UART) to output and vice versa */ + tmp = dmx_tx_port->MODER; + if (break_enable) { + tmp &= MODER_DELETE(dmx_tx_pin); + tmp |= OUTPUT(dmx_tx_pin); + } else { + tmp &= MODER_DELETE(dmx_tx_pin); + tmp |= ALTFUNC(dmx_tx_pin); + } + dmx_tx_port->MODER = tmp; +} + + +void dmx_init(uint8_t *data, uint32_t universe_length, GPIO_TypeDef *tx_port, uint8_t tx_pin, + uint16_t dmx_delay, uint16_t dmx_break_len, uint16_t break_pause) +{ + dmx_tx_pin = tx_pin; + dmx_tx_port = tx_port; + dmx_data_ptr = data; + dmx_tx_break_len = dmx_break_len; + dmx_tx_break_pause = break_pause; + + /* Enable UART1 and TIM14 clock */ + RCC->APB2ENR |= RCC_APB2ENR_USART1EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM14EN; + + /* Enable DMA clock */ + RCC->AHBENR |= RCC_AHBENR_DMAEN; + + /* Set baudrate: 48MHz / 250k = 129 */ + USART1->BRR = 192u; + + /* Two stop bits */ + USART1->CR2 = USART_CR2_STOP_1; + + /* Transmitter enable, USART enable */ + USART1->CR1 = USART_CR1_TE | USART_CR1_UE; + + /* Configure TIM14 to count in 10 us steps */ + TIM14->PSC = 480u; + + /* Configure the reload value. Must be higher or equal to 200 */ + if (dmx_delay < 2000) + dmx_delay = 2000; + TIM14->ARR = dmx_delay - 1; + + /* Enable TIM14 interrupt on update */ + TIM14->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE; + NVIC_EnableIRQ(TIM14_IRQn); + + +} + +void dmx_stream_start(void) +{ + tx_state = DMX_TX_IDLE; + TIM14->CR1 |= TIM_CR1_CEN; +} + +void dmx_stream_stop(void) +{ + tx_state = DMX_TX_OFF; + TIM14->CR1 &= ~TIM_CR1_CEN; + DMA1_Channel2->CCR = 0ul; + __DSB(); + USART1->CR3 = 0ul; +} + + +void TIM14_IRQHandler(void) +{ + /* Clear interrupt sources */ + TIM14->SR = 0; + + /* Start the break sequence if idle */ + switch (tx_state) { + case DMX_TX_IDLE: + tx_state = DMX_TX_BREAK; + /* Disable the DMA transfer */ + USART1->CR3 = 0ul; + + TIM14->CCR1 = dmx_tx_break_len; + + /* Send break */ + dmx_break(true); + break; + + case DMX_TX_BREAK: + /* Stop break pulse */ + dmx_break(false); + TIM14->CCR1 = dmx_tx_break_len + dmx_tx_break_pause; + tx_state = DMX_TX_BREAK_PAUSE; + break; + case DMX_TX_BREAK_PAUSE: + tx_state = DMX_TX_IDLE; + break; + default: + break; + } +} \ No newline at end of file diff --git a/i2c.c b/i2c.c new file mode 100644 index 0000000..23b6cd0 --- /dev/null +++ b/i2c.c @@ -0,0 +1,138 @@ +#include "stm32f030x6.h" +#include +#include + +void i2c_init(void) +{ + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; + + /* Use SYSCLK for I2C, because HSI (default) is disabled */ + RCC->CFGR3 |= RCC_CFGR3_I2C1SW; + + i2c_reset(); + + /* Setup clocks */ + I2C1->TIMINGR = ((6 << 28) & I2C_TIMINGR_PRESC) | ((8 << 20) & I2C_TIMINGR_SCLDEL) | + ((8 << 16) & I2C_TIMINGR_SDADEL) | ((0x20 << 8) & I2C_TIMINGR_SCLH) | + ((0x20 << 0) & I2C_TIMINGR_SCLL); + + + /* Peripheral enable */ + I2C1->CR1 = I2C_CR1_PE; +} + +int i2c_write(uint8_t i2c_addr, const uint8_t *data, uint8_t len) +{ + uint32_t isr; + + /* Clear stop flag */ + I2C1->ICR |= I2C_ICR_STOPCF; + + /* Setup len bytes for write transfer */ + I2C1->CR2 = I2C_CR2_AUTOEND | ((len << 16 ) & I2C_CR2_NBYTES) | (i2c_addr & 0xFE) | I2C_CR2_START; + + do { + isr = I2C1->ISR; + if (isr & I2C_ISR_NACKF) { + /* NACK received */ + return -1; + } else if (isr & I2C_ISR_TXIS) { + /* I2C ready to get data to send */ + I2C1->TXDR = (uint32_t)*data; + data++; + len--; + } + } while (len > 0); + + /* All data transferred. Wait for automatically generated stop */ + while (!(I2C1->ISR & I2C_ISR_STOPF)) { + if (I2C1->ISR & I2C_ISR_NACKF) { + return -1; + } + } + + /* Clear stop flag */ + I2C1->ICR |= I2C_ICR_STOPCF; + + return 0; +} + +int i2c_read(uint8_t i2c_addr, uint8_t command, uint8_t *data, uint8_t len) +{ + int run; + int err; + uint32_t isr; + + if (!data || !len) { + return -1000; + } + + /* Clear stop flag */ + I2C1->ICR |= I2C_ICR_STOPCF; + + /* Setup len bytes for write transfer of command */ + I2C1->CR2 = ((1 << 16 ) & I2C_CR2_NBYTES) | (i2c_addr & 0xFE) | I2C_CR2_START; + + err = 0; + run = 1; + do { + isr = I2C1->ISR; + + if (isr & I2C_ISR_NACKF) { + err = 1; + run = 0; + } else if (isr & I2C_ISR_TXIS) { + I2C1->TXDR = (uint32_t)command; + run = 0; + } + } while (run); + + if (err) { + return -1; + } + + /* Wait for command to be transmitted */ + run = 1; + err = 0; + do { + isr = I2C1->ISR; + + if (isr & I2C_ISR_NACKF) { + err = 1; + run = 0; + } else if (isr & I2C_ISR_TC) { + run = 0; + } + } while (run); + + if (err) { + return -1; + } + + /* Setup read request */ + I2C1->CR2 = I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | ((len << 16 ) & I2C_CR2_NBYTES) | (i2c_addr & 0xFE) | I2C_CR2_START; + while (len) { + if (I2C1->ISR & I2C_ISR_RXNE) { + *data = I2C1->RXDR; + data++; + len--; + } + } + + while (!(I2C1->ISR & I2C_ISR_STOPF)) { + if (I2C1->ISR & I2C_ISR_NACKF) { + return -1; + } + } + + /* Clear stop flag */ + I2C1->ICR |= I2C_ICR_STOPCF; + + return 0; +} + +void i2c_reset(void) +{ + I2C1->CR1 = 0; + I2C1->CR2 = 0; +} \ No newline at end of file diff --git a/include/dmx.h b/include/dmx.h new file mode 100644 index 0000000..151dc10 --- /dev/null +++ b/include/dmx.h @@ -0,0 +1,14 @@ +#ifndef _DMX_H_ +#define _DMX_H_ + +#include + + +void dmx_init(uint8_t *data, uint32_t universe_length, GPIO_TypeDef *tx_port, uint8_t tx_pin, + uint16_t dmx_delay, uint16_t dmx_break_len, uint16_t break_pause); + +void dmx_stream_start(void); + +void dmx_stream_stop(void); + +#endif /* _DMX_H_ */ \ No newline at end of file diff --git a/include/i2c.h b/include/i2c.h new file mode 100644 index 0000000..d2ffa61 --- /dev/null +++ b/include/i2c.h @@ -0,0 +1,14 @@ +#ifndef _I2C_H_ +#define _I2C_H_ + +#include + +void i2c_init(void); + +void i2c_reset(void); + +int i2c_write(uint8_t i2c_addr, const uint8_t *data, uint8_t len); + +int i2c_read(uint8_t i2c_addr, uint8_t command, uint8_t *data, uint8_t len); + +#endif /* _I2C_H_ */ \ No newline at end of file diff --git a/main.c b/main.c index 1a96a33..3eb0b90 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,21 @@ +#include +#include #include #include #include +#include + +#define PCA9555_ADDR (0x40) +#define PCA9555_REG_IN_PORT0 (0x0) +#define PCA9555_REG_IN_PORT1 (0x1) +#define PCA9555_REG_OUT_PORT0 (0x2) +#define PCA9555_REG_OUT_PORT1 (0x3) +#define PCA9555_REG_POLARITY0 (0x4) +#define PCA9555_REG_POLARITY1 (0x5) +#define PCA9555_REG_CONFIG0 (0x6) +#define PCA9555_REG_CONFIG1 (0x7) + +static uint8_t dmx_universe[129]; static void setup_pins(void) { @@ -45,14 +60,50 @@ static void setup_pins(void) int main(void) { + uint8_t i2c_command[2]; + uint8_t port; + uint32_t odr; + setup_pins(); + i2c_init(); + dmx_init(dmx_universe, sizeof(dmx_universe), GPIOA, 2u, 2300u, 10u, 5u); + + /* Setup Systick for 1ms ticks */ SysTick_Config(48000000UL/1000); GPIOA->ODR |= (1<<5) | (1<<7); + /* Setup PCA */ + i2c_command[0] = PCA9555_REG_CONFIG1; + i2c_command[1] = 0x00; + i2c_write(PCA9555_ADDR, i2c_command, 2u); + + i2c_command[0] = PCA9555_REG_CONFIG0; + i2c_command[1] = 0xFF; + i2c_write(PCA9555_ADDR, i2c_command, 2u); + + dmx_stream_start(); + /* Blink the LEDs */ while (1) { - systick_wait_ms(250); - GPIOA->ODR ^= (1 << 5) | (1 << 6) | (1 << 7); + systick_wait_ms(3); + i2c_read(PCA9555_ADDR, PCA9555_REG_IN_PORT0, &port, 1u); + i2c_command[0] = PCA9555_REG_OUT_PORT1; + i2c_command[1] = ~port; + odr = GPIOA->ODR; + odr &= ~((1<<5) | (1<<6) | (1<<7)); + if (port & (1<<0)) { + odr |= (1<<5); + } + if (port & (1<<1)) { + odr |= (1<<6); + } + if (port & (1<<2)) { + odr |= (1<<7); + } + GPIOA->ODR = odr; + + i2c_write(PCA9555_ADDR, i2c_command, 2u); + } }