Compare commits
16 Commits
2028e485d8
...
82952ddda3
Author | SHA1 | Date |
---|---|---|
King Kévin | 82952ddda3 | |
King Kévin | 0e3247bf73 | |
King Kévin | 6ebdb31e07 | |
King Kévin | 61a427c18a | |
King Kévin | e44110d315 | |
King Kévin | b4234fedd9 | |
King Kévin | 426cd48d0f | |
King Kévin | bccf187c1a | |
King Kévin | 79d8860cc2 | |
King Kévin | 09ed3c91ea | |
King Kévin | 68def6b234 | |
King Kévin | d5e63eefd2 | |
King Kévin | f83af4bab8 | |
King Kévin | 3cdebf1cc8 | |
King Kévin | 1d93451981 | |
King Kévin | 99b2ccd62d |
|
@ -1,5 +1,4 @@
|
|||
[submodule "libopencm3"]
|
||||
path = libopencm3
|
||||
url = https://github.com/manuelbl/libopencm3
|
||||
url = https://github.com/libopencm3/libopencm3/
|
||||
ignore = all
|
||||
branch = no-vbus-sensing
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
||||
#include <libopencm3/stm32/desig.h> // design utilities
|
||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||
#include <libopencm3/usb/dwc/otg_fs.h> // USB definitions
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // board definitions
|
||||
|
@ -379,6 +380,8 @@ void main(void)
|
|||
board_setup(); // setup board
|
||||
uart_setup(); // setup USART (for printing)
|
||||
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
|
||||
OTG_FS_GCCFG |= OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; // disable VBUS sensing
|
||||
OTG_FS_GCCFG &= ~(OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN); // force USB device mode
|
||||
puts("\nwelcome to the CuVoodoo STM32F4 example firmware\n"); // print welcome message
|
||||
|
||||
#if DEBUG
|
||||
|
|
|
@ -37,9 +37,8 @@ void main(void)
|
|||
__dfu_magic[2] = 0;
|
||||
__dfu_magic[3] = 0;
|
||||
} else { // check if the force DFU mode input is set
|
||||
// disable SWJ pin to use as GPIO
|
||||
#if (defined(DFU_FORCE_PIN) && defined(DFU_FORCE_VALUE))
|
||||
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for button
|
||||
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for button
|
||||
#if (DFU_FORCE_VALUE == 1)
|
||||
gpio_mode_setup(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
|
||||
if (gpio_get(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode
|
||||
|
@ -49,9 +48,9 @@ void main(void)
|
|||
#endif // DFU_FORCE_VALUE
|
||||
dfu_force = true; // DFU mode forced
|
||||
}
|
||||
#endif // defined(DFU_FORCE_PIN)
|
||||
rcc_periph_reset_pulse(GPIO_RST(DFU_FORCE_PIN)); // reset pin GPIO domain
|
||||
rcc_periph_clock_disable(GPIO_RCC(DFU_FORCE_PIN)); // disable pin GPIO domain
|
||||
#endif // defined(DFU_FORCE_PIN)
|
||||
}
|
||||
|
||||
// start application if valid
|
||||
|
|
2
global.h
2
global.h
|
@ -404,6 +404,8 @@ int32_t adds32_safe(int32_t a, int32_t b);
|
|||
#define tim10_isr tim1_up_tim10_isr
|
||||
/** interrupt service routine for timer 11 */
|
||||
#define tim11_isr tim1_trg_com_tim11_isr
|
||||
/** get TIM_OC based on CHx identifier */
|
||||
#define TIM_OC(x) CAT2(TIM_OC,x)
|
||||
/** get TIM_IC based on CHx identifier */
|
||||
#define TIM_IC(x) CAT2(TIM_IC,x)
|
||||
/** get TIM_IC_IN_TI based on CHx identifier */
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/** control RGB LED matrix panels through shift registers
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2022
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/dma.h> // DMA library
|
||||
#include <libopencm3/stm32/timer.h> // timer library
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
|
||||
#include "led_rgbpanel.h" // library API
|
||||
#include "global.h" // common methods
|
||||
|
||||
// RGB matrix pins (warning: hard coded)
|
||||
#define RGBPANEL_OE_PIN PB10 /**< pin to enable output (active low) */
|
||||
#define RGBPANEL_A_PIN PB0 /**< pin to select line */
|
||||
#define RGBPANEL_B_PIN PB1 /**< pin to select line */
|
||||
#define RGBPANEL_C_PIN PB2 /**< pin to select line */
|
||||
#define RGBPANEL_D_PIN PB3 /**< pin to select line */
|
||||
#define RGBPANEL_CLK_PIN PA0 /**< pin to generate clock for serial data */
|
||||
#define RGBPANEL_LAT_PIN PA1 /**< pin to latch data on rising edge */
|
||||
#define RGBPANEL_R1_PIN PA2 /**< pin to enable red color on top half */
|
||||
#define RGBPANEL_G1_PIN PA3 /**< pin to enable green color on top half */
|
||||
#define RGBPANEL_B1_PIN PA4 /**< pin to enable blue color on top half */
|
||||
#define RGBPANEL_R2_PIN PA5 /**< pin to enable red color on bottom half */
|
||||
#define RGBPANEL_G2_PIN PA6 /**< pin to enable green color on bottom half */
|
||||
#define RGBPANEL_B2_PIN PA7 /**< pin to enable blue color on bottom half */
|
||||
|
||||
#define RGBPANEL_DMA DMA2 /**< DMA used to send data to the RGB matrix (only DMA2 can be used for memory-to-memory transfer) */
|
||||
#define RGBPANEL_RCC_DMA RCC_DMA2 /**< RCC for DMA used for the RGB matrix */
|
||||
#define RGBPANEL_STREAM DMA_STREAM1 /**< stream used to send data to the RGB matrix (any stream can be used for memory-to-memory transfer) */
|
||||
#define RGBPANEL_CHANNEL DMA_SxCR_CHSEL_0 /**< channel used to send data to the RGB matrix (any channel can be used for memory-to-memory transfer) */
|
||||
#define RGBPANEL_IRQ NVIC_DMA2_STREAM1_IRQ /**< IRQ for when a line transfer is complete */
|
||||
#define RGBPANEL_ISR dma2_stream1_isr /**< ISR for when a line transfer is complete */
|
||||
#define RGBPANEL_TIMER 3 /**< timer to update lines */
|
||||
|
||||
static uint8_t rgbpanel_data[RGBPANEL_HEIGHT / 2][RGBPANEL_WIDTH * 2]; /**< data to be sent to RGB matrix (one byte includes upper and lower half values, each byte has 2 clock edges) */
|
||||
static volatile uint8_t rgbpanel_line = 0; // line being transferred
|
||||
|
||||
void rgbpanel_setup(void)
|
||||
{
|
||||
// configure pin for output enable
|
||||
rcc_periph_clock_enable(GPIO_RCC(RGBPANEL_OE_PIN)); // enable clock for GPIO port peripheral
|
||||
gpio_set(GPIO_PORT(RGBPANEL_OE_PIN), GPIO_PIN(RGBPANEL_OE_PIN)); // disable output
|
||||
gpio_set_output_options(GPIO_PORT(RGBPANEL_OE_PIN), GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN(RGBPANEL_OE_PIN)); // set fast edge
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_OE_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_OE_PIN)); // set pin as output
|
||||
|
||||
// configure pins for data and clock lines
|
||||
const uint32_t rgbpanel_serial_port = GPIO_PORT(RGBPANEL_LAT_PIN); // common port for pins controlling the serial data
|
||||
const uint16_t rgbpanel_serial_pins = GPIO_PIN(RGBPANEL_LAT_PIN) | GPIO_PIN(RGBPANEL_CLK_PIN) | GPIO_PIN(RGBPANEL_R1_PIN) | GPIO_PIN(RGBPANEL_G1_PIN) | GPIO_PIN(RGBPANEL_B1_PIN) | GPIO_PIN(RGBPANEL_R2_PIN) | GPIO_PIN(RGBPANEL_G2_PIN) | GPIO_PIN(RGBPANEL_B2_PIN); // pins controlling the serial data
|
||||
rcc_periph_clock_enable(GPIO_RCC(RGBPANEL_LAT_PIN)); // enable clock for GPIO port peripheral
|
||||
gpio_clear(rgbpanel_serial_port, rgbpanel_serial_pins); // disable LEDs
|
||||
gpio_set_output_options(rgbpanel_serial_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, rgbpanel_serial_pins); // set fast edge
|
||||
gpio_mode_setup(rgbpanel_serial_port, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, rgbpanel_serial_pins); // set pin as output
|
||||
|
||||
// configure pins for address lines
|
||||
rcc_periph_clock_enable(RCC_GPIOB); // enable clock for GPIO port peripheral
|
||||
gpio_clear(RCC_GPIOB, GPIO0 | GPIO1 | GPIO2 | GPIO3); // unselect line
|
||||
gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO0 | GPIO1 | GPIO2 | GPIO3); // set fast edge
|
||||
gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0 | GPIO1 | GPIO2 | GPIO3); // set pin as output
|
||||
|
||||
// configure DMA to sent line data
|
||||
// because there is no peripheral request for data, this is a memory to memory transfer
|
||||
rcc_periph_clock_enable(RGBPANEL_RCC_DMA); // enable clock for DMA peripheral (any DMA and channel can be used)
|
||||
dma_disable_stream(RGBPANEL_DMA, RGBPANEL_STREAM); // disable stream before re-configuring
|
||||
while (DMA_SCR(RGBPANEL_DMA, RGBPANEL_STREAM) & DMA_SxCR_EN); // wait until transfer is finished before we can reconfigure
|
||||
dma_stream_reset(RGBPANEL_DMA, RGBPANEL_STREAM); // use default values
|
||||
dma_set_peripheral_size(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_SxCR_PSIZE_8BIT); // we only write the 8 first bit
|
||||
dma_enable_peripheral_increment_mode(RGBPANEL_DMA, RGBPANEL_STREAM); // increment address of memory to read
|
||||
dma_set_memory_address(RGBPANEL_DMA, RGBPANEL_STREAM, (uint32_t) &GPIOA_ODR); // set GPIOA as destination (for memory-to-memory transfer, the destination is the memory)
|
||||
dma_set_memory_size(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_SxCR_MSIZE_8BIT); // read 8 bits for transfer
|
||||
dma_disable_memory_increment_mode(RGBPANEL_DMA, RGBPANEL_STREAM); // don't increment GPIO address
|
||||
dma_set_number_of_data(RGBPANEL_DMA, RGBPANEL_STREAM, LENGTH(rgbpanel_data[0])); // set transfer size (one line)
|
||||
dma_channel_select(RGBPANEL_DMA, RGBPANEL_STREAM, RGBPANEL_CHANNEL); // set the channel for this stream
|
||||
dma_set_transfer_mode(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_SxCR_DIR_MEM_TO_MEM); // set transfer from memory to memory
|
||||
dma_set_priority(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_SxCR_PL_LOW); // there is no need to rush
|
||||
dma_enable_transfer_complete_interrupt(RGBPANEL_DMA, RGBPANEL_STREAM); // interrupt when line transfer is complete
|
||||
nvic_enable_irq(RGBPANEL_IRQ); // enable interrupt
|
||||
rgbpanel_clear(); // clear matrix
|
||||
gpio_clear(GPIO_PORT(RGBPANEL_OE_PIN), GPIO_PIN(RGBPANEL_OE_PIN)); // enable output
|
||||
|
||||
// configure timer to go through rows/lines
|
||||
rcc_periph_clock_enable(RCC_TIM(RGBPANEL_TIMER)); // enable clock for timer domain
|
||||
rcc_periph_reset_pulse(RST_TIM(RGBPANEL_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(RGBPANEL_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
|
||||
timer_set_prescaler(TIM(RGBPANEL_TIMER), 2 - 1); // hand tuned prescale to minimize inter-line ghosting
|
||||
timer_set_period(TIM(RGBPANEL_TIMER), 0x9fff - 1); // hand tuned period to minimize inter-line ghosting
|
||||
timer_clear_flag(TIM(RGBPANEL_TIMER), TIM_SR_UIF); // clear update (overflow) flag
|
||||
timer_update_on_overflow(TIM(RGBPANEL_TIMER)); // only use counter overflow as UEV source (use overflow as next line update indication)
|
||||
timer_enable_irq(TIM(RGBPANEL_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(RGBPANEL_TIMER)); // catch interrupt in service routine
|
||||
timer_enable_counter(TIM(RGBPANEL_TIMER)); // start timer to update RGB matrix
|
||||
}
|
||||
|
||||
void rgbpanel_release(void)
|
||||
{
|
||||
// release timer
|
||||
timer_disable_counter(TIM(RGBPANEL_TIMER)); // stop timer
|
||||
nvic_disable_irq(NVIC_TIM_IRQ(RGBPANEL_TIMER)); // disable interrupt
|
||||
rcc_periph_reset_pulse(RST_TIM(RGBPANEL_TIMER)); // reset timer state
|
||||
|
||||
// release DMA (it will stop onve completed
|
||||
while (DMA_SCR(RGBPANEL_DMA, RGBPANEL_STREAM) & DMA_SxCR_EN); // wait until DMA is complete
|
||||
dma_disable_stream(RGBPANEL_DMA, RGBPANEL_STREAM); // disable stream
|
||||
dma_stream_reset(RGBPANEL_DMA, RGBPANEL_STREAM); // use default values
|
||||
nvic_disable_irq(RGBPANEL_IRQ); // disable interrupt
|
||||
|
||||
// release GPIO
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_OE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_OE_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_A_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_A_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_B_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_B_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_C_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_C_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_D_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_D_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_CLK_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_CLK_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_LAT_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_LAT_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_R1_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_R1_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_G1_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_G1_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_B1_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_B1_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_R2_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_R2_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_G2_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_G2_PIN));
|
||||
gpio_mode_setup(GPIO_PORT(RGBPANEL_B2_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(RGBPANEL_B2_PIN));
|
||||
|
||||
}
|
||||
|
||||
void rgbpanel_clear(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < LENGTH(rgbpanel_data); i++) { // for each line
|
||||
for (uint8_t j = 0; j < LENGTH(rgbpanel_data[0]); j += 2) { // for each clock cycle
|
||||
rgbpanel_data[i][j + 0] = 0; // create clock falling edge
|
||||
rgbpanel_data[i][j + 1] = 1; // create clock rising edge
|
||||
}
|
||||
rgbpanel_data[i][LENGTH(rgbpanel_data[0]) - 1] |= (1 << 1); // latch data (next line will remove the latch)
|
||||
}
|
||||
}
|
||||
|
||||
void rgbpanel_set(int16_t x, int16_t y, bool r, bool g, bool b)
|
||||
{
|
||||
if (x < 0 || x >= RGBPANEL_WIDTH) {
|
||||
return;
|
||||
}
|
||||
if (y < 0 || y >= RGBPANEL_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
const uint8_t row = y % (RGBPANEL_HEIGHT / 2); // get the actual line/row
|
||||
const uint8_t col = x * 2; // get the actual column
|
||||
uint8_t data = 0; // there we will set the color bits
|
||||
if (y < (RGBPANEL_HEIGHT / 2)) {
|
||||
data = rgbpanel_data[row][col] & 0xe0; // keep lower line colors
|
||||
if (r) {
|
||||
data |= (1 << 2);
|
||||
}
|
||||
if (g) {
|
||||
data |= (1 << 3);
|
||||
}
|
||||
if (b) {
|
||||
data |= (1 << 4);
|
||||
}
|
||||
} else {
|
||||
data = rgbpanel_data[row][col] & 0x1c; // keep upper line colors
|
||||
if (r) {
|
||||
data |= (1 << 5);
|
||||
}
|
||||
if (g) {
|
||||
data |= (1 << 6);
|
||||
}
|
||||
if (b) {
|
||||
data |= (1 << 7);
|
||||
}
|
||||
}
|
||||
// set data on low edge
|
||||
rgbpanel_data[row][col + 0] &= 0x3; // clear color data (don't touch clock and latch data)
|
||||
rgbpanel_data[row][col + 0] |= data; // set the LED data
|
||||
// set data on high edge
|
||||
rgbpanel_data[row][col + 1] &= 0x3; // clear color data (don't touch clock and latch data)
|
||||
rgbpanel_data[row][col + 1] |= data; // set the LED data on clock high edge
|
||||
}
|
||||
|
||||
/** ISR triggered when the data for the line of the RGB matrix has been transferred
|
||||
* we switch line just after the data is latched, to reduce ghosting
|
||||
*/
|
||||
void RGBPANEL_ISR(void)
|
||||
{
|
||||
if (dma_get_interrupt_flag(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_TCIF)) {
|
||||
dma_clear_interrupt_flags(RGBPANEL_DMA, RGBPANEL_STREAM, DMA_TCIF);
|
||||
GPIOB_ODR = (GPIOB_ODR & 0xfff0) | (rgbpanel_line & 0xf); // select line (line on lower and upper half are updated at once)
|
||||
rgbpanel_line = (rgbpanel_line + 1) % (RGBPANEL_HEIGHT / 2); // go to next line (two lines are updated at once)
|
||||
}
|
||||
}
|
||||
|
||||
/** interrupt service routine called to update next line of RGB matrix
|
||||
* @note ideally the next line should be updated when the current one is complete, but the DMA is too fast.
|
||||
* @note switching lines too fast causes inter-line ghosting of the LEDs (on the same column), due to capacitance and driver switching limitations
|
||||
*/
|
||||
void TIM_ISR(RGBPANEL_TIMER)(void)
|
||||
{
|
||||
if (timer_get_flag(TIM(RGBPANEL_TIMER), TIM_SR_UIF)) { // update event happened
|
||||
timer_clear_flag(TIM(RGBPANEL_TIMER), TIM_SR_UIF); // clear flag
|
||||
if (DMA_SCR(RGBPANEL_DMA, RGBPANEL_STREAM) & DMA_SxCR_EN) { // DMA is not complete
|
||||
return;
|
||||
}
|
||||
dma_set_peripheral_address(RGBPANEL_DMA, RGBPANEL_STREAM, (uint32_t)&rgbpanel_data[rgbpanel_line]); // set memory containing line data to be transferred (for memory-to-memory transfer, the source is the peripheral)
|
||||
dma_enable_stream(RGBPANEL_DMA, RGBPANEL_STREAM); // start sending next line
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/** control RGB LED matrix panels through shift registers
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2022
|
||||
*/
|
||||
#define RGBPANEL_HEIGHT 32 /**< number of rows in the RGB matrix */
|
||||
#define RGBPANEL_WIDTH 64 /**< number of columns in the RGB matrix */
|
||||
|
||||
/** setup peripheral to control RGB panel */
|
||||
void rgbpanel_setup(void);
|
||||
/** release peripheral used to control RGB panel */
|
||||
void rgbpanel_release(void);
|
||||
/** switch off all LEDs on the RGB panel */
|
||||
void rgbpanel_clear(void);
|
||||
/** set color of the LED on the RGB panel
|
||||
* @param[in] x horizontal position (0 = left)
|
||||
* @param[in] y vertical position (0 = top)
|
||||
* @param[in] r if the red LED should be on
|
||||
* @param[in] g if the green LED should be on
|
||||
* @param[in] b if the blue LED should be on
|
||||
*/
|
||||
void rgbpanel_set(int16_t x, int16_t y, bool r, bool g, bool b);
|
|
@ -2,7 +2,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @date 2016-2022
|
||||
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
|
||||
*/
|
||||
|
||||
|
@ -26,9 +26,16 @@
|
|||
* @{
|
||||
*/
|
||||
/** SPI peripheral
|
||||
* @note SPI2 is 5V tolerant and can be operated in open-drain mode will 1 kO pull-up resistor to 5V to get 5V signal level
|
||||
* @note if pin is 5V tolerant, it can be operated in open-drain mode will 1 kOhm pull-up resistor to 5V to get 5V signal level
|
||||
*/
|
||||
#define LED_WS2812B_SPI 2 /**< SPI peripheral */
|
||||
#define LED_WS2812B_SPI 1 /**< SPI peripheral */
|
||||
#define LED_WS2812B_DR &SPI1_DR /**< SPI Data Register to transmit data */
|
||||
#define LED_WS2812B_DOUT PB5 /**< SPI MOSI pin used for data output */
|
||||
#define LED_WS2812B_AF GPIO_AF5 /**< alternate function for SPI pin */
|
||||
#define LED_WS2812B_RCC_DMA RCC_DMA2 /**< RCC for DMA for SPI peripheral */
|
||||
#define LED_WS2812B_DMA DMA2 /**< DMA for SPI peripheral */
|
||||
#define LED_WS2812B_STREAM DMA_STREAM3 /**< DMA stream for SPI TX */
|
||||
#define LED_WS2812B_CHANNEL DMA_SxCR_CHSEL_3 /**< DMA channel for SPI TX */
|
||||
/** @} */
|
||||
|
||||
/** bit template to encode one byte to be shifted out by SPI to the WS2812B LEDs
|
||||
|
@ -40,8 +47,8 @@
|
|||
*/
|
||||
#define LED_WS2812B_SPI_TEMPLATE 0x924924
|
||||
|
||||
/** data encoded to be shifted out by SPI for the WS2812B, plus the 50us reset (~40 data bits) */
|
||||
uint8_t led_ws2812b_data[LED_WS2812B_LEDS * 3 * 3 + 40 * 3 / 8 + 1] = {0};
|
||||
/** data encoded to be shifted out by SPI for the WS2812B, plus the 50us reset */
|
||||
uint8_t led_ws2812b_data[LED_WS2812B_LEDS * 3 * 3 + 25] = {0};
|
||||
|
||||
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
|
@ -94,13 +101,14 @@ void led_ws2812b_setup(void)
|
|||
}
|
||||
|
||||
// setup SPI to transmit data
|
||||
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(LED_WS2812B_SPI)); // enable clock for SPI output peripheral
|
||||
gpio_set_mode(SPI_MOSI_PORT(LED_WS2812B_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, SPI_MOSI_PIN(LED_WS2812B_SPI)); // set MOSI as output (push-pull needs to be shifted to 5V, open-drain consumes up to 5 mA with pull-up resistor of 1 kO)
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||
rcc_periph_clock_enable(GPIO_RCC(LED_WS2812B_DOUT)); // enable clock for SPI output peripheral
|
||||
gpio_mode_setup(GPIO_PORT(LED_WS2812B_DOUT), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(LED_WS2812B_DOUT)); // set SPI MOSI pin to alternate function
|
||||
gpio_set_output_options(GPIO_PORT(LED_WS2812B_DOUT), GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO_PIN(LED_WS2812B_DOUT)); // set SPI MOSI pin output as open-drain
|
||||
gpio_set_af(GPIO_PORT(LED_WS2812B_DOUT), LED_WS2812B_AF, GPIO_PIN(LED_WS2812B_DOUT)); // set alternate function to SPI
|
||||
rcc_periph_clock_enable(RCC_SPI(LED_WS2812B_SPI)); // enable clock for SPI peripheral
|
||||
spi_reset(SPI(LED_WS2812B_SPI)); // clear SPI values to default
|
||||
spi_set_master_mode(SPI(LED_WS2812B_SPI)); // set SPI as master since we generate the clock
|
||||
spi_set_baudrate_prescaler(SPI(LED_WS2812B_SPI), SPI_CR1_BR_FPCLK_DIV_16); // set clock to 0.44 us per bit
|
||||
spi_set_baudrate_prescaler(SPI(LED_WS2812B_SPI), SPI_CR1_BR_FPCLK_DIV_32); // set clock to 0.30 us per bit
|
||||
spi_set_clock_polarity_1(SPI(LED_WS2812B_SPI)); // clock is high when idle
|
||||
spi_set_clock_phase_1(SPI(LED_WS2812B_SPI)); // output data on second edge (rising)
|
||||
spi_set_dff_8bit(SPI(LED_WS2812B_SPI)); // use 8 bits for simpler encoding (but there will be more interrupts)
|
||||
|
@ -109,20 +117,25 @@ void led_ws2812b_setup(void)
|
|||
spi_set_unidirectional_mode(SPI(LED_WS2812B_SPI)); // we only need to transmit data
|
||||
spi_enable_software_slave_management(SPI(LED_WS2812B_SPI)); // control the slave select in software (so we can start transmission)
|
||||
spi_set_nss_high(SPI(LED_WS2812B_SPI)); // set NSS high so we can output
|
||||
spi_enable_tx_dma(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transferred
|
||||
spi_enable(SPI(LED_WS2812B_SPI)); // enable SPI
|
||||
|
||||
// configure DMA to provide the pattern to be shifted out from SPI to the WS2812B LEDs
|
||||
rcc_periph_clock_enable(RCC_DMA_SPI(LED_WS2812B_SPI)); // enable clock for DMA peripheral
|
||||
dma_channel_reset(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // start with fresh channel configuration
|
||||
dma_set_read_from_memory(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // set direction from memory to peripheral
|
||||
dma_set_memory_size(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_MSIZE_8BIT); // read 8 bits from memory
|
||||
dma_set_memory_address(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), (uint32_t)led_ws2812b_data); // set bit pattern as source address
|
||||
dma_enable_memory_increment_mode(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // go through bit pattern
|
||||
dma_set_number_of_data(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), LENGTH(led_ws2812b_data)); // set the size of the data to transmit
|
||||
dma_set_peripheral_address(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), (uint32_t)&SPI_DR(SPI(LED_WS2812B_SPI))); // set SPI as peripheral destination address
|
||||
dma_set_peripheral_size(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral
|
||||
dma_set_priority(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_PL_HIGH); // set priority to high since time is crucial for the peripheral
|
||||
dma_enable_circular_mode(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // always send the data
|
||||
dma_enable_channel(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // enable DMA channel
|
||||
spi_enable_tx_dma(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transferred
|
||||
// configure DMA
|
||||
rcc_periph_clock_enable(LED_WS2812B_RCC_DMA); // enable clock for DMA peripheral
|
||||
dma_disable_stream(LED_WS2812B_DMA, LED_WS2812B_STREAM); // disable stream before re-configuring
|
||||
while (DMA_SCR(LED_WS2812B_DMA, LED_WS2812B_STREAM) & DMA_SxCR_EN); // wait until transfer is finished before we can reconfigure
|
||||
dma_stream_reset(LED_WS2812B_DMA, LED_WS2812B_STREAM); // use default values
|
||||
dma_channel_select(LED_WS2812B_DMA, LED_WS2812B_STREAM, LED_WS2812B_CHANNEL); // set the channel for this stream
|
||||
dma_set_transfer_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); // set transfer from memory to memory
|
||||
dma_set_memory_size(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_MSIZE_8BIT); // read 8 bits for transfer
|
||||
dma_set_memory_address(LED_WS2812B_DMA, LED_WS2812B_STREAM, (uint32_t)led_ws2812b_data); // set memory address to read from
|
||||
dma_enable_memory_increment_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // increment address of memory to read
|
||||
|
||||
dma_set_peripheral_address(LED_WS2812B_DMA, LED_WS2812B_STREAM, (uint32_t)LED_WS2812B_DR); // set peripheral address to write data to
|
||||
dma_set_peripheral_size(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_PSIZE_8BIT); // we only write the 8 first bit
|
||||
dma_disable_peripheral_increment_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // always write to the SPI data register
|
||||
dma_set_number_of_data(LED_WS2812B_DMA, LED_WS2812B_STREAM, LENGTH(led_ws2812b_data)); // set transfer size
|
||||
dma_set_priority(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_PL_HIGH); // set priority to high since time is crucial for the peripheral
|
||||
dma_enable_circular_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // always send the data
|
||||
dma_enable_stream(LED_WS2812B_DMA, LED_WS2812B_STREAM); // enable DMA channel
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @date 2016-2022
|
||||
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
|
||||
*/
|
||||
#pragma once
|
||||
#error not converted for STM32F4
|
||||
|
||||
/** number of LEDs on the WS2812B strip */
|
||||
#define LED_WS2812B_LEDS 48
|
||||
#define LED_WS2812B_LEDS 3U
|
||||
|
||||
/** setup WS2812B LED driver
|
||||
* @note this starts the continuous transmission
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016
|
||||
* @date 2016-2022
|
||||
* @note peripherals used: USART @ref radio_esp8266_usart
|
||||
*/
|
||||
|
||||
|
@ -25,17 +25,22 @@
|
|||
/** @defgroup radio_esp8266_usart USART peripheral used for communication with radio
|
||||
* @{
|
||||
*/
|
||||
#define RADIO_ESP8266_USART 2 /**< USART peripheral */
|
||||
#define RADIO_ESP8266_USART 1 /**< USART peripheral */
|
||||
#define RADIO_ESP8266_TX PA9 /**< pin used for USART TX */
|
||||
#define RADIO_ESP8266_RX PA10 /**< pin used for USART RX */
|
||||
#define RADIO_ESP8266_AF GPIO_AF7 /**< alternate function for UART pins */
|
||||
/** @} */
|
||||
|
||||
/* input and output buffers and used memory */
|
||||
static uint8_t rx_buffer[24] = {0}; /**< buffer for received data (we only expect AT responses) */
|
||||
static uint8_t rx_buffer[24 + 530] = {0}; /**< buffer for received data */
|
||||
static volatile uint16_t rx_used = 0; /**< number of byte in receive buffer */
|
||||
static uint8_t tx_buffer[256] = {0}; /**< buffer for data to transmit */
|
||||
static volatile uint16_t tx_used = 0; /**< number of bytes used in transmit buffer */
|
||||
|
||||
volatile bool radio_esp8266_activity = false;
|
||||
volatile bool radio_esp8266_success = false;
|
||||
uint8_t radio_esp8266_received[512 + 18];
|
||||
uint16_t radio_esp8266_received_len = 0;
|
||||
|
||||
/** transmit data to radio
|
||||
* @param[in] data data to transmit
|
||||
|
@ -47,26 +52,55 @@ static void radio_esp8266_transmit(uint8_t* data, uint8_t length) {
|
|||
__WFI(); // sleep until something happened
|
||||
}
|
||||
usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // ensure transmit interrupt is disable to prevent index corruption (the ISR should already have done it)
|
||||
rx_used = 0; // reset receive buffer
|
||||
radio_esp8266_activity = false; // reset status because of new activity
|
||||
for (tx_used=0; tx_used<length && tx_used<LENGTH(tx_buffer); tx_used++) { // copy data
|
||||
tx_buffer[tx_used] = data[length-1-tx_used]; // put character in buffer (in reverse order)
|
||||
for (tx_used = 0; tx_used < length && tx_used < LENGTH(tx_buffer); tx_used++) { // copy data
|
||||
tx_buffer[tx_used] = data[length - 1 - tx_used]; // put character in buffer (in reverse order)
|
||||
}
|
||||
if (tx_used) {
|
||||
usart_enable_tx_interrupt(USART(RADIO_ESP8266_USART)); // enable interrupt to send bytes
|
||||
}
|
||||
}
|
||||
|
||||
/** transmit string to radio
|
||||
* @param[in] data data to transmit
|
||||
* @param[in] length length of data to transmit
|
||||
*/
|
||||
static void radio_esp8266_transmits(char* str) {
|
||||
if (NULL == str) {
|
||||
return;
|
||||
}
|
||||
const uint16_t length = strlen(str);
|
||||
radio_esp8266_transmit((uint8_t*)str, length);
|
||||
}
|
||||
|
||||
/** transmit string to radio
|
||||
* @param[in] data data to transmit
|
||||
* @param[in] length length of data to transmit
|
||||
*/
|
||||
static void radio_esp8266_transmit_at(char* at) {
|
||||
if (NULL == at) {
|
||||
return;
|
||||
}
|
||||
radio_esp8266_transmits("AT");
|
||||
radio_esp8266_transmits(at);
|
||||
radio_esp8266_transmits("\r\n");
|
||||
}
|
||||
|
||||
void radio_esp8266_setup(void)
|
||||
{
|
||||
/* enable USART I/O peripheral */
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART)
|
||||
rcc_periph_clock_enable(USART_PORT_RCC(RADIO_ESP8266_USART)); // enable clock for USART port peripheral
|
||||
rcc_periph_clock_enable(USART_RCC(RADIO_ESP8266_USART)); // enable clock for USART peripheral
|
||||
gpio_set_mode(USART_PORT(RADIO_ESP8266_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(RADIO_ESP8266_USART)); // setup GPIO pin USART transmit
|
||||
gpio_set_mode(USART_PORT(RADIO_ESP8266_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(RADIO_ESP8266_USART)); // setup GPIO pin USART receive
|
||||
gpio_set(USART_PORT(RADIO_ESP8266_USART), USART_PIN_RX(RADIO_ESP8266_USART)); // pull up to avoid noise when not connected
|
||||
// configure pins
|
||||
rcc_periph_clock_enable(GPIO_RCC(RADIO_ESP8266_TX)); // enable clock for USART TX pin port peripheral
|
||||
gpio_mode_setup(GPIO_PORT(RADIO_ESP8266_TX), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(RADIO_ESP8266_TX)); // set TX pin to alternate function
|
||||
gpio_set_output_options(GPIO_PORT(RADIO_ESP8266_TX), GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN(RADIO_ESP8266_TX)); // set TX pin output as push-pull
|
||||
gpio_set_af(GPIO_PORT(RADIO_ESP8266_TX), RADIO_ESP8266_AF, GPIO_PIN(RADIO_ESP8266_TX)); // set alternate function to USART
|
||||
rcc_periph_clock_enable(GPIO_RCC(RADIO_ESP8266_RX)); // enable clock for USART RX pin port peripheral
|
||||
gpio_mode_setup(GPIO_PORT(RADIO_ESP8266_RX), GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN(RADIO_ESP8266_RX)); // set GPIO to alternate function, with pull up to avoid noise in case it is not connected
|
||||
gpio_set_af(GPIO_PORT(RADIO_ESP8266_RX), RADIO_ESP8266_AF, GPIO_PIN(RADIO_ESP8266_RX)); // set alternate function to USART
|
||||
|
||||
/* setup USART parameters for ESP8266 AT firmware */
|
||||
// configure USART for ESP8266 AT firmware
|
||||
rcc_periph_clock_enable(RCC_USART(RADIO_ESP8266_USART)); // enable clock for USART peripheral
|
||||
rcc_periph_reset_pulse(RST_USART(RADIO_ESP8266_USART)); // reset peripheral
|
||||
usart_set_baudrate(USART(RADIO_ESP8266_USART), 115200); // AT firmware 0.51 (SDK 1.5.0) uses 115200 bps
|
||||
usart_set_databits(USART(RADIO_ESP8266_USART), 8);
|
||||
usart_set_stopbits(USART(RADIO_ESP8266_USART), USART_STOPBITS_1);
|
||||
|
@ -74,9 +108,9 @@ void radio_esp8266_setup(void)
|
|||
usart_set_parity(USART(RADIO_ESP8266_USART), USART_PARITY_NONE);
|
||||
usart_set_flow_control(USART(RADIO_ESP8266_USART), USART_FLOWCONTROL_NONE);
|
||||
|
||||
nvic_enable_irq(USART_IRQ(RADIO_ESP8266_USART)); // enable the USART interrupt
|
||||
nvic_enable_irq(USART_IRQ(RADIO_ESP8266_USART)); // enable the UART interrupt
|
||||
usart_enable_rx_interrupt(USART(RADIO_ESP8266_USART)); // enable receive interrupt
|
||||
usart_enable(USART(RADIO_ESP8266_USART)); // enable USART
|
||||
usart_enable(USART(RADIO_ESP8266_USART)); // enable UART
|
||||
|
||||
/* reset buffer states */
|
||||
rx_used = 0;
|
||||
|
@ -84,37 +118,88 @@ void radio_esp8266_setup(void)
|
|||
radio_esp8266_activity = false;
|
||||
radio_esp8266_success = false;
|
||||
|
||||
radio_esp8266_transmit((uint8_t*)"AT\r\n",4); // verify if module is present
|
||||
radio_esp8266_transmit_at(""); // verify if module is present
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
radio_esp8266_transmit((uint8_t*)"AT+RST\r\n",8); // reset module
|
||||
radio_esp8266_transmit_at("E0"); // disable echoing
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
while(rx_used<13 || memcmp((char*)&rx_buffer[rx_used-13], "WIFI GOT IP\r\n", 13)!=0) { // wait to have IP
|
||||
}
|
||||
|
||||
void radio_esp8266_reset(void)
|
||||
{
|
||||
radio_esp8266_transmit_at("+RST"); // reset module
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
radio_esp8266_transmit_at("E0"); // disable echoing
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
while(rx_used < 13 || memcmp((char*)&rx_buffer[rx_used - 13], "WIFI GOT IP\r\n", 13) != 0) { // wait to have IP
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
radio_esp8266_transmit((uint8_t*)"ATE0\r\n",6); // disable echoing
|
||||
}
|
||||
|
||||
bool radio_esp8266_connected(void)
|
||||
{
|
||||
// verify if we ware connect to access point
|
||||
radio_esp8266_transmit_at("+CWJAP_CUR?");
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
if (rx_used < 5 || 0 == memcmp((char*)&rx_buffer[rx_used - 5], "No AP", 5)) {
|
||||
rx_used = 0; // finished using the buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we have an IP
|
||||
radio_esp8266_transmit_at("+CIPSTA_CUR?"); // verify if module is present
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
if (rx_used < 17 || '0' == rx_buffer[16]) { // check for +CIPSTA_CUR:ip:"0.0.0.0"
|
||||
rx_used = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
rx_used = 0; // clear buffer
|
||||
return true;
|
||||
}
|
||||
|
||||
void radio_esp8266_tcp_open(char* host, uint16_t port)
|
||||
{
|
||||
char command[256] = {0}; // string to create command
|
||||
int length = snprintf(command, LENGTH(command), "AT+CIPSTART=\"TCP\",\"%s\",%u\r\n", host, port); // create AT command to establish a TCP connection
|
||||
if (length>0) {
|
||||
if (length > 0) {
|
||||
radio_esp8266_transmit((uint8_t*)command, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool radio_esp8266_listen(bool udp, uint16_t port)
|
||||
{
|
||||
char command[256] = {0}; // string to create command
|
||||
int length = snprintf(command, LENGTH(command), "AT+CIPSTART=\"%s\",\"0.0.0.0\",0,%u\r\n", udp ? "UDP" : "TCP", port); // create AT command to establish a listening connection
|
||||
if (!length) {
|
||||
return false;
|
||||
}
|
||||
radio_esp8266_transmit((uint8_t*)command, length);
|
||||
while (!radio_esp8266_activity) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
if (!radio_esp8266_success) { // send AT command did not succeed
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void radio_esp8266_send(uint8_t* data, uint8_t length)
|
||||
{
|
||||
char command[16+1] = {0}; // string to create command
|
||||
char command[16 + 1] = {0}; // string to create command
|
||||
int command_length = snprintf(command, LENGTH(command), "AT+CIPSEND=%u\r\n", length); // create AT command to send data
|
||||
if (command_length>0) {
|
||||
if (command_length > 0) {
|
||||
radio_esp8266_transmit((uint8_t*)command, command_length); // transmit AT command
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
|
@ -128,35 +213,58 @@ void radio_esp8266_send(uint8_t* data, uint8_t length)
|
|||
|
||||
void radio_esp8266_close(void)
|
||||
{
|
||||
radio_esp8266_transmit((uint8_t*)"AT+CIPCLOSE\r\n", 13); // send AT command to close established connection
|
||||
radio_esp8266_transmit_at("+CIPCLOSE"); // send AT command to close established connection
|
||||
}
|
||||
|
||||
/** USART interrupt service routine called when data has been transmitted or received */
|
||||
void USART_ISR(RADIO_ESP8266_USART)(void)
|
||||
{
|
||||
if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // data has been transmitted
|
||||
if (usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // data has been transmitted
|
||||
if (tx_used) { // there is still data in the buffer to transmit
|
||||
usart_send(USART(RADIO_ESP8266_USART),tx_buffer[tx_used-1]); // put data in transmit register
|
||||
usart_send(USART(RADIO_ESP8266_USART), tx_buffer[tx_used - 1]); // put data in transmit register
|
||||
tx_used--; // update used size
|
||||
} else { // no data in the buffer to transmit
|
||||
usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // disable transmit interrupt
|
||||
}
|
||||
}
|
||||
if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_RXNE)) { // data has been received
|
||||
while (rx_used>=LENGTH(rx_buffer)) { // if buffer is full
|
||||
memmove(rx_buffer,&rx_buffer[1],LENGTH(rx_buffer)-1); // drop old data to make space (ring buffer are more efficient but harder to handle)
|
||||
if (usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_RXNE)) { // data has been received
|
||||
while (rx_used >= LENGTH(rx_buffer)) { // if buffer is full
|
||||
memmove(rx_buffer, &rx_buffer[1], LENGTH(rx_buffer) - 1); // drop old data to make space (ring buffer are more efficient but harder to handle)
|
||||
rx_used--; // update used buffer information
|
||||
}
|
||||
rx_buffer[rx_used++] = usart_recv(USART(RADIO_ESP8266_USART)); // put character in buffer
|
||||
/*
|
||||
if ('\n' == rx_buffer[rx_used - 1]) {
|
||||
rx_buffer[rx_used] = '\0';
|
||||
printf((char*)rx_buffer);
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
// if the used send a packet with these strings during the commands detection the AT command response will break (AT commands are hard to handle perfectly)
|
||||
if (rx_used>=4 && memcmp((char*)&rx_buffer[rx_used-4], "OK\r\n", 4)==0) { // OK received
|
||||
if (rx_used >= 4 && memcmp((char*)&rx_buffer[rx_used - 4], "OK\r\n", 4) == 0) { // OK received
|
||||
radio_esp8266_activity = true; // response received
|
||||
radio_esp8266_success = true; // command succeeded
|
||||
rx_used = 0; // reset buffer
|
||||
} else if (rx_used>=7 && memcmp((char*)&rx_buffer[rx_used-7], "ERROR\r\n", 7)==0) { // ERROR received
|
||||
} else if (rx_used >= 7 && memcmp((char*)&rx_buffer[rx_used - 7], "ERROR\r\n", 7) == 0) { // ERROR received
|
||||
radio_esp8266_activity = true; // response received
|
||||
radio_esp8266_success = false; // command failed
|
||||
rx_used = 0; // reset buffer
|
||||
} else if (rx_used >= 7 && memcmp((char*)&rx_buffer[0], "\r\n+IPD,", 7) == 0) { // IP data being received
|
||||
// find if we received the length
|
||||
int32_t data_length = 0;
|
||||
uint32_t data_start = 0;
|
||||
for (uint16_t i = 7; i < rx_used; i++) {
|
||||
if (':' == rx_buffer[i]) {
|
||||
data_start = i + 1;
|
||||
data_length = atoi((char*)&rx_buffer[7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data_length > 0 && rx_used >= data_start + data_length) {
|
||||
if ((uint32_t)data_length <= LENGTH(radio_esp8266_received)) {
|
||||
memcpy(radio_esp8266_received, &rx_buffer[data_start], data_length);
|
||||
radio_esp8266_received_len = data_length;
|
||||
}
|
||||
rx_used = 0; // reset buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,27 +2,42 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016
|
||||
* @date 2016-2022
|
||||
* @note peripherals used: USART @ref radio_esp8266_usart
|
||||
*/
|
||||
#pragma once
|
||||
#error not converted for STM32F4
|
||||
|
||||
/** a response has been returned by the radio */
|
||||
extern volatile bool radio_esp8266_activity;
|
||||
/** the last command has succeeded */
|
||||
extern volatile bool radio_esp8266_success;
|
||||
/** data received (overwritten upon next receive) */
|
||||
extern uint8_t radio_esp8266_received[];
|
||||
/** length of receive data */
|
||||
extern uint16_t radio_esp8266_received_len;
|
||||
|
||||
/** setup peripherals to communicate with radio
|
||||
* @note this is blocking to ensure we are connected to the WiFi network
|
||||
*/
|
||||
void radio_esp8266_setup(void);
|
||||
/** reset ESP */
|
||||
void radio_esp8266_reset(void);
|
||||
/** check if connect to the network
|
||||
* @return if connected to network
|
||||
*/
|
||||
bool radio_esp8266_connected(void);
|
||||
/** establish TCP connection
|
||||
* @param[in] host host to connect to
|
||||
* @param[in] port TCP port to connect to
|
||||
* @note wait for activity to get success status
|
||||
*/
|
||||
void radio_esp8266_tcp_open(char* host, uint16_t port);
|
||||
/** open listening connection
|
||||
* @param[in] udp if it's an UDP or TCP connection
|
||||
* @param[in] port port to listen to
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
bool radio_esp8266_listen(bool udp, uint16_t port);
|
||||
/** send data (requires established connection)
|
||||
* @param[in] data data to send
|
||||
* @param[in] length size of data to send
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||
#include <libopencm3/usb/usbd.h> // USB library
|
||||
#include <libopencm3/usb/dfu.h> // USB DFU library
|
||||
#include <libopencm3/usb/dwc/otg_fs.h> // additional USB definitions
|
||||
|
||||
#include "global.h" // global utilities
|
||||
#include "usb_dfu.h" // USB DFU header and definitions
|
||||
|
@ -366,6 +367,8 @@ void usb_dfu_setup(void)
|
|||
gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); // set alternate function to USB
|
||||
usb_device = usbd_init(&otgfs_usb_driver, &usb_dfu_device, &usb_dfu_configuration, usb_dfu_strings, LENGTH(usb_dfu_strings), usbd_control_buffer, sizeof(usbd_control_buffer)); // configure USB device
|
||||
usbd_register_control_callback(usb_device, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usb_dfu_control_request); // set control request handling DFU operations
|
||||
OTG_FS_GCCFG |= OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; // disable VBUS sensing
|
||||
OTG_FS_GCCFG &= ~(OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN); // force USB device mode
|
||||
}
|
||||
|
||||
void usb_dfu_start(void)
|
||||
|
|
Loading…
Reference in New Issue