From 0ec7e72b01dca8070c1a8ae9a0b0347d2281eadb Mon Sep 17 00:00:00 2001 From: stevstrong Date: Fri, 21 Oct 2016 22:31:27 +0200 Subject: [PATCH 01/25] improved USB serial Rx and implemented buffered Tx --- .../maple/libmaple/usb/stm32f1/usb_cdcacm.c | 227 +++++++++++------- STM32F1/cores/maple/usb_serial.cpp | 45 +--- STM32F1/cores/maple/usb_serial.h | 2 +- .../system/libmaple/usb/stm32f1/usb_reg_map.h | 4 +- 4 files changed, 153 insertions(+), 125 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c index 448c05d..7a5a772 100644 --- a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c +++ b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c @@ -261,18 +261,28 @@ static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = { /* I/O state */ -#define CDC_SERIAL_BUFFER_SIZE 512 +#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2 +#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1) /* Received data */ -static volatile uint8 vcomBufferRx[CDC_SERIAL_BUFFER_SIZE]; -/* Read index into vcomBufferRx */ -static volatile uint32 rx_offset = 0; -/* Number of bytes left to transmit */ -static volatile uint32 n_unsent_bytes = 0; -/* Are we currently sending an IN packet? */ -static volatile uint8 transmitting = 0; -/* Number of unread bytes */ -static volatile uint32 n_unread_bytes = 0; +static volatile uint8 vcomBufferRx[CDC_SERIAL_RX_BUFFER_SIZE]; +/* Write index to vcomBufferRx */ +static volatile uint32 rx_head; +/* Read index from vcomBufferRx */ +static volatile uint32 rx_tail; + +#define CDC_SERIAL_TX_BUFFER_SIZE 256 // must be power of 2 +#define CDC_SERIAL_TX_BUFFER_SIZE_MASK (CDC_SERIAL_TX_BUFFER_SIZE-1) +// Tx data +static volatile uint8 vcomBufferTx[CDC_SERIAL_TX_BUFFER_SIZE]; +// Write index to vcomBufferTx +static volatile uint32 tx_head; +// Read index from vcomBufferTx +static volatile uint32 tx_tail; +// Are we currently sending an IN packet? +static volatile int8 transmitting; + + /* Other state (line coding, DTR/RTS) */ @@ -393,30 +403,34 @@ void usb_cdcacm_putc(char ch) { ; } -/* This function is blocking. +/* This function is non-blocking. * - * It copies data from a usercode buffer into the USB peripheral TX + * It copies data from a user buffer into the USB peripheral TX * buffer, and returns the number of bytes copied. */ -uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) { - /* Last transmission hasn't finished, so abort. */ - while ( usb_cdcacm_is_transmitting()>0 ) ; // wait for end of transmission +uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) +{ + if (len==0) return 0; // no data to send - /* We can only put USB_CDCACM_TX_EPSIZE bytes in the buffer. */ - if (len > USB_CDCACM_TX_EPSIZE) { - len = USB_CDCACM_TX_EPSIZE; - } + uint32 head = tx_head; // load volatile variable + uint32 tx_unsent = (head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; - /* Queue bytes for sending. */ - if (len) { - usb_copy_to_pma(buf, len, USB_CDCACM_TX_ADDR); + // We can only put bytes in the buffer if there is place + if (len > (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1) ) { + len = (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1); } - // We still need to wait for the interrupt, even if we're sending - // zero bytes. (Sending zero-size packets is useful for flushing - // host-side buffers.) - usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, len); - n_unsent_bytes = len; - transmitting = 1; - usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID); + if (len==0) return 0; // buffer full + + uint16 i; + // copy data from user buffer to USB Tx buffer + for (i=0; i0 ? transmitting : 0); } uint16 usb_cdcacm_get_pending(void) { - return n_unsent_bytes; + return (tx_head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; } -/* Nonblocking byte receive. +/* Non-blocking byte receive. * * Copies up to len bytes from our private data buffer (*NOT* the PMA) * into buf and deq's the FIFO. */ -uint32 usb_cdcacm_rx(uint8* buf, uint32 len) { +uint32 usb_cdcacm_rx(uint8* buf, uint32 len) +{ /* Copy bytes to buffer. */ uint32 n_copied = usb_cdcacm_peek(buf, len); /* Mark bytes as read. */ - n_unread_bytes -= n_copied; - rx_offset = (rx_offset + n_copied) % CDC_SERIAL_BUFFER_SIZE; + uint16 tail = rx_tail; // load volatile variable + tail = (tail + n_copied) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + rx_tail = tail; // store volatile variable - /* If all bytes have been read, re-enable the RX endpoint, which - * was set to NAK when the current batch of bytes was received. */ - if (n_unread_bytes == 0) { - usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE); + uint32 rx_unread = (rx_head - tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + // If buffer was emptied to a pre-set value, re-enable the RX endpoint + if ( rx_unread <= 64 ) { // experimental value, gives the best performance usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); - } - + } return n_copied; } -/* Nonblocking byte lookahead. +/* Non-blocking byte lookahead. * * Looks at unread bytes without marking them as read. */ -uint32 usb_cdcacm_peek(uint8* buf, uint32 len) { +uint32 usb_cdcacm_peek(uint8* buf, uint32 len) +{ int i; - uint32 head = rx_offset; + uint32 tail = rx_tail; + uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; - if (len > n_unread_bytes) { - len = n_unread_bytes; + if (len > rx_unread) { + len = rx_unread; } for (i = 0; i < len; i++) { - buf[i] = vcomBufferRx[head]; - head = (head + 1) % CDC_SERIAL_BUFFER_SIZE; + buf[i] = vcomBufferRx[tail]; + tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } return len; } -uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) { +uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) +{ int i; - uint32 head = (rx_offset + offset) % CDC_SERIAL_BUFFER_SIZE; + uint32 tail = (rx_tail + offset) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ; + uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; - if (len + offset > n_unread_bytes) { - len = n_unread_bytes - offset; + if (len + offset > rx_unread) { + len = rx_unread - offset; } for (i = 0; i < len; i++) { - buf[i] = vcomBufferRx[head]; - head = (head + 1) % CDC_SERIAL_BUFFER_SIZE; + buf[i] = vcomBufferRx[tail]; + tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } return len; @@ -495,12 +513,12 @@ uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) { /* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */ int usb_cdcacm_peek_char() { - if (n_unread_bytes == 0) + if (usb_cdcacm_data_available() == 0) { return -1; } - return vcomBufferRx[rx_offset]; + return vcomBufferRx[rx_tail]; } uint8 usb_cdcacm_get_dtr() { @@ -534,41 +552,75 @@ int usb_cdcacm_get_n_data_bits(void) { return line_coding.bDataBits; } - /* * Callbacks */ - -static void vcomDataTxCb(void) { - n_unsent_bytes = 0; - transmitting = 0; +static void vcomDataTxCb(void) +{ + uint32 tail = tx_tail; // load volatile variable + uint32 tx_unsent = (tx_head - tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; + if (tx_unsent==0) { + if ( (--transmitting)==0) goto flush; // no more data to send + return; // it was already flushed, keep Tx endpoint disabled + } + transmitting = 1; + // We can only send up to USB_CDCACM_TX_EPSIZE bytes in the endpoint. + if (tx_unsent > USB_CDCACM_TX_EPSIZE) { + tx_unsent = USB_CDCACM_TX_EPSIZE; + } + // copy the bytes from USB Tx buffer to PMA buffer + uint32 *dst = usb_pma_ptr(USB_CDCACM_TX_ADDR); + uint16 tmp = 0; + uint16 val; + int i; + for (i = 0; i < tx_unsent; i++) { + val = vcomBufferTx[tail]; + tail = (tail + 1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; + if (i&1) { + *dst++ = tmp | (val<<8); + } else { + tmp = val; + } + } + if ( tx_unsent&1 ) { + *dst = tmp; + } + tx_tail = tail; // store volatile variable +flush: + // enable Tx endpoint + usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, tx_unsent); + usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID); } -static void vcomDataRxCb(void) { - uint32 ep_rx_size; - uint32 tail = (rx_offset + n_unread_bytes) % CDC_SERIAL_BUFFER_SIZE; - uint8 ep_rx_data[USB_CDCACM_RX_EPSIZE]; + +static void vcomDataRxCb(void) +{ + uint32 head = rx_head; // load volatile variable + + uint32 ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP); + // This copy won't overwrite unread bytes as long as there is + // enough room in the USB Rx buffer for next packet + uint32 *src = usb_pma_ptr(USB_CDCACM_RX_ADDR); + uint16 tmp = 0; + uint8 val; uint32 i; - - usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_NAK); - ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP); - /* This copy won't overwrite unread bytes, since we've set the RX - * endpoint to NAK, and will only set it to VALID when all bytes - * have been read. */ - usb_copy_from_pma((uint8*)ep_rx_data, ep_rx_size, - USB_CDCACM_RX_ADDR); - for (i = 0; i < ep_rx_size; i++) { - vcomBufferRx[tail] = ep_rx_data[i]; - tail = (tail + 1) % CDC_SERIAL_BUFFER_SIZE; + if (i&1) { + val = tmp>>8; + } else { + tmp = *src++; + val = tmp&0xFF; + } + vcomBufferRx[head] = val; + head = (head + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } + rx_head = head; // store volatile variable - n_unread_bytes += ep_rx_size; - - if ( n_unread_bytes == 0 ) { - usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE); - usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); - } + uint32 rx_unread = (head - rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + // only enable further Rx if there is enough room to receive one more packet + if ( rx_unread < (CDC_SERIAL_RX_BUFFER_SIZE-USB_CDCACM_RX_EPSIZE) ) { + usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); + } if (rx_hook) { rx_hook(USB_CDCACM_HOOK_RX, 0); @@ -646,10 +698,11 @@ static void usbReset(void) { SetDeviceAddress(0); /* Reset the RX/TX state */ - n_unread_bytes = 0; - n_unsent_bytes = 0; - rx_offset = 0; - transmitting = 0; + rx_head = 0; + rx_tail = 0; + tx_head = 0; + tx_tail = 0; + transmitting = -1; } static RESULT usbDataSetup(uint8 request) { diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp index ab8816b..24b0014 100644 --- a/STM32F1/cores/maple/usb_serial.cpp +++ b/STM32F1/cores/maple/usb_serial.cpp @@ -100,44 +100,23 @@ size_t n = 0; size_t USBSerial::write(const char *str) { size_t n = 0; - this->write(str, strlen(str)); + this->write((const uint8*)str, strlen(str)); return n; } -size_t USBSerial::write(const void *buf, uint32 len) { +size_t USBSerial::write(const uint8 *buf, uint32 len) +{ size_t n = 0; if (!this->isConnected() || !buf) { return 0; } uint32 txed = 0; - uint32 old_txed = 0; - uint32 start = millis(); - - uint32 sent = 0; - - while (txed < len && (millis() - start < USB_TIMEOUT)) { - sent = usb_cdcacm_tx((const uint8*)buf + txed, len - txed); - txed += sent; - if (old_txed != txed) { - start = millis(); - } - old_txed = txed; + while (txed < len) { + txed += usb_cdcacm_tx((const uint8*)buf + txed, len - txed); } - -#if 0 -// this code leads to a serious performance drop and appears to be -// unnecessary - everything seems to work fine without, -jcw, 2015-11-05 -// see http://stm32duino.com/posting.php?mode=quote&f=3&p=7746 - if (sent == USB_CDCACM_TX_EPSIZE) { - while (usb_cdcacm_is_transmitting() != 0) { - } - /* flush out to avoid having the pc wait for more data */ - usb_cdcacm_tx(NULL, 0); - } -#endif - return n; + return n; } int USBSerial::available(void) { @@ -168,14 +147,10 @@ void USBSerial::flush(void) return; } -uint32 USBSerial::read(void *buf, uint32 len) { - if (!buf) { - return 0; - } - +uint32 USBSerial::read(uint8 * buf, uint32 len) { uint32 rxed = 0; while (rxed < len) { - rxed += usb_cdcacm_rx((uint8*)buf + rxed, len - rxed); + rxed += usb_cdcacm_rx(buf + rxed, len - rxed); } return rxed; @@ -183,13 +158,13 @@ uint32 USBSerial::read(void *buf, uint32 len) { /* Blocks forever until 1 byte is received */ int USBSerial::read(void) { - uint8 b; + int8 b; /* this->read(&b, 1); return b; */ - if (usb_cdcacm_rx(&b, 1)==0) + if (usb_cdcacm_rx((uint8*)&b, 1)==0) { return -1; } diff --git a/STM32F1/cores/maple/usb_serial.h b/STM32F1/cores/maple/usb_serial.h index 740ab0f..e50a16c 100644 --- a/STM32F1/cores/maple/usb_serial.h +++ b/STM32F1/cores/maple/usb_serial.h @@ -53,7 +53,7 @@ public: virtual int available(void);// Changed to virtual - uint32 read(void *buf, uint32 len); + uint32 read(uint8 * buf, uint32 len); // uint8 read(void); // Roger Clark. added functions to support Arduino 1.0 API diff --git a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h index 6683264..9e9214c 100644 --- a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h +++ b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h @@ -349,8 +349,8 @@ static inline void usb_clear_status_out(uint8 ep) { void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset); void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset); -static inline void* usb_pma_ptr(uint32 offset) { - return (void*)(USB_PMA_BASE + 2 * offset); +static inline uint32 * usb_pma_ptr(uint32 offset) { + return (uint32*)(USB_PMA_BASE + 2 * offset); } /* From 42cb8b5bc809df2aa4b9d82f535d7a71caae9282 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Fri, 21 Oct 2016 23:01:45 +0200 Subject: [PATCH 02/25] small change - revert local variable to uint8 in Serial.read --- STM32F1/cores/maple/usb_serial.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp index 24b0014..5eaf70e 100644 --- a/STM32F1/cores/maple/usb_serial.cpp +++ b/STM32F1/cores/maple/usb_serial.cpp @@ -158,13 +158,13 @@ uint32 USBSerial::read(uint8 * buf, uint32 len) { /* Blocks forever until 1 byte is received */ int USBSerial::read(void) { - int8 b; + uint8 b; /* this->read(&b, 1); return b; */ - if (usb_cdcacm_rx((uint8*)&b, 1)==0) + if (usb_cdcacm_rx(&b, 1)==0) { return -1; } From 4bc4b1d6bd42816c935053f043b8697f5e393434 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Mon, 31 Oct 2016 22:12:04 +0100 Subject: [PATCH 03/25] remove unused functions + adapt passed parameter - remove unused functions form usb_reg_map - change passed buffer type to uint8 * --- STM32F1/cores/maple/libmaple/usb/stm32f1/usb_reg_map.c | 8 ++++---- STM32F1/cores/maple/usb_serial.h | 2 +- STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_reg_map.c b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_reg_map.c index ea60cb3..3f5446a 100644 --- a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_reg_map.c +++ b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_reg_map.c @@ -29,7 +29,7 @@ /* TODO these could use some improvement; they're fairly * straightforward ports of the analogous ST code. The PMA blit * routines in particular are obvious targets for performance - * measurement and tuning. */ + * measurement and tuning. void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset) { uint16 *dst = (uint16*)usb_pma_ptr(pma_offset); @@ -57,7 +57,7 @@ void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset) { *dst = *src & 0xFF; } } - + */ static void usb_set_ep_rx_count_common(uint32 *rxc, uint16 count) { uint16 nblocks; if (count > 62) { @@ -76,12 +76,12 @@ static void usb_set_ep_rx_count_common(uint32 *rxc, uint16 count) { *rxc = nblocks << 10; } } - +/* void usb_set_ep_rx_buf0_count(uint8 ep, uint16 count) { uint32 *rxc = usb_ep_rx_buf0_count_ptr(ep); usb_set_ep_rx_count_common(rxc, count); } - +*/ void usb_set_ep_rx_count(uint8 ep, uint16 count) { uint32 *rxc = usb_ep_rx_count_ptr(ep); usb_set_ep_rx_count_common(rxc, count); diff --git a/STM32F1/cores/maple/usb_serial.h b/STM32F1/cores/maple/usb_serial.h index e50a16c..96bbefc 100644 --- a/STM32F1/cores/maple/usb_serial.h +++ b/STM32F1/cores/maple/usb_serial.h @@ -65,7 +65,7 @@ public: size_t write(uint8); size_t write(const char *str); - size_t write(const void*, uint32); + size_t write(const uint8*, uint32); uint8 getRTS(); uint8 getDTR(); diff --git a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h index 9e9214c..d0423fc 100644 --- a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h +++ b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h @@ -345,10 +345,10 @@ static inline void usb_clear_status_out(uint8 ep) { /* * PMA conveniences */ - +/* void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset); void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset); - +*/ static inline uint32 * usb_pma_ptr(uint32 offset) { return (uint32*)(USB_PMA_BASE + 2 * offset); } @@ -567,7 +567,7 @@ static inline uint16 usb_get_ep_rx_buf0_count(uint8 ep) { return usb_get_ep_tx_count(ep); } -void usb_set_ep_rx_buf0_count(uint8 ep, uint16 count); +//void usb_set_ep_rx_buf0_count(uint8 ep, uint16 count); static inline uint32* usb_ep_rx_buf1_count_ptr(uint8 ep) { return usb_ep_rx_count_ptr(ep); From 5db25232840ec8f0922a79e3a1a19a86f5397207 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Mon, 31 Oct 2016 22:29:40 +0100 Subject: [PATCH 04/25] improve SPI low level functions - optimize code and run-time --- STM32F1/cores/maple/libmaple/spi.c | 6 +- STM32F1/libraries/SPI/src/SPI.cpp | 180 ++++++++++------------------- STM32F1/libraries/SPI/src/SPI.h | 25 ++-- 3 files changed, 75 insertions(+), 136 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/spi.c b/STM32F1/cores/maple/libmaple/spi.c index 1020c5b..64f06d3 100644 --- a/STM32F1/cores/maple/libmaple/spi.c +++ b/STM32F1/cores/maple/libmaple/spi.c @@ -84,7 +84,7 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { } /** - * @brief Nonblocking SPI transmit. + * @brief Blocking SPI transmit. * @param dev SPI port to use for transmission * @param buf Buffer to transmit. The sizeof buf's elements are * inferred from dev's data frame format (i.e., are @@ -95,7 +95,8 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) { uint32 txed = 0; uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT; - while (spi_is_tx_empty(dev) && (txed < len)) { + while ( txed < len ) { + while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty if (byte_frame) { dev->regs->DR = ((const uint8*)buf)[txed++]; } else { @@ -162,5 +163,6 @@ static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) { spi_irq_disable(dev, SPI_INTERRUPTS_ALL); if ( (dev->regs->CR1&MASK)!=(cr1_config&MASK) ) spi_peripheral_disable(dev); dev->regs->CR1 = cr1_config; + //spi_rx_dma_enable(dev); spi_peripheral_enable(dev); } diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 4eeec38..0832791 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -41,6 +41,8 @@ #include "boards.h" //#include "HardwareSerial.h" +/** Time in ms for DMA receive timeout */ +#define DMA_TIMEOUT 100 #if CYCLES_PER_MICROSECOND != 72 /* TODO [0.2.0?] something smarter than this */ @@ -304,138 +306,110 @@ void SPIClass::endTransaction(void) * I/O */ -uint8 SPIClass::read(void) { - uint8 buf[1]; - this->read(buf, 1); - return buf[0]; +uint8 SPIClass::read(void) +{ + while ( spi_is_rx_nonempty(_currentSetting->spi_d)==0 ) ; + return (uint8)spi_rx_reg(_currentSetting->spi_d); } -void SPIClass::read(uint8 *buf, uint32 len) { +void SPIClass::read(uint8 *buf, uint32 len) +{ + spi_reg_map * regs = _currentSetting->spi_d->regs; + uint8 b = (regs->DR); // clear the RX buffer in case a byte is waiting on it. uint32 rxed = 0; - while (rxed < len) { - while (!spi_is_rx_nonempty(_currentSetting->spi_d)) - ; - buf[rxed++] = (uint8)spi_rx_reg(_currentSetting->spi_d); + // start sequence + while ( rxed < len) { + regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." + while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register + buf[rxed++] = (uint8)(regs->DR); // read and store the received byte } } -void SPIClass::write(uint16 data) { - // this->write(&data, 1); - +void SPIClass::write(uint16 data) +{ /* Added for 16bit data Victor Perez. Roger Clark - * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) - * The original method, of calling write(*data, length) . + * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, + * by taking the Tx code from transfer(byte) * This almost doubles the speed of this function. */ - spi_tx_reg(_currentSetting->spi_d, data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } -//void SPIClass::write(uint8 byte) { - // this->write(&byte, 1); - - /* Roger Clark - * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) - * The original method, of calling write(*data, length) . - * This almost doubles the speed of this function. - */ - -// spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." -// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." -// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." -//} - -void SPIClass::write(const uint8 *data, uint32 length) { - uint32 txed = 0; - while (txed < length) { - txed += spi_tx(_currentSetting->spi_d, data + txed, length - txed); - } - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "4. After writing the last data item into the SPI_DR register, wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... then wait until BSY=0, this indicates that the transmission of the last data is complete." +void SPIClass::write(const void *data, uint32 length) +{ + spi_tx(_currentSetting->spi_d, data, length); // data can be array of bytes or words + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." // taken from SdSpiSTM32F1.cpp - Victor's lib, and adapted to support device selection - if (spi_is_rx_nonempty(_currentSetting->spi_d)) { - uint8_t b = spi_rx_reg(_currentSetting->spi_d); - } + uint16 b = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! } uint16_t SPIClass::transfer16(uint16_t wr_data) const { spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." - uint16_t rd_data = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." -// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." -// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return rd_data; + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." } uint8 SPIClass::transfer(uint8 byte) const { spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." - uint8 b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return b; + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint8)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." } + /* Roger Clark and Victor Perez, 2015 * Performs a DMA SPI transfer with at least a receive buffer. -* If a TX buffer is not provided, FF is sent over and over for the lenght of the transfer. -* On exit TX buffer is not modified, and RX buffer cotains the received data. +* If a TX buffer is not provided, FF is sent over and over for the length of the transfer. +* On exit TX buffer is not modified, and RX buffer contains the received data. * Still in progress. */ -uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length) { +uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length) +{ if (length == 0) return 0; uint8 b = 0; - if (spi_is_rx_nonempty(_currentSetting->spi_d) == 1) b = spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. + spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); // dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - + // RX spi_rx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - receiveBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length); - + // TX + uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); spi_tx_dma_enable(_currentSetting->spi_d); - if (!transmitBuf) { - static uint8_t ff = 0XFF; - transmitBuf = &ff; - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, (DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit FF repeatedly - } - else { - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit buffer DMA + if ( transmitBuf==0 ) { + static uint8_t ff = 0XFF; + transmitBuf = &ff; + flags ^= DMA_MINC_MODE; // remove increment mode } + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit -// while (dma1_ch3_Active); -// if (receiveBuf) { uint32_t m = millis(); while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. - if ((millis() - m) > 100) { -// dma1_ch3_Active = 0; - b = 2; - break; - } + if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); -// } while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel); spi_rx_dma_disable(_currentSetting->spi_d); // And disable generation of DMA request from the SPI port so other peripherals can use the channels spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } + uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! return b; } @@ -443,10 +417,12 @@ uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length * Performs a DMA SPI send using a TX buffer. * On exit TX buffer is not modified. * Still in progress. +* 2016 - stevstrong - reworked to automatically detect bit size from SPI setting */ -uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) { +uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) +{ if (length == 0) return 0; - uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); + uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); uint8 b = 0; // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); @@ -454,55 +430,27 @@ uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) { // TX spi_tx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, flags);// Transmit buffer DMA + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit - + // while (dma1_ch3_Active); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. + uint32_t m = millis(); + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } + } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } + uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! return b; } -uint8 SPIClass::dmaSend(uint16 *transmitBuf, uint16 length, bool minc) { - if (length == 0) return 0; - uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); - uint8 b; - dma1_ch3_Active=true; - dma_init(_currentSetting->spiDmaDev); -// dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - - // TX - spi_tx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_16BITS, - transmitBuf, DMA_SIZE_16BITS, flags);// Transmit buffer DMA - dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); - dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit - -// while (dma1_ch3_Active); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. - dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); - - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); - spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } - return b; -} - - void SPIClass::attachInterrupt(void) { // Should be enableInterrupt() } diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index e949051..0f8497a 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -249,7 +249,7 @@ public: * @param buffer Bytes to transmit. * @param length Number of bytes in buffer to transmit. */ - void write(const uint8 *buffer, uint32 length); + void write(const void * buffer, uint32 length); /** * @brief Transmit a byte, then return the next unread byte. @@ -264,6 +264,7 @@ public: /** * @brief Sets up a DMA Transfer for "length" bytes. + * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. * * This function transmits and receives to buffers. * @@ -271,30 +272,18 @@ public: * @param receiveBuf buffer Bytes to save received data. * @param length Number of bytes in buffer to transmit. */ - uint8 dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length); + uint8 dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length); /** - * @brief Sets up a DMA Transmit for bytes. + * @brief Sets up a DMA Transmit for SPI 8 or 16 bit transfer mode. + * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. * - * This function transmits and does not care about the RX fifo. - * - * @param transmitBuf buffer Bytes to transmit, - * @param length Number of bytes in buffer to transmit. - * @param minc Set to use Memory Increment mode, clear to use Circular mode. - */ - uint8 dmaSend(uint8 *transmitBuf, uint16 length, bool minc = 1); - - /** - * @brief Sets up a DMA Transmit for half words. - * SPI PERFIPHERAL MUST BE SET TO 16 BIT MODE BEFORE - * - * This function transmits and does not care about the RX fifo. + * This function only transmits and does not care about the RX fifo. * * @param data buffer half words to transmit, * @param length Number of bytes in buffer to transmit. - * @param minc Set to use Memory Increment mode (default if blank), clear to use Circular mode. */ - uint8 dmaSend(uint16 *transmitBuf, uint16 length, bool minc = 1); + uint8 dmaSend(void * transmitBuf, uint16 length); /* * Pin accessors From 3469ef291baad000b3c64564623d7a09e1312356 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Fri, 11 Nov 2016 22:04:16 +0100 Subject: [PATCH 05/25] Revert "improve SPI low level functions" This reverts commit 5db25232840ec8f0922a79e3a1a19a86f5397207. --- STM32F1/cores/maple/libmaple/spi.c | 6 +- STM32F1/libraries/SPI/src/SPI.cpp | 180 +++++++++++++++++++---------- STM32F1/libraries/SPI/src/SPI.h | 25 ++-- 3 files changed, 136 insertions(+), 75 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/spi.c b/STM32F1/cores/maple/libmaple/spi.c index 64f06d3..1020c5b 100644 --- a/STM32F1/cores/maple/libmaple/spi.c +++ b/STM32F1/cores/maple/libmaple/spi.c @@ -84,7 +84,7 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { } /** - * @brief Blocking SPI transmit. + * @brief Nonblocking SPI transmit. * @param dev SPI port to use for transmission * @param buf Buffer to transmit. The sizeof buf's elements are * inferred from dev's data frame format (i.e., are @@ -95,8 +95,7 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) { uint32 txed = 0; uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT; - while ( txed < len ) { - while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty + while (spi_is_tx_empty(dev) && (txed < len)) { if (byte_frame) { dev->regs->DR = ((const uint8*)buf)[txed++]; } else { @@ -163,6 +162,5 @@ static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) { spi_irq_disable(dev, SPI_INTERRUPTS_ALL); if ( (dev->regs->CR1&MASK)!=(cr1_config&MASK) ) spi_peripheral_disable(dev); dev->regs->CR1 = cr1_config; - //spi_rx_dma_enable(dev); spi_peripheral_enable(dev); } diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 0832791..4eeec38 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -41,8 +41,6 @@ #include "boards.h" //#include "HardwareSerial.h" -/** Time in ms for DMA receive timeout */ -#define DMA_TIMEOUT 100 #if CYCLES_PER_MICROSECOND != 72 /* TODO [0.2.0?] something smarter than this */ @@ -306,110 +304,138 @@ void SPIClass::endTransaction(void) * I/O */ -uint8 SPIClass::read(void) -{ - while ( spi_is_rx_nonempty(_currentSetting->spi_d)==0 ) ; - return (uint8)spi_rx_reg(_currentSetting->spi_d); +uint8 SPIClass::read(void) { + uint8 buf[1]; + this->read(buf, 1); + return buf[0]; } -void SPIClass::read(uint8 *buf, uint32 len) -{ - spi_reg_map * regs = _currentSetting->spi_d->regs; - uint8 b = (regs->DR); // clear the RX buffer in case a byte is waiting on it. +void SPIClass::read(uint8 *buf, uint32 len) { uint32 rxed = 0; - // start sequence - while ( rxed < len) { - regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register - buf[rxed++] = (uint8)(regs->DR); // read and store the received byte + while (rxed < len) { + while (!spi_is_rx_nonempty(_currentSetting->spi_d)) + ; + buf[rxed++] = (uint8)spi_rx_reg(_currentSetting->spi_d); } } -void SPIClass::write(uint16 data) -{ +void SPIClass::write(uint16 data) { + // this->write(&data, 1); + /* Added for 16bit data Victor Perez. Roger Clark - * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, - * by taking the Tx code from transfer(byte) + * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) + * The original method, of calling write(*data, length) . * This almost doubles the speed of this function. */ + spi_tx_reg(_currentSetting->spi_d, data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } -void SPIClass::write(const void *data, uint32 length) -{ - spi_tx(_currentSetting->spi_d, data, length); // data can be array of bytes or words - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." +//void SPIClass::write(uint8 byte) { + // this->write(&byte, 1); + + /* Roger Clark + * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) + * The original method, of calling write(*data, length) . + * This almost doubles the speed of this function. + */ + +// spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." +// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." +// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." +//} + +void SPIClass::write(const uint8 *data, uint32 length) { + uint32 txed = 0; + while (txed < length) { + txed += spi_tx(_currentSetting->spi_d, data + txed, length - txed); + } + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "4. After writing the last data item into the SPI_DR register, wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... then wait until BSY=0, this indicates that the transmission of the last data is complete." // taken from SdSpiSTM32F1.cpp - Victor's lib, and adapted to support device selection - uint16 b = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! + if (spi_is_rx_nonempty(_currentSetting->spi_d)) { + uint8_t b = spi_rx_reg(_currentSetting->spi_d); + } } uint16_t SPIClass::transfer16(uint16_t wr_data) const { spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." + uint16_t rd_data = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." +// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." +// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return rd_data; } uint8 SPIClass::transfer(uint8 byte) const { spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return (uint8)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." + uint8 b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return b; } - /* Roger Clark and Victor Perez, 2015 * Performs a DMA SPI transfer with at least a receive buffer. -* If a TX buffer is not provided, FF is sent over and over for the length of the transfer. -* On exit TX buffer is not modified, and RX buffer contains the received data. +* If a TX buffer is not provided, FF is sent over and over for the lenght of the transfer. +* On exit TX buffer is not modified, and RX buffer cotains the received data. * Still in progress. */ -uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length) -{ +uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length) { if (length == 0) return 0; uint8 b = 0; - spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. + if (spi_is_rx_nonempty(_currentSetting->spi_d) == 1) b = spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); // dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - + // RX spi_rx_dma_enable(_currentSetting->spi_d); - dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, - receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, + receiveBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length); - + // TX - uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); spi_tx_dma_enable(_currentSetting->spi_d); - if ( transmitBuf==0 ) { - static uint8_t ff = 0XFF; - transmitBuf = &ff; - flags ^= DMA_MINC_MODE; // remove increment mode + if (!transmitBuf) { + static uint8_t ff = 0XFF; + transmitBuf = &ff; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, + transmitBuf, DMA_SIZE_8BITS, (DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit FF repeatedly + } + else { + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, + transmitBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit buffer DMA } - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, - transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit +// while (dma1_ch3_Active); +// if (receiveBuf) { uint32_t m = millis(); while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. - if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } + if ((millis() - m) > 100) { +// dma1_ch3_Active = 0; + b = 2; + break; + } } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); +// } while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel); spi_rx_dma_disable(_currentSetting->spi_d); // And disable generation of DMA request from the SPI port so other peripherals can use the channels spi_tx_dma_disable(_currentSetting->spi_d); - uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! + if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." + uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + } return b; } @@ -417,12 +443,10 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length * Performs a DMA SPI send using a TX buffer. * On exit TX buffer is not modified. * Still in progress. -* 2016 - stevstrong - reworked to automatically detect bit size from SPI setting */ -uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) -{ +uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) { if (length == 0) return 0; - uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); + uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); uint8 b = 0; // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); @@ -430,27 +454,55 @@ uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) // TX spi_tx_dma_enable(_currentSetting->spi_d); - dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, - transmitBuf, dma_bit_size, flags);// Transmit buffer DMA + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, + transmitBuf, DMA_SIZE_8BITS, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit - + // while (dma1_ch3_Active); - uint32_t m = millis(); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. - if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } - } + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); spi_tx_dma_disable(_currentSetting->spi_d); - uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! + if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." + uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + } return b; } +uint8 SPIClass::dmaSend(uint16 *transmitBuf, uint16 length, bool minc) { + if (length == 0) return 0; + uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); + uint8 b; + dma1_ch3_Active=true; + dma_init(_currentSetting->spiDmaDev); +// dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); + + // TX + spi_tx_dma_enable(_currentSetting->spi_d); + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_16BITS, + transmitBuf, DMA_SIZE_16BITS, flags);// Transmit buffer DMA + dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); + dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit + +// while (dma1_ch3_Active); + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. + dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + spi_tx_dma_disable(_currentSetting->spi_d); + if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." + b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." + } + return b; +} + + void SPIClass::attachInterrupt(void) { // Should be enableInterrupt() } diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index 0f8497a..e949051 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -249,7 +249,7 @@ public: * @param buffer Bytes to transmit. * @param length Number of bytes in buffer to transmit. */ - void write(const void * buffer, uint32 length); + void write(const uint8 *buffer, uint32 length); /** * @brief Transmit a byte, then return the next unread byte. @@ -264,7 +264,6 @@ public: /** * @brief Sets up a DMA Transfer for "length" bytes. - * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. * * This function transmits and receives to buffers. * @@ -272,18 +271,30 @@ public: * @param receiveBuf buffer Bytes to save received data. * @param length Number of bytes in buffer to transmit. */ - uint8 dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length); + uint8 dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length); /** - * @brief Sets up a DMA Transmit for SPI 8 or 16 bit transfer mode. - * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. + * @brief Sets up a DMA Transmit for bytes. * - * This function only transmits and does not care about the RX fifo. + * This function transmits and does not care about the RX fifo. + * + * @param transmitBuf buffer Bytes to transmit, + * @param length Number of bytes in buffer to transmit. + * @param minc Set to use Memory Increment mode, clear to use Circular mode. + */ + uint8 dmaSend(uint8 *transmitBuf, uint16 length, bool minc = 1); + + /** + * @brief Sets up a DMA Transmit for half words. + * SPI PERFIPHERAL MUST BE SET TO 16 BIT MODE BEFORE + * + * This function transmits and does not care about the RX fifo. * * @param data buffer half words to transmit, * @param length Number of bytes in buffer to transmit. + * @param minc Set to use Memory Increment mode (default if blank), clear to use Circular mode. */ - uint8 dmaSend(void * transmitBuf, uint16 length); + uint8 dmaSend(uint16 *transmitBuf, uint16 length, bool minc = 1); /* * Pin accessors From b2e349ca36d245869111db0502123b08c318f5e7 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Fri, 11 Nov 2016 22:12:13 +0100 Subject: [PATCH 06/25] Revert "Revert "improve SPI low level functions"" This reverts commit 3469ef291baad000b3c64564623d7a09e1312356. --- STM32F1/cores/maple/libmaple/spi.c | 6 +- STM32F1/libraries/SPI/src/SPI.cpp | 180 ++++++++++------------------- STM32F1/libraries/SPI/src/SPI.h | 25 ++-- 3 files changed, 75 insertions(+), 136 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/spi.c b/STM32F1/cores/maple/libmaple/spi.c index 1020c5b..64f06d3 100644 --- a/STM32F1/cores/maple/libmaple/spi.c +++ b/STM32F1/cores/maple/libmaple/spi.c @@ -84,7 +84,7 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { } /** - * @brief Nonblocking SPI transmit. + * @brief Blocking SPI transmit. * @param dev SPI port to use for transmission * @param buf Buffer to transmit. The sizeof buf's elements are * inferred from dev's data frame format (i.e., are @@ -95,7 +95,8 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) { uint32 txed = 0; uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT; - while (spi_is_tx_empty(dev) && (txed < len)) { + while ( txed < len ) { + while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty if (byte_frame) { dev->regs->DR = ((const uint8*)buf)[txed++]; } else { @@ -162,5 +163,6 @@ static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) { spi_irq_disable(dev, SPI_INTERRUPTS_ALL); if ( (dev->regs->CR1&MASK)!=(cr1_config&MASK) ) spi_peripheral_disable(dev); dev->regs->CR1 = cr1_config; + //spi_rx_dma_enable(dev); spi_peripheral_enable(dev); } diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 4eeec38..0832791 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -41,6 +41,8 @@ #include "boards.h" //#include "HardwareSerial.h" +/** Time in ms for DMA receive timeout */ +#define DMA_TIMEOUT 100 #if CYCLES_PER_MICROSECOND != 72 /* TODO [0.2.0?] something smarter than this */ @@ -304,138 +306,110 @@ void SPIClass::endTransaction(void) * I/O */ -uint8 SPIClass::read(void) { - uint8 buf[1]; - this->read(buf, 1); - return buf[0]; +uint8 SPIClass::read(void) +{ + while ( spi_is_rx_nonempty(_currentSetting->spi_d)==0 ) ; + return (uint8)spi_rx_reg(_currentSetting->spi_d); } -void SPIClass::read(uint8 *buf, uint32 len) { +void SPIClass::read(uint8 *buf, uint32 len) +{ + spi_reg_map * regs = _currentSetting->spi_d->regs; + uint8 b = (regs->DR); // clear the RX buffer in case a byte is waiting on it. uint32 rxed = 0; - while (rxed < len) { - while (!spi_is_rx_nonempty(_currentSetting->spi_d)) - ; - buf[rxed++] = (uint8)spi_rx_reg(_currentSetting->spi_d); + // start sequence + while ( rxed < len) { + regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." + while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register + buf[rxed++] = (uint8)(regs->DR); // read and store the received byte } } -void SPIClass::write(uint16 data) { - // this->write(&data, 1); - +void SPIClass::write(uint16 data) +{ /* Added for 16bit data Victor Perez. Roger Clark - * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) - * The original method, of calling write(*data, length) . + * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, + * by taking the Tx code from transfer(byte) * This almost doubles the speed of this function. */ - spi_tx_reg(_currentSetting->spi_d, data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } -//void SPIClass::write(uint8 byte) { - // this->write(&byte, 1); - - /* Roger Clark - * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte) - * The original method, of calling write(*data, length) . - * This almost doubles the speed of this function. - */ - -// spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." -// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." -// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." -//} - -void SPIClass::write(const uint8 *data, uint32 length) { - uint32 txed = 0; - while (txed < length) { - txed += spi_tx(_currentSetting->spi_d, data + txed, length - txed); - } - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "4. After writing the last data item into the SPI_DR register, wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... then wait until BSY=0, this indicates that the transmission of the last data is complete." +void SPIClass::write(const void *data, uint32 length) +{ + spi_tx(_currentSetting->spi_d, data, length); // data can be array of bytes or words + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." // taken from SdSpiSTM32F1.cpp - Victor's lib, and adapted to support device selection - if (spi_is_rx_nonempty(_currentSetting->spi_d)) { - uint8_t b = spi_rx_reg(_currentSetting->spi_d); - } + uint16 b = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! } uint16_t SPIClass::transfer16(uint16_t wr_data) const { spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." - uint16_t rd_data = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." -// while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." -// while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return rd_data; + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." } uint8 SPIClass::transfer(uint8 byte) const { spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_rx_nonempty(_currentSetting->spi_d) == 0); // "4. Wait until RXNE=1 ..." - uint8 b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return b; + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint8)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." } + /* Roger Clark and Victor Perez, 2015 * Performs a DMA SPI transfer with at least a receive buffer. -* If a TX buffer is not provided, FF is sent over and over for the lenght of the transfer. -* On exit TX buffer is not modified, and RX buffer cotains the received data. +* If a TX buffer is not provided, FF is sent over and over for the length of the transfer. +* On exit TX buffer is not modified, and RX buffer contains the received data. * Still in progress. */ -uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length) { +uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length) +{ if (length == 0) return 0; uint8 b = 0; - if (spi_is_rx_nonempty(_currentSetting->spi_d) == 1) b = spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. + spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); // dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - + // RX spi_rx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - receiveBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length); - + // TX + uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); spi_tx_dma_enable(_currentSetting->spi_d); - if (!transmitBuf) { - static uint8_t ff = 0XFF; - transmitBuf = &ff; - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, (DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit FF repeatedly - } - else { - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit buffer DMA + if ( transmitBuf==0 ) { + static uint8_t ff = 0XFF; + transmitBuf = &ff; + flags ^= DMA_MINC_MODE; // remove increment mode } + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit -// while (dma1_ch3_Active); -// if (receiveBuf) { uint32_t m = millis(); while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. - if ((millis() - m) > 100) { -// dma1_ch3_Active = 0; - b = 2; - break; - } + if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); -// } while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel); spi_rx_dma_disable(_currentSetting->spi_d); // And disable generation of DMA request from the SPI port so other peripherals can use the channels spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } + uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! return b; } @@ -443,10 +417,12 @@ uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length * Performs a DMA SPI send using a TX buffer. * On exit TX buffer is not modified. * Still in progress. +* 2016 - stevstrong - reworked to automatically detect bit size from SPI setting */ -uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) { +uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) +{ if (length == 0) return 0; - uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); + uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); uint8 b = 0; // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); @@ -454,55 +430,27 @@ uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) { // TX spi_tx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_8BITS, - transmitBuf, DMA_SIZE_8BITS, flags);// Transmit buffer DMA + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit - + // while (dma1_ch3_Active); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. + uint32_t m = millis(); + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } + } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - uint8 x = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } + uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! return b; } -uint8 SPIClass::dmaSend(uint16 *transmitBuf, uint16 length, bool minc) { - if (length == 0) return 0; - uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); - uint8 b; - dma1_ch3_Active=true; - dma_init(_currentSetting->spiDmaDev); -// dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - - // TX - spi_tx_dma_enable(_currentSetting->spi_d); - dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, DMA_SIZE_16BITS, - transmitBuf, DMA_SIZE_16BITS, flags);// Transmit buffer DMA - dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); - dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit - -// while (dma1_ch3_Active); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0); //Avoid interrupts and just loop waiting for the flag to be set. - dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); - - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); - spi_tx_dma_disable(_currentSetting->spi_d); - if (spi_is_rx_nonempty(_currentSetting->spi_d) != 0){; // "4. Wait until RXNE=1 ..." - b = spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." - } - return b; -} - - void SPIClass::attachInterrupt(void) { // Should be enableInterrupt() } diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index e949051..0f8497a 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -249,7 +249,7 @@ public: * @param buffer Bytes to transmit. * @param length Number of bytes in buffer to transmit. */ - void write(const uint8 *buffer, uint32 length); + void write(const void * buffer, uint32 length); /** * @brief Transmit a byte, then return the next unread byte. @@ -264,6 +264,7 @@ public: /** * @brief Sets up a DMA Transfer for "length" bytes. + * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. * * This function transmits and receives to buffers. * @@ -271,30 +272,18 @@ public: * @param receiveBuf buffer Bytes to save received data. * @param length Number of bytes in buffer to transmit. */ - uint8 dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length); + uint8 dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length); /** - * @brief Sets up a DMA Transmit for bytes. + * @brief Sets up a DMA Transmit for SPI 8 or 16 bit transfer mode. + * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting. * - * This function transmits and does not care about the RX fifo. - * - * @param transmitBuf buffer Bytes to transmit, - * @param length Number of bytes in buffer to transmit. - * @param minc Set to use Memory Increment mode, clear to use Circular mode. - */ - uint8 dmaSend(uint8 *transmitBuf, uint16 length, bool minc = 1); - - /** - * @brief Sets up a DMA Transmit for half words. - * SPI PERFIPHERAL MUST BE SET TO 16 BIT MODE BEFORE - * - * This function transmits and does not care about the RX fifo. + * This function only transmits and does not care about the RX fifo. * * @param data buffer half words to transmit, * @param length Number of bytes in buffer to transmit. - * @param minc Set to use Memory Increment mode (default if blank), clear to use Circular mode. */ - uint8 dmaSend(uint16 *transmitBuf, uint16 length, bool minc = 1); + uint8 dmaSend(void * transmitBuf, uint16 length); /* * Pin accessors From e7456e191650dc54b0fa8e7b95670f5621356265 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Sat, 10 Dec 2016 22:36:21 +0100 Subject: [PATCH 07/25] further optimizations - extend read function to 16bit - add repeated write the same byte/word a specified number of time - revert increment option to dmaSend --- STM32F1/cores/maple/libmaple/spi.c | 26 ++++++++++-------- STM32F1/libraries/SPI/src/SPI.cpp | 42 ++++++++++++++++++------------ STM32F1/libraries/SPI/src/SPI.h | 37 +++++++++++++++----------- 3 files changed, 63 insertions(+), 42 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/spi.c b/STM32F1/cores/maple/libmaple/spi.c index 64f06d3..e0ee90d 100644 --- a/STM32F1/cores/maple/libmaple/spi.c +++ b/STM32F1/cores/maple/libmaple/spi.c @@ -93,16 +93,21 @@ void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { * @return Number of elements transmitted. */ uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) { - uint32 txed = 0; - uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT; - while ( txed < len ) { - while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty - if (byte_frame) { - dev->regs->DR = ((const uint8*)buf)[txed++]; - } else { - dev->regs->DR = ((const uint16*)buf)[txed++]; - } - } + uint32 txed = len; + spi_reg_map *regs = dev->regs; + if ( spi_dff(dev) == SPI_DFF_8_BIT ) { + const uint8 * dp8 = (const uint8*)buf; + while ( len-- ) { + while ( (regs->SR & SPI_SR_TXE)==0 ) ; //while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty + regs->DR = *dp8++; + } + } else { + const uint16 * dp16 = (const uint16*)buf; + while ( len-- ) { + while ( (regs->SR & SPI_SR_TXE)==0 ) ; //while ( spi_is_tx_empty(dev)==0 ); // wait Tx to be empty + regs->DR = *dp16++; + } + } return txed; } @@ -163,6 +168,5 @@ static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) { spi_irq_disable(dev, SPI_INTERRUPTS_ALL); if ( (dev->regs->CR1&MASK)!=(cr1_config&MASK) ) spi_peripheral_disable(dev); dev->regs->CR1 = cr1_config; - //spi_rx_dma_enable(dev); spi_peripheral_enable(dev); } diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 0832791..9b6e28e 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -160,7 +160,7 @@ void SPIClass::begin(void) { void SPIClass::beginSlave(void) { spi_init(_currentSetting->spi_d); configure_gpios(_currentSetting->spi_d, 0); - uint32 flags = ((_currentSetting->bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB) | _currentSetting->dataSize | SPI_SW_SLAVE); + uint32 flags = ((_currentSetting->bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB) | _currentSetting->dataSize | SPI_RX_ONLY); #ifdef SPI_DEBUG Serial.print("spi_slave_enable("); Serial.print(_currentSetting->dataMode); Serial.print(","); Serial.print(flags); Serial.println(")"); #endif @@ -306,10 +306,10 @@ void SPIClass::endTransaction(void) * I/O */ -uint8 SPIClass::read(void) +uint16 SPIClass::read(void) { while ( spi_is_rx_nonempty(_currentSetting->spi_d)==0 ) ; - return (uint8)spi_rx_reg(_currentSetting->spi_d); + return (uint16)spi_rx_reg(_currentSetting->spi_d); } void SPIClass::read(uint8 *buf, uint32 len) @@ -321,7 +321,7 @@ void SPIClass::read(uint8 *buf, uint32 len) while ( rxed < len) { regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register - buf[rxed++] = (uint8)(regs->DR); // read and store the received byte + *buf++ = (regs->DR); // read and store the received byte } } @@ -332,11 +332,21 @@ void SPIClass::write(uint16 data) * by taking the Tx code from transfer(byte) * This almost doubles the speed of this function. */ - spi_tx_reg(_currentSetting->spi_d, data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + spi_tx_reg(_currentSetting->spi_d, data); // write the data to be transmitted into the SPI_DR register (this clears the TXE flag) while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } +void SPIClass::write(uint16 data, uint32 n) +{ + // Added by stevstrong: Repeatedly send same data by the specified number of times + spi_reg_map * regs = _currentSetting->spi_d->regs; + while ( (n--)>0 ) { + regs->DR = data; // write the data to be transmitted into the SPI_DR register (this clears the TXE flag) + while ( (regs->SR & SPI_SR_TXE)==0 ) ; // wait till Tx empty + } + while ( (regs->SR & SPI_SR_BSY) != 0); // wait until BSY=0 before returning +} + void SPIClass::write(const void *data, uint32 length) { spi_tx(_currentSetting->spi_d, data, length); // data can be array of bytes or words @@ -346,13 +356,6 @@ void SPIClass::write(const void *data, uint32 length) uint16 b = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! } -uint16_t SPIClass::transfer16(uint16_t wr_data) const { - spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." -} - uint8 SPIClass::transfer(uint8 byte) const { spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." @@ -360,6 +363,13 @@ uint8 SPIClass::transfer(uint8 byte) const { return (uint8)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." } +uint16_t SPIClass::transfer16(uint16_t wr_data) const { + spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." +} + /* Roger Clark and Victor Perez, 2015 * Performs a DMA SPI transfer with at least a receive buffer. * If a TX buffer is not provided, FF is sent over and over for the length of the transfer. @@ -419,10 +429,10 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length * Still in progress. * 2016 - stevstrong - reworked to automatically detect bit size from SPI setting */ -uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) +uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length, bool minc) { if (length == 0) return 0; - uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); + uint32 flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); uint8 b = 0; // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); @@ -438,7 +448,7 @@ uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length) // while (dma1_ch3_Active); uint32_t m = millis(); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set. if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index 0f8497a..717a2e9 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -115,6 +115,13 @@ public: init_MightInline(clock, bitOrder, dataMode, dataSize); } } + SPISettings(uint32_t clock) { + if (__builtin_constant_p(clock)) { + init_AlwaysInline(clock, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT); + } else { + init_MightInline(clock, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT); + } + } SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT); } private: void init_MightInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode, uint32_t dataSize) { @@ -216,38 +223,38 @@ public: */ /** - * @brief Return the next unread byte. + * @brief Return the next unread byte/word. * - * If there is no unread byte waiting, this function will block + * If there is no unread byte/word waiting, this function will block * until one is received. */ - uint8 read(void); + uint16 read(void); /** * @brief Read length bytes, storing them into buffer. * @param buffer Buffer to store received bytes into. - * @param length Number of bytes to store in buffer. This + * @param length Number of bytes to store in buffer. This * function will block until the desired number of * bytes have been read. */ void read(uint8 *buffer, uint32 length); /** - * @brief Transmit a byte. - * @param data Byte to transmit. - */ -// void write(uint8 data); - - /** - * @brief Transmit a half word. + * @brief Transmit one byte/word. * @param data to transmit. */ void write(uint16 data); /** - * @brief Transmit multiple bytes. - * @param buffer Bytes to transmit. - * @param length Number of bytes in buffer to transmit. + * @brief Transmit one byte/word a specified number of times. + * @param data to transmit. + */ + void write(uint16 data, uint32 n); + + /** + * @brief Transmit multiple bytes/words. + * @param buffer Bytes/words to transmit. + * @param length Number of bytes/words in buffer to transmit. */ void write(const void * buffer, uint32 length); @@ -283,7 +290,7 @@ public: * @param data buffer half words to transmit, * @param length Number of bytes in buffer to transmit. */ - uint8 dmaSend(void * transmitBuf, uint16 length); + uint8 dmaSend(void * transmitBuf, uint16 length, bool minc = 1); /* * Pin accessors From fb823b6929e4416b0921b4ff6c9ea4fa5a186262 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Mon, 26 Dec 2016 15:26:51 +0100 Subject: [PATCH 08/25] remove unused code & bugfixes - setDataSize shall first disable the SPI before writing new value - dmaTransfer adapted for TFT library usage --- .gitignore | 2 ++ STM32F1/libraries/SPI/src/SPI.cpp | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 05bb0a5..1fc5dc8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ other/maple-bootloader/build other/maple-bootloader/*~ *.o tools/src/stm32flash_serial/src/parsers/parsers.a +*.bak +*.1 diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 9b6e28e..45ee25a 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -214,8 +214,10 @@ void SPIClass::setBitOrder(BitOrder bitOrder) void SPIClass::setDataSize(uint32 datasize) { _currentSetting->dataSize = datasize; - uint32 cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_DFF); - _currentSetting->spi_d->regs->CR1 = cr1 | (datasize & SPI_CR1_DFF); + uint32 cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_DFF); + uint8 en = spi_is_enabled(_currentSetting->spi_d); + spi_peripheral_disable(_currentSetting->spi_d); + _currentSetting->spi_d->regs->CR1 = cr1 | (datasize & SPI_CR1_DFF) | en; } void SPIClass::setDataMode(uint8_t dataMode) @@ -316,12 +318,11 @@ void SPIClass::read(uint8 *buf, uint32 len) { spi_reg_map * regs = _currentSetting->spi_d->regs; uint8 b = (regs->DR); // clear the RX buffer in case a byte is waiting on it. - uint32 rxed = 0; // start sequence - while ( rxed < len) { + while ( (len--)>0) { regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register - *buf++ = (regs->DR); // read and store the received byte + *buf++ = (uint8)(regs->DR); // read and store the received byte } } @@ -333,6 +334,7 @@ void SPIClass::write(uint16 data) * This almost doubles the speed of this function. */ spi_tx_reg(_currentSetting->spi_d, data); // write the data to be transmitted into the SPI_DR register (this clears the TXE flag) + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } @@ -387,7 +389,7 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length // RX spi_rx_dma_enable(_currentSetting->spi_d); - dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS; dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length); @@ -408,7 +410,7 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit uint32_t m = millis(); - while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & 0x2)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set. if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); @@ -434,13 +436,10 @@ uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length, bool minc) if (length == 0) return 0; uint32 flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); uint8 b = 0; -// dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); -// dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); - // TX spi_tx_dma_enable(_currentSetting->spi_d); - dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_8BIT) ? DMA_SIZE_8BITS : DMA_SIZE_16BITS; + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS; dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); From 83e5f4832206f1924168c217df0c7bff1b06dad7 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Thu, 13 Apr 2017 22:34:37 +0200 Subject: [PATCH 09/25] remove "deprecated" flag from dma_setup_transfer remove deprecated flag --- STM32F1/cores/maple/libmaple/dma_f1.c | 1 - STM32F1/system/libmaple/stm32f1/include/series/dma.h | 1 - 2 files changed, 2 deletions(-) diff --git a/STM32F1/cores/maple/libmaple/dma_f1.c b/STM32F1/cores/maple/libmaple/dma_f1.c index 6400d15..c7c3c00 100644 --- a/STM32F1/cores/maple/libmaple/dma_f1.c +++ b/STM32F1/cores/maple/libmaple/dma_f1.c @@ -341,7 +341,6 @@ void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) { * @see dma_attach_interrupt() * @see dma_enable() */ -__deprecated void dma_setup_transfer(dma_dev *dev, dma_channel channel, __io void *peripheral_address, diff --git a/STM32F1/system/libmaple/stm32f1/include/series/dma.h b/STM32F1/system/libmaple/stm32f1/include/series/dma.h index bedb602..ff61857 100644 --- a/STM32F1/system/libmaple/stm32f1/include/series/dma.h +++ b/STM32F1/system/libmaple/stm32f1/include/series/dma.h @@ -559,7 +559,6 @@ typedef enum dma_mode_flags { * * (It's not possible to fully configure a DMA stream on F2 with just * this information, so this interface is too tied to the F1.) */ -__deprecated void dma_setup_transfer(dma_dev *dev, dma_channel channel, __io void *peripheral_address, From f1608abe0b8e250d22999a804fc105f391974305 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Mon, 22 May 2017 21:09:12 +0200 Subject: [PATCH 10/25] SPI update - cleaned up commented out lines - run-time optimized write and transfer functions - run-time optimized non-DMA block read routine - function call order within DMA routines tied to F4 structure - in line with AN4031, chapter 4.3 - reworked send functions (currently not used, deprecated, but still) --- STM32F1/libraries/SPI/src/SPI.cpp | 118 +++++++++++++++--------------- STM32F1/libraries/SPI/src/SPI.h | 2 +- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 45ee25a..1108014 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -135,9 +135,8 @@ SPIClass::SPIClass(uint32 spi_num) { _settings[2].spiDmaDev = DMA2; _settings[2].spiTxDmaChannel = DMA_CH2; _settings[2].spiRxDmaChannel = DMA_CH1; -#endif - - //pinMode(BOARD_SPI_DEFAULT_SS,OUTPUT); +#endif + } /* @@ -260,9 +259,6 @@ void SPIClass::beginTransaction(uint8_t pin, SPISettings settings) #ifdef SPI_DEBUG Serial.println("SPIClass::beginTransaction"); #endif - //_SSPin=pin; - //pinMode(_SSPin,OUTPUT); - //digitalWrite(_SSPin,LOW); setBitOrder(settings.bitOrder); setDataMode(settings.dataMode); setDataSize(settings.dataSize); @@ -316,14 +312,23 @@ uint16 SPIClass::read(void) void SPIClass::read(uint8 *buf, uint32 len) { + if ( len == 0 ) return; + spi_rx_reg(_currentSetting->spi_d); // clear the RX buffer in case a byte is waiting on it. spi_reg_map * regs = _currentSetting->spi_d->regs; - uint8 b = (regs->DR); // clear the RX buffer in case a byte is waiting on it. - // start sequence - while ( (len--)>0) { - regs->DR = 0x00FF; // " write the data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while ( (regs->SR & SPI_SR_RXNE)==0 ) ; // wait till data is available in the Rx register - *buf++ = (uint8)(regs->DR); // read and store the received byte - } + // start sequence: write byte 0 + regs->DR = 0x00FF; // write the first byte + // main loop + while ( (--len) ) { + while( !(regs->SR & SPI_SR_TXE) ); // wait for TXE flag + noInterrupts(); // go atomic level - avoid interrupts to surely get the previously received data + regs->DR = 0x00FF; // write the next data item to be transmitted into the SPI_DR register. This clears the TXE flag. + while ( !(regs->SR & SPI_SR_RXNE) ); // wait till data is available in the DR register + *buf++ = (uint8)(regs->DR); // read and store the received byte. This clears the RXNE flag. + interrupts(); // let systick do its job + } + // read remaining last byte + while ( !(regs->SR & SPI_SR_RXNE) ); // wait till data is available in the Rx register + *buf++ = (uint8)(regs->DR); // read and store the received byte } void SPIClass::write(uint16 data) @@ -344,32 +349,37 @@ void SPIClass::write(uint16 data, uint32 n) spi_reg_map * regs = _currentSetting->spi_d->regs; while ( (n--)>0 ) { regs->DR = data; // write the data to be transmitted into the SPI_DR register (this clears the TXE flag) - while ( (regs->SR & SPI_SR_TXE)==0 ) ; // wait till Tx empty + while ( (regs->SR & SPI_SR_TXE)==0 ) ; // wait till Tx empty } while ( (regs->SR & SPI_SR_BSY) != 0); // wait until BSY=0 before returning } -void SPIClass::write(const void *data, uint32 length) +void SPIClass::write(void *data, uint32 length) { - spi_tx(_currentSetting->spi_d, data, length); // data can be array of bytes or words - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - // taken from SdSpiSTM32F1.cpp - Victor's lib, and adapted to support device selection - uint16 b = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! + spi_dev * spi_d = _currentSetting->spi_d; + spi_tx(spi_d, (void*)data, length); // data can be array of bytes or words + while (spi_is_tx_empty(spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." } -uint8 SPIClass::transfer(uint8 byte) const { - spi_tx_reg(_currentSetting->spi_d, byte); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return (uint8)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." +uint8 SPIClass::transfer(uint8 byte) const +{ + spi_dev * spi_d = _currentSetting->spi_d; + spi_rx_reg(spi_d); // read any previous data + spi_tx_reg(spi_d, byte); // Write the data item to be transmitted into the SPI_DR register + while (spi_is_tx_empty(spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint8)spi_rx_reg(spi_d); // "... and read the last received data." } -uint16_t SPIClass::transfer16(uint16_t wr_data) const { - spi_tx_reg(_currentSetting->spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." - while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." - while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - return (uint16)spi_rx_reg(_currentSetting->spi_d); // "... and read the last received data." +uint16_t SPIClass::transfer16(uint16_t wr_data) const +{ + spi_dev * spi_d = _currentSetting->spi_d; + spi_rx_reg(spi_d); // read any previous data + spi_tx_reg(spi_d, wr_data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)." + while (spi_is_tx_empty(spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + return (uint16)spi_rx_reg(spi_d); // "... and read the last received data." } /* Roger Clark and Victor Perez, 2015 @@ -382,21 +392,19 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length { if (length == 0) return 0; uint8 b = 0; - spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. // dma1_ch3_Active=true; dma_init(_currentSetting->spiDmaDev); // dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event); // RX - spi_rx_dma_enable(_currentSetting->spi_d); dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS; dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, - receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA + receiveBuf, dma_bit_size, (DMA_MINC_MODE));// receive buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length); + dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive // TX - uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT); - spi_tx_dma_enable(_currentSetting->spi_d); + uint32 flags = (DMA_MINC_MODE | DMA_FROM_MEM); if ( transmitBuf==0 ) { static uint8_t ff = 0XFF; transmitBuf = &ff; @@ -405,23 +413,25 @@ uint8 SPIClass::dmaTransfer(void * transmitBuf, void * receiveBuf, uint16 length dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); - - dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive + dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit + + spi_rx_reg(_currentSetting->spi_d); //Clear the RX buffer in case a byte is waiting on it. + spi_rx_dma_enable(_currentSetting->spi_d); + spi_tx_dma_enable(_currentSetting->spi_d); // must be the last enable to avoid DMA error flag uint32_t m = millis(); while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + //delayMicroseconds(10); if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } - dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); - dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel); - spi_rx_dma_disable(_currentSetting->spi_d); // And disable generation of DMA request from the SPI port so other peripherals can use the channels spi_tx_dma_disable(_currentSetting->spi_d); - uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! + spi_rx_dma_disable(_currentSetting->spi_d); // And disable generation of DMA request from the SPI port so other peripherals can use the channels + dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel); return b; } @@ -438,26 +448,25 @@ uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length, bool minc) uint8 b = 0; dma_init(_currentSetting->spiDmaDev); // TX - spi_tx_dma_enable(_currentSetting->spi_d); dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS; dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, transmitBuf, dma_bit_size, flags);// Transmit buffer DMA dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); + dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit + spi_tx_dma_enable(_currentSetting->spi_d); -// while (dma1_ch3_Active); uint32_t m = millis(); while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + //delayMicroseconds(10); if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } } - dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." - dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); spi_tx_dma_disable(_currentSetting->spi_d); - uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove! - return b; + dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + return b; } void SPIClass::attachInterrupt(void) { @@ -493,18 +502,13 @@ uint8 SPIClass::nssPin(void) { */ uint8 SPIClass::send(uint8 data) { - uint8 buf[] = {data}; - return this->send(buf, 1); + this->write(data); + return 1; } uint8 SPIClass::send(uint8 *buf, uint32 len) { - uint32 txed = 0; - uint8 ret = 0; - while (txed < len) { - this->write(buf[txed++]); - ret = this->read(); - } - return ret; + this->write(buf, len); + return len; } uint8 SPIClass::recv(void) { diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index 717a2e9..b662384 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -256,7 +256,7 @@ public: * @param buffer Bytes/words to transmit. * @param length Number of bytes/words in buffer to transmit. */ - void write(const void * buffer, uint32 length); + void write(void * buffer, uint32 length); /** * @brief Transmit a byte, then return the next unread byte. From aa79ea1d6e5838bba1041f333c20f0758536452c Mon Sep 17 00:00:00 2001 From: stevstrong Date: Wed, 24 May 2017 18:40:41 +0200 Subject: [PATCH 11/25] Adafruit_ILI9341_STM - reworked for 16 bit SPI register accesses --- .../Adafruit_ILI9341_STM.cpp | 24 ++++--------------- .../Adafruit_ILI9341_STM.h | 8 +++++-- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.cpp b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.cpp index e21f33c..db57b3e 100644 --- a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.cpp +++ b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.cpp @@ -36,7 +36,7 @@ Adafruit_ILI9341_STM::Adafruit_ILI9341_STM(int8_t cs, int8_t dc, int8_t rst) : A } -void Adafruit_ILI9341_STM::spiwrite(uint8_t c) { +void Adafruit_ILI9341_STM::spiwrite(uint16_t c) { //Serial.print("0x"); Serial.print(c, HEX); Serial.print(", "); @@ -178,10 +178,7 @@ void Adafruit_ILI9341_STM::begin(void) { SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); #elif defined (__STM32F1__) - SPI.begin(); - SPI.setClockDivider(SPI_CLOCK_DIV2); - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); + SPI.beginTransaction(SPISettings(36000000)); #elif defined (__arm__) SPI.begin(); @@ -335,6 +332,7 @@ void Adafruit_ILI9341_STM::begin(void) { if (hwSPI) spi_begin(); writecommand(ILI9341_DISPON); //Display on if (hwSPI) spi_end(); + if (hwSPI) SPI.setDataSize(SPI_CR1_DFF); } @@ -345,18 +343,14 @@ void Adafruit_ILI9341_STM::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, writecommand(ILI9341_CASET); // Column addr set *dcport |= dcpinmask; *csport &= ~cspinmask; - SPI.setDataSize (SPI_CR1_DFF); SPI.write(x0); SPI.write(x1); -// SPI.setDataSize (0); writecommand(ILI9341_PASET); // Row addr set *dcport |= dcpinmask; *csport &= ~cspinmask; -// SPI.setDataSize (SPI_CR1_DFF); SPI.write(y0); SPI.write(y1); - SPI.setDataSize (0); writecommand(ILI9341_RAMWR); // write to RAM @@ -385,7 +379,6 @@ void Adafruit_ILI9341_STM::pushColor(uint16_t color) { //digitalWrite(_cs, LOW); *csport &= ~cspinmask; - spiwrite(color >> 8); spiwrite(color); *csport |= cspinmask; @@ -403,7 +396,6 @@ void Adafruit_ILI9341_STM::drawPixel(int16_t x, int16_t y, uint16_t color) { *dcport |= dcpinmask; *csport &= ~cspinmask; - spiwrite(color >> 8); spiwrite(color); *csport |= cspinmask; @@ -431,10 +423,8 @@ void Adafruit_ILI9341_STM::drawFastVLine(int16_t x, int16_t y, int16_t h, *csport &= ~cspinmask; #if defined (__STM32F1__) - SPI.setDataSize (SPI_CR1_DFF); // Set SPI 16bit mode lineBuffer[0] = color; SPI.dmaSend(lineBuffer, h, 0); - SPI.setDataSize (0); #else uint8_t hi = color >> 8, lo = color; while (h--) { @@ -464,10 +454,8 @@ void Adafruit_ILI9341_STM::drawFastHLine(int16_t x, int16_t y, int16_t w, *csport &= ~cspinmask; #if defined (__STM32F1__) - SPI.setDataSize (SPI_CR1_DFF); // Set spi 16bit mode lineBuffer[0] = color; SPI.dmaSend(lineBuffer, w, 0); - SPI.setDataSize (0); #else uint8_t hi = color >> 8, lo = color; while (w--) { @@ -485,11 +473,9 @@ void Adafruit_ILI9341_STM::fillScreen(uint16_t color) { setAddrWindow(0, 0, _width - 1, _height - 1); *dcport |= dcpinmask; *csport &= ~cspinmask; - SPI.setDataSize (SPI_CR1_DFF); // Set spi 16bit mode lineBuffer[0] = color; SPI.dmaSend(lineBuffer, (65535), 0); SPI.dmaSend(lineBuffer, ((_width * _height) - 65535), 0); - SPI.setDataSize (0); #else fillRect(0, 0, _width, _height, color); @@ -515,7 +501,6 @@ void Adafruit_ILI9341_STM::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, *dcport |= dcpinmask; *csport &= ~cspinmask; #if defined (__STM32F1__) - SPI.setDataSize (SPI_CR1_DFF); // Set spi 16bit mode lineBuffer[0] = color; if (w*h <= 65535) { SPI.dmaSend(lineBuffer, (w*h), 0); @@ -524,7 +509,6 @@ void Adafruit_ILI9341_STM::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, SPI.dmaSend(lineBuffer, (65535), 0); SPI.dmaSend(lineBuffer, ((w*h) - 65535), 0); } - SPI.setDataSize (0); #else uint8_t hi = color >> 8, lo = color; for(y=h; y>0; y--) @@ -672,6 +656,7 @@ uint16_t Adafruit_ILI9341_STM::color565(uint8_t r, uint8_t g, uint8_t b) { void Adafruit_ILI9341_STM::setRotation(uint8_t m) { if (hwSPI) spi_begin(); + if (hwSPI) SPI.setDataSize(0); writecommand(ILI9341_MADCTL); rotation = m % 4; // can't be higher than 3 switch (rotation) { @@ -696,6 +681,7 @@ void Adafruit_ILI9341_STM::setRotation(uint8_t m) { _height = ILI9341_TFTWIDTH; break; } + if (hwSPI) SPI.setDataSize(SPI_CR1_DFF); if (hwSPI) spi_end(); } diff --git a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h index a716c27..6554bc0 100644 --- a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h +++ b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h @@ -8,9 +8,13 @@ This library has been modified for the Maple Mini #include "Arduino.h" #include "Print.h" -#include +#include #include +#ifndef swap + #define swap(a, b) { int16_t t = a; a = b; b = t; } +#endif + #define ILI9341_TFTWIDTH 240 #define ILI9341_TFTHEIGHT 320 @@ -125,7 +129,7 @@ class Adafruit_ILI9341_STM : public Adafruit_GFX { void dummyclock(void); */ - void spiwrite(uint8_t), + void spiwrite(uint16_t), writecommand(uint8_t c), writedata(uint8_t d), commandList(uint8_t *addr); From 782b53119d7ee0c8dec605421c5bf5be60f622a4 Mon Sep 17 00:00:00 2001 From: stevstrong Date: Thu, 25 May 2017 10:18:27 +0200 Subject: [PATCH 12/25] used GFX library changed back to GFX_AS --- STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h index 6554bc0..137b7a4 100644 --- a/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h +++ b/STM32F1/libraries/Adafruit_ILI9341_STM/Adafruit_ILI9341_STM.h @@ -8,7 +8,7 @@ This library has been modified for the Maple Mini #include "Arduino.h" #include "Print.h" -#include +#include #include #ifndef swap From 9ae2f6fa78bc258d61a11fc3f0157fd3537780d0 Mon Sep 17 00:00:00 2001 From: Zou Hanya Date: Sat, 3 Jun 2017 08:27:33 +0900 Subject: [PATCH 13/25] Avoid initializing USB serial twice --- STM32F1/cores/maple/usb_serial.cpp | 5 +++++ STM32F1/cores/maple/usb_serial.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp index 5eaf70e..32fe471 100644 --- a/STM32F1/cores/maple/usb_serial.cpp +++ b/STM32F1/cores/maple/usb_serial.cpp @@ -55,6 +55,7 @@ static void ifaceSetupHook(unsigned, void*); #define USB_TIMEOUT 50 +bool USBSerial::_hasBegun = false; USBSerial::USBSerial(void) { #if !BOARD_HAVE_SERIALUSB ASSERT(0); @@ -62,6 +63,9 @@ USBSerial::USBSerial(void) { } void USBSerial::begin(void) { + if (_hasBegun) + return; + _hasBegun = true; #if BOARD_HAVE_SERIALUSB usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook); @@ -90,6 +94,7 @@ void USBSerial::end(void) { usb_cdcacm_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); usb_cdcacm_remove_hooks(USB_CDCACM_HOOK_RX | USB_CDCACM_HOOK_IFACE_SETUP); #endif + _hasBegun = false; } size_t USBSerial::write(uint8 ch) { diff --git a/STM32F1/cores/maple/usb_serial.h b/STM32F1/cores/maple/usb_serial.h index 96bbefc..3146a3c 100644 --- a/STM32F1/cores/maple/usb_serial.h +++ b/STM32F1/cores/maple/usb_serial.h @@ -71,6 +71,9 @@ public: uint8 getDTR(); uint8 isConnected(); uint8 pending(); + +protected: + static bool _hasBegun; }; #ifdef SERIAL_USB From df78777e98a411036036388cb0ab809f35c6741a Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Tue, 6 Jun 2017 20:45:50 +1000 Subject: [PATCH 14/25] Add yield() call in delay() to support the Arduino Scheduler library. Ref issue #299 --- STM32F1/cores/maple/wirish_time.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/STM32F1/cores/maple/wirish_time.cpp b/STM32F1/cores/maple/wirish_time.cpp index 08fd2d1..6404bec 100644 --- a/STM32F1/cores/maple/wirish_time.cpp +++ b/STM32F1/cores/maple/wirish_time.cpp @@ -32,11 +32,15 @@ #include #include +#include "Arduino.h" void delay(unsigned long ms) { uint32 start = millis(); - while (millis() - start < ms) - ; + do + { + yield(); + } + while (millis() - start < ms); } void delayMicroseconds(uint32 us) { From 3c4307ebac3102bf95bc6fd7b637449860bef053 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Wed, 7 Jun 2017 14:00:24 +1000 Subject: [PATCH 15/25] Fix issue where USB Serial begin(xx) begin(xx,yy) did nothing, when they should ahave called begin() --- STM32F1/cores/maple/usb_serial.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp index 32fe471..1fe2bf4 100644 --- a/STM32F1/cores/maple/usb_serial.cpp +++ b/STM32F1/cores/maple/usb_serial.cpp @@ -54,8 +54,10 @@ static void ifaceSetupHook(unsigned, void*); */ #define USB_TIMEOUT 50 - +#if BOARD_HAVE_SERIALUSB bool USBSerial::_hasBegun = false; +#endif + USBSerial::USBSerial(void) { #if !BOARD_HAVE_SERIALUSB ASSERT(0); @@ -63,10 +65,12 @@ USBSerial::USBSerial(void) { } void USBSerial::begin(void) { + +#if BOARD_HAVE_SERIALUSB if (_hasBegun) return; _hasBegun = true; -#if BOARD_HAVE_SERIALUSB + usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook); usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook); @@ -79,6 +83,7 @@ void USBSerial::begin(unsigned long ignoreBaud) volatile unsigned long removeCompilerWarningsIgnoreBaud=ignoreBaud; ignoreBaud=removeCompilerWarningsIgnoreBaud; + begin(); } void USBSerial::begin(unsigned long ignoreBaud, uint8_t ignore) { @@ -87,14 +92,16 @@ volatile uint8_t removeCompilerWarningsIgnore=ignore; ignoreBaud=removeCompilerWarningsIgnoreBaud; ignore=removeCompilerWarningsIgnore; + begin(); } void USBSerial::end(void) { #if BOARD_HAVE_SERIALUSB usb_cdcacm_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); usb_cdcacm_remove_hooks(USB_CDCACM_HOOK_RX | USB_CDCACM_HOOK_IFACE_SETUP); + _hasBegun = false; #endif - _hasBegun = false; + } size_t USBSerial::write(uint8 ch) { From 0444a3652709b2ed98de2fc708535859fa182727 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Sun, 11 Jun 2017 15:35:14 +1000 Subject: [PATCH 16/25] Added an additional SPI API function dmaSendAsync which will start the a DMA send of a buffer and return immediately. The next time dmaSendAsync is called it waits if the previous transfer is not complete. Note the buffer is not copied, so ddouble buffering is needed to use this function --- STM32F1/libraries/SPI/src/SPI.cpp | 42 +++++++++++++++++++++++++++++++ STM32F1/libraries/SPI/src/SPI.h | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/STM32F1/libraries/SPI/src/SPI.cpp b/STM32F1/libraries/SPI/src/SPI.cpp index 1108014..f0d9f7f 100644 --- a/STM32F1/libraries/SPI/src/SPI.cpp +++ b/STM32F1/libraries/SPI/src/SPI.cpp @@ -469,6 +469,48 @@ uint8 SPIClass::dmaSend(void * transmitBuf, uint16 length, bool minc) return b; } + +uint8 SPIClass::dmaSendAsync(void * transmitBuf, uint16 length, bool minc) +{ + static bool isRunning=false; + uint8 b = 0; + + if (isRunning) + { + + uint32_t m = millis(); + while ((dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)==0) {//Avoid interrupts and just loop waiting for the flag to be set. + //delayMicroseconds(10); + if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; } + } + + while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..." + while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI." + spi_tx_dma_disable(_currentSetting->spi_d); + dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + isRunning=false; + } + + + if (length == 0) return 0; + uint32 flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT); + + dma_init(_currentSetting->spiDmaDev); + // TX + dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS; + dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size, + transmitBuf, dma_bit_size, flags);// Transmit buffer DMA + dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length); + dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); + dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit + spi_tx_dma_enable(_currentSetting->spi_d); + + isRunning=true; + + return b; +} + + void SPIClass::attachInterrupt(void) { // Should be enableInterrupt() } diff --git a/STM32F1/libraries/SPI/src/SPI.h b/STM32F1/libraries/SPI/src/SPI.h index b662384..4d9b563 100644 --- a/STM32F1/libraries/SPI/src/SPI.h +++ b/STM32F1/libraries/SPI/src/SPI.h @@ -291,7 +291,7 @@ public: * @param length Number of bytes in buffer to transmit. */ uint8 dmaSend(void * transmitBuf, uint16 length, bool minc = 1); - + uint8 dmaSendAsync(void * transmitBuf, uint16 length, bool minc = 1); /* * Pin accessors */ From f47846662db1c7aa61430ca5ffcde8ce9d6b9579 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Sun, 11 Jun 2017 15:42:05 +1000 Subject: [PATCH 17/25] Added Wire.end() for bit banged version (TwoWire.cpp) --- STM32F1/libraries/Wire/Wire.cpp | 12 ++++++++++++ STM32F1/libraries/Wire/Wire.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/STM32F1/libraries/Wire/Wire.cpp b/STM32F1/libraries/Wire/Wire.cpp index abc57f8..01ee72f 100644 --- a/STM32F1/libraries/Wire/Wire.cpp +++ b/STM32F1/libraries/Wire/Wire.cpp @@ -184,6 +184,18 @@ void TwoWire::begin(uint8 self_addr) { set_sda(HIGH); } +void TwoWire::end() +{ + if (this->scl_pin) + { + pinMode(this->scl_pin, INPUT); + } + if (this->sda_pin) + { + pinMode(this->sda_pin, INPUT); + } +} + TwoWire::~TwoWire() { this->scl_pin=0; this->sda_pin=0; diff --git a/STM32F1/libraries/Wire/Wire.h b/STM32F1/libraries/Wire/Wire.h index add0ac9..c7edfef 100644 --- a/STM32F1/libraries/Wire/Wire.h +++ b/STM32F1/libraries/Wire/Wire.h @@ -111,6 +111,10 @@ class TwoWire : public WireBase { * Shifts out the data through SDA and clocks SCL for the slave device */ void i2c_shift_out(uint8); + + + + protected: /* * Processes the incoming I2C message defined by WireBase @@ -130,6 +134,11 @@ class TwoWire : public WireBase { */ void begin(uint8 = 0x00); + /* + * Sets pins SDA and SCL to INPUT + */ + void end(); + /* * If object is destroyed, set pin numbers to 0. */ From 953aa8b88e0c9479e591c693a03b6fa37a64a73a Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Sun, 11 Jun 2017 15:55:08 +1000 Subject: [PATCH 18/25] Added end() to HardWire (I2C) --- STM32F1/libraries/Wire/HardWire.cpp | 5 +++++ STM32F1/libraries/Wire/HardWire.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/STM32F1/libraries/Wire/HardWire.cpp b/STM32F1/libraries/Wire/HardWire.cpp index 8a63183..b3c1850 100644 --- a/STM32F1/libraries/Wire/HardWire.cpp +++ b/STM32F1/libraries/Wire/HardWire.cpp @@ -75,3 +75,8 @@ HardWire::~HardWire() { void HardWire::begin(uint8 self_addr) { i2c_master_enable(sel_hard, dev_flags); } + +void HardWire:end() { + i2c_disable(sel_hard); + sel_hard = 0; +} diff --git a/STM32F1/libraries/Wire/HardWire.h b/STM32F1/libraries/Wire/HardWire.h index 6f137b8..6f30cb3 100644 --- a/STM32F1/libraries/Wire/HardWire.h +++ b/STM32F1/libraries/Wire/HardWire.h @@ -59,6 +59,11 @@ public: * passed flags */ HardWire(uint8, uint8 = 0); + + /* + * Shuts down (disables) the hardware I2C + */ + void end(); /* * Disables the I2C device and remove the device address. From cc355255ac9b6358a4cd6e32b1d8216a49364764 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Mon, 12 Jun 2017 16:42:49 +1000 Subject: [PATCH 19/25] Added WS2812B (aka Neopixel) library using SPI DMA asynchronous transfers and double buffering --- STM32F1/libraries/WS2812B | 1 + 1 file changed, 1 insertion(+) create mode 160000 STM32F1/libraries/WS2812B diff --git a/STM32F1/libraries/WS2812B b/STM32F1/libraries/WS2812B new file mode 160000 index 0000000..b7f1e27 --- /dev/null +++ b/STM32F1/libraries/WS2812B @@ -0,0 +1 @@ +Subproject commit b7f1e2709e48f533e4d43aec3d36f8c48337bc31 From 7487d96f70ba3f78c00623556267cff6fb3a223c Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Tue, 13 Jun 2017 08:32:56 +1000 Subject: [PATCH 20/25] Fix typo in Hardware end() --- STM32F1/libraries/Wire/HardWire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STM32F1/libraries/Wire/HardWire.cpp b/STM32F1/libraries/Wire/HardWire.cpp index b3c1850..1f3ebf2 100644 --- a/STM32F1/libraries/Wire/HardWire.cpp +++ b/STM32F1/libraries/Wire/HardWire.cpp @@ -76,7 +76,7 @@ void HardWire::begin(uint8 self_addr) { i2c_master_enable(sel_hard, dev_flags); } -void HardWire:end() { +void HardWire::end() { i2c_disable(sel_hard); sel_hard = 0; } From 40dd7c6fe1a3f48a9657f54516737e80a78291a3 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Tue, 13 Jun 2017 10:37:16 +1000 Subject: [PATCH 21/25] Removed WS2812B lib as it had a .git folder in it which was causing problems --- STM32F1/libraries/WS2812B | 1 - 1 file changed, 1 deletion(-) delete mode 160000 STM32F1/libraries/WS2812B diff --git a/STM32F1/libraries/WS2812B b/STM32F1/libraries/WS2812B deleted file mode 160000 index b7f1e27..0000000 --- a/STM32F1/libraries/WS2812B +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b7f1e2709e48f533e4d43aec3d36f8c48337bc31 From ca484b4fdd2efcc1338381021ac267a211751639 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Tue, 13 Jun 2017 10:38:05 +1000 Subject: [PATCH 22/25] Add WS2812B library again, but this time without the .git folder in it --- STM32F1/libraries/WS2812B/README.md | 78 +++++ .../examples/WS2812B_test/WS2812B_test.ino | 303 ++++++++++++++++++ STM32F1/libraries/WS2812B/keywords.txt | 27 ++ STM32F1/libraries/WS2812B/library.properties | 10 + STM32F1/libraries/WS2812B/src/WS2812B.cpp | 238 ++++++++++++++ STM32F1/libraries/WS2812B/src/WS2812B.h | 81 +++++ 6 files changed, 737 insertions(+) create mode 100644 STM32F1/libraries/WS2812B/README.md create mode 100644 STM32F1/libraries/WS2812B/examples/WS2812B_test/WS2812B_test.ino create mode 100644 STM32F1/libraries/WS2812B/keywords.txt create mode 100644 STM32F1/libraries/WS2812B/library.properties create mode 100644 STM32F1/libraries/WS2812B/src/WS2812B.cpp create mode 100644 STM32F1/libraries/WS2812B/src/WS2812B.h diff --git a/STM32F1/libraries/WS2812B/README.md b/STM32F1/libraries/WS2812B/README.md new file mode 100644 index 0000000..3f10144 --- /dev/null +++ b/STM32F1/libraries/WS2812B/README.md @@ -0,0 +1,78 @@ +# WS2812B_STM32_Libmaple +WS2812B (Neopixel) library for Arduino STM32 (Libmaple core) + +Written by Roger Clark www.rogerclark.net, from first principals + +This library uses SPI DMA to control a strip of WS2812B (NeoPixel) LEDS + +It should be generally compatible with the Adafruit NeoPixel library, +except I have not had chance to implement one or two of the lesser used functions + +Connect Data In of the strip to SPI1 MOSI + +This library has only been tested on the WS2812B LED. It may not work with the older WS2812 or +other types of addressable RGB LED, becuase it relies on a division multiple of the 72Mhz clock +frequence on the STM32 SPI to generate the correct width T0H pulse, of 400ns +/- 150nS +SPI DIV32 gives a pulse width of 444nS which is well within spec for the WS2812B but +is probably too long for the WS2812 which needs a 350ns pulse for T0H + +##Technical details + +The library uses SPI to send the mark/space encoded data to the LED's + +Each mark/space squarewave is generated by 3 bits of data +A pixel 0 value is achieved by sending the bit pattern 100 +A pixel 1 value is achieved by sending the bit pattern 110 + +Where the duration of each bit is 444nS +Hence 100 generates a mark space value of 444/888nS +and 110 generates a mark space value of 888/444nS + +This method results in the smallest storage requirement and the fastest send times, +however because the 8 bit pixel channel data is encoded in 24 bits, (3 bytes) the values required in each of the 3 bytes to represent +a specific value is not easy to generate. + +The bit pattern in the 3 bytes is +88877766 65554443 33222111 + +For speed of operation the values reqired for each byte for each of the 256 possible values is held in 3 separate 256 byte LUTS +which were pre-computed by this function (which generates the full 24 bit pattern for a given input value (0-255) + +``` +uint32_t convert(uint8_t data) +{ + uint32_t out=0; + for(uint8_t mask = 0x80; mask; mask >>= 1) + { + out=out<<3; + if (data & mask) + { + out = out | 0B110;//Bit high + } + else + { + out = out | 0B100;// bit low + } + } + return out; +} +``` + +The STM32F103 has plenty of flash space (either 64 or 128k), so I used 256 byte LUTs even though the number of unique values in each LUT is +only 8,4 and 8 bytes respectively. +However to use small LUTS requires shifting and masking of the input data, and the code was written with a preference for speed over binary size + +The encoded pixel buffer is 2 bytes longer than the actual encoded data. +The first and last encoded bytes are all zeros. This is because the SPI hardware seems to preload MOSI with its output value before the start +of the DMA transfer, which causes the first encoded pulse to be around 50ns longer than the subsequent bits, (around 490 or 500ns) +This had the effect of causing the first LED to always think the MS bit of the green channel was set to High. + +So having the first encoded byte of zeros, is a work-around , as although the first encoded bit is still 490nS wide its a logic zero and is therefore +not visible, becuase the default state is of the SPI when not transmitting data is logic zero +The last byte was also set to all zeros, as occasionally MOSI seemed to be left set to logic high on completion of the SPI DMA send + +Adding these 2 bytes does slightly slow down the transfer, as it add 444ns * 8 = just over 3.5uS to both end. +But the WS2812B theoretically requires a reset time of more than 50uS between transmissions, so 3.5uS can be part of that time. +In reality the WS2812B seems to only need around 6uS of reset time, so for all practical purposes, there no delays are needed at all in the +library to enforce the reset time, as the overead of the function call and the SPI DMA setup plus the 3.5uS gives the enough reset time. + diff --git a/STM32F1/libraries/WS2812B/examples/WS2812B_test/WS2812B_test.ino b/STM32F1/libraries/WS2812B/examples/WS2812B_test/WS2812B_test.ino new file mode 100644 index 0000000..bf4934f --- /dev/null +++ b/STM32F1/libraries/WS2812B/examples/WS2812B_test/WS2812B_test.ino @@ -0,0 +1,303 @@ +#include + +#define NUM_LEDS 30 +/* + * Note. Library uses SPI1 + * Connect the WS2812B data input to MOSI on your board. + * + */ +WS2812B strip = WS2812B(NUM_LEDS); +// Note. Gamma is not really supported in the library, its only included as some functions used in this example require Gamma +uint8_t LEDGamma[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, + 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, + 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, + 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, +215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; + +void setup() +{ + strip.begin();// Sets up the SPI + strip.show();// Clears the strip, as by default the strip data is set to all LED's off. + // strip.setBrightness(8); +} + +void loop() +{ + colorWipe(strip.Color(0, 255, 0), 20); // Green + colorWipe(strip.Color(255, 0, 0), 20); // Red + colorWipe(strip.Color(0, 0, 255), 20); // Blue + rainbow(10); + rainbowCycle(10); + theaterChase(strip.Color(255, 0, 0), 20); // Red + theaterChase(strip.Color(0, 255, 0), 20); // Green + theaterChase(strip.Color(0, 0, 255), 20); // Blue + theaterChaseRainbow(10); + whiteOverRainbow(20,75,5); + pulseWhite(5); + delay(250); + fullWhite(); + delay(250); + rainbowFade2White(3,3,1); +} + +// Fill the dots one after the other with a color +void colorWipe(uint32_t c, uint8_t wait) +{ + for(uint16_t i=0; i= 0 ; j--){ + for(uint16_t i=0; i 255 - fadeMax ){ + fadeVal--; + } + + strip.show(); + delay(wait); + } + + } + + + + delay(500); + + + for(int k = 0 ; k < whiteLoops ; k ++){ + + for(int j = 0; j < 256 ; j++){ + + for(uint16_t i=0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, strip.Color(0,0,0, LEDGamma[j] ) ); + } + strip.show(); + } + + delay(2000); + for(int j = 255; j >= 0 ; j--){ + + for(uint16_t i=0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, strip.Color(0,0,0, LEDGamma[j] ) ); + } + strip.show(); + } + } + + delay(500); + + +} + +void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) { + + if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1; + + int head = whiteLength - 1; + int tail = 0; + + int loops = 3; + int loopNum = 0; + + static unsigned long lastTime = 0; + + + while(true){ + for(int j=0; j<256; j++) { + for(uint16_t i=0; i= tail && i <= head) || (tail > head && i >= tail) || (tail > head && i <= head) ){ + strip.setPixelColor(i, strip.Color(0,0,0, 255 ) ); + } + else{ + strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); + } + + } + + if(millis() - lastTime > whiteSpeed) { + head++; + tail++; + if(head == strip.numPixels()){ + loopNum++; + } + lastTime = millis(); + } + + if(loopNum == loops) return; + + head%=strip.numPixels(); + tail%=strip.numPixels(); + strip.show(); + delay(wait); + } + } + +} +void fullWhite() { + + for(uint16_t i=0; i> 16); +} +uint8_t green(uint32_t c) { + return (c >> 8); +} +uint8_t blue(uint32_t c) { + return (c); +} diff --git a/STM32F1/libraries/WS2812B/keywords.txt b/STM32F1/libraries/WS2812B/keywords.txt new file mode 100644 index 0000000..2d8b83a --- /dev/null +++ b/STM32F1/libraries/WS2812B/keywords.txt @@ -0,0 +1,27 @@ +####################################### +# Syntax Coloring Map For WS2812B +# Class +####################################### + +WS2812B KEYWORD1 + +####################################### +# Methods and Functions +####################################### + +setPixelColor KEYWORD2 +numPixels KEYWORD2 +Color KEYWORD2 +show KEYWORD2 + +clear KEYWORD2 +updateLength KEYWORD2 + +canShow KEYWORD2 + + +####################################### +# Constants +####################################### + + diff --git a/STM32F1/libraries/WS2812B/library.properties b/STM32F1/libraries/WS2812B/library.properties new file mode 100644 index 0000000..936976d --- /dev/null +++ b/STM32F1/libraries/WS2812B/library.properties @@ -0,0 +1,10 @@ +name=WS2812B +version=1.0 +author=Roger Clark, based on the Adafruit Neopixel library API +email= +sentence=WS2812B (Neopixel) library +paragraph=WS2812B (Neopixel) library for STM32F1 LibMaple +url= +architectures=STM32F1 +maintainer=Roger Clark +category=Uncategorized \ No newline at end of file diff --git a/STM32F1/libraries/WS2812B/src/WS2812B.cpp b/STM32F1/libraries/WS2812B/src/WS2812B.cpp new file mode 100644 index 0000000..36483fc --- /dev/null +++ b/STM32F1/libraries/WS2812B/src/WS2812B.cpp @@ -0,0 +1,238 @@ +/*----------------------------------------------------------------------------------------------- + Arduino library to control WS2812B RGB Led strips using the Arduino STM32 LibMaple core + ----------------------------------------------------------------------------------------------- + + Note. + This library has only been tested on the WS2812B LED. It may not work with the older WS2812 or + other types of addressable RGB LED, becuase it relies on a division multiple of the 72Mhz clock + frequence on the STM32 SPI to generate the correct width T0H pulse, of 400ns +/- 150nS + SPI DIV32 gives a pulse width of 444nS which is well within spec for the WS2812B but + is probably too long for the WS2812 which needs a 350ns pulse for T0H + + This WS2811B library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + It is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + See . + -----------------------------------------------------------------------------------------------*/ + +#include "WS2812B.h" +#include "pins_arduino.h" +#include "wiring_private.h" +#include + + +// Constructor when n is the number of LEDs in the strip +WS2812B::WS2812B(uint16_t number_of_leds) : + brightness(0), pixels(NULL) +{ + updateLength(number_of_leds); +} + + +WS2812B::~WS2812B() +{ + if(pixels) + { + free(pixels); + } + SPI.end(); +} + +void WS2812B::begin(void) { + +if (!begun) +{ + SPI.setClockDivider(SPI_CLOCK_DIV32);// need bit rate of 400nS but closest we can do @ 72Mhz is 444ns (which is within spec) + SPI.begin(); + begun = true; +} +} + +void WS2812B::updateLength(uint16_t n) +{ + if(doubleBuffer) + { + free(doubleBuffer); + } + + numBytes = (n<<3) + n + 2; // 9 encoded bytes per pixel. 1 byte empty peamble to fix issue with SPI MOSI and on byte at the end to clear down MOSI + // Note. (n<<3) +n is a fast way of doing n*9 + if((doubleBuffer = (uint8_t *)malloc(numBytes*2))) + { + numLEDs = n; + pixels = doubleBuffer; + // Only need to init the part of the double buffer which will be interacted with by the API e.g. setPixelColor + *pixels=0;//clear the preamble byte + *(pixels+numBytes-1)=0;// clear the post send cleardown byte. + clear();// Set the encoded data to all encoded zeros + } + else + { + numLEDs = numBytes = 0; + } +} + +// Sends the current buffer to the leds +void WS2812B::show(void) +{ + SPI.dmaSendAsync(pixels,numBytes);// Start the DMA transfer of the current pixel buffer to the LEDs and return immediately. + + // Need to copy the last / current buffer to the other half of the double buffer as most API code does not rebuild the entire contents + // from scratch. Often just a few pixels are changed e.g in a chaser effect + + if (pixels==doubleBuffer) + { + // pixels was using the first buffer + pixels = doubleBuffer+numBytes; // set pixels to second buffer + memcpy(pixels,doubleBuffer,numBytes);// copy first buffer to second buffer + } + else + { + // pixels was using the second buffer + pixels = doubleBuffer; // set pixels to first buffer + memcpy(pixels,doubleBuffer+numBytes,numBytes); // copy second buffer to first buffer + } +} + +/*Sets a specific pixel to a specific r,g,b colour +* Because the pixels buffer contains the encoded bitstream, which is in triplets +* the lookup table need to be used to find the correct pattern for each byte in the 3 byte sequence. +*/ +void WS2812B::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) + { + uint8_t *bptr = pixels + (n<<3) + n +1; + uint8_t *tPtr = (uint8_t *)encoderLookup + g*2 + g;// need to index 3 x g into the lookup + + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + + tPtr = (uint8_t *)encoderLookup + r*2 + r; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + + tPtr = (uint8_t *)encoderLookup + b*2 + b; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + } + +void WS2812B::setPixelColor(uint16_t n, uint32_t c) + { + uint8_t r,g,b; + + if(brightness) + { + r = ((int)((uint8_t)(c >> 16)) * (int)brightness) >> 8; + g = ((int)((uint8_t)(c >> 8)) * (int)brightness) >> 8; + b = ((int)((uint8_t)c) * (int)brightness) >> 8; + } + else + { + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + } + + uint8_t *bptr = pixels + (n<<3) + n +1; + uint8_t *tPtr = (uint8_t *)encoderLookup + g*2 + g;// need to index 3 x g into the lookup + + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + + tPtr = (uint8_t *)encoderLookup + r*2 + r; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + + tPtr = (uint8_t *)encoderLookup + b*2 + b; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; +} + +// Convert separate R,G,B into packed 32-bit RGB color. +// Packed format is always RGB, regardless of LED strand color order. +uint32_t WS2812B::Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + +// Convert separate R,G,B,W into packed 32-bit WRGB color. +// Packed format is always WRGB, regardless of LED strand color order. +uint32_t WS2812B::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + + +uint16_t WS2812B::numPixels(void) const { + return numLEDs; +} + +// Adjust output brightness; 0=darkest (off), 255=brightest. This does +// NOT immediately affect what's currently displayed on the LEDs. The +// next call to show() will refresh the LEDs at this level. However, +// this process is potentially "lossy," especially when increasing +// brightness. The tight timing in the WS2811/WS2812 code means there +// aren't enough free cycles to perform this scaling on the fly as data +// is issued. So we make a pass through the existing color data in RAM +// and scale it (subsequent graphics commands also work at this +// brightness level). If there's a significant step up in brightness, +// the limited number of steps (quantization) in the old data will be +// quite visible in the re-scaled version. For a non-destructive +// change, you'll need to re-render the full strip data. C'est la vie. +void WS2812B::setBrightness(uint8_t b) { + // Stored brightness value is different than what's passed. + // This simplifies the actual scaling math later, allowing a fast + // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t, + // adding 1 here may (intentionally) roll over...so 0 = max brightness + // (color values are interpreted literally; no scaling), 1 = min + // brightness (off), 255 = just below max brightness. + uint8_t newBrightness = b + 1; + if(newBrightness != brightness) { // Compare against prior value + // Brightness has changed -- re-scale existing data in RAM + uint8_t c, + *ptr = pixels, + oldBrightness = brightness - 1; // De-wrap old brightness value + uint16_t scale; + if(oldBrightness == 0) scale = 0; // Avoid /0 + else if(b == 255) scale = 65535 / oldBrightness; + else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; + for(uint16_t i=0; i> 8; + } + brightness = newBrightness; + } +} + +//Return the brightness value +uint8_t WS2812B::getBrightness(void) const { + return brightness - 1; +} + +/* +* Sets the encoded pixel data to turn all the LEDs off. +*/ +void WS2812B::clear() +{ + uint8_t * bptr= pixels+1;// Note first byte in the buffer is a preable and is always zero. hence the +1 + uint8_t *tPtr; + + for(int i=0;i< (numLEDs *3);i++) + { + tPtr = (uint8_t *)encoderLookup; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + *bptr++ = *tPtr++; + } +} diff --git a/STM32F1/libraries/WS2812B/src/WS2812B.h b/STM32F1/libraries/WS2812B/src/WS2812B.h new file mode 100644 index 0000000..30bc875 --- /dev/null +++ b/STM32F1/libraries/WS2812B/src/WS2812B.h @@ -0,0 +1,81 @@ +/*-------------------------------------------------------------------- + The WS2812B library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + It is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + See . + --------------------------------------------------------------------*/ + +#ifndef WS2812B_H +#define WS2812B_H + +#include +/* + * old version used 3 separate tables, one per byte of the 24 bit encoded data + * +static const uint8_t byte0Lookup[256]={0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD2,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB}; +static const uint8_t byte1Lookup[256]={0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D}; +static const uint8_t byte2Lookup[256]={0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6}; +*/ + +// New version uses one large LUT as its faster index into sequential bytes for the GRB pattern +static const uint8_t encoderLookup[256*3]={ 0x92,0x49,0x24,0x92,0x49,0x26,0x92,0x49,0x34,0x92,0x49,0x36,0x92,0x49,0xA4,0x92,0x49,0xA6,0x92,0x49,0xB4,0x92,0x49,0xB6,0x92,0x4D,0x24,0x92,0x4D,0x26,0x92,0x4D,0x34,0x92,0x4D,0x36,0x92,0x4D,0xA4,0x92,0x4D,0xA6,0x92,0x4D,0xB4,0x92,0x4D,0xB6,0x92,0x69,0x24,0x92,0x69,0x26,0x92,0x69,0x34,0x92,0x69,0x36,0x92,0x69,0xA4,0x92,0x69,0xA6,0x92,0x69,0xB4,0x92,0x69,0xB6,0x92,0x6D,0x24,0x92,0x6D,0x26,0x92,0x6D,0x34,0x92,0x6D,0x36,0x92,0x6D,0xA4,0x92,0x6D,0xA6,0x92,0x6D,0xB4,0x92,0x6D,0xB6,0x93,0x49,0x24,0x93,0x49,0x26,0x93,0x49,0x34,0x93,0x49,0x36,0x93,0x49,0xA4,0x93,0x49,0xA6,0x93,0x49,0xB4,0x93,0x49,0xB6,0x93,0x4D,0x24,0x93,0x4D,0x26,0x93,0x4D,0x34,0x93,0x4D,0x36,0x93,0x4D,0xA4,0x93,0x4D,0xA6,0x93,0x4D,0xB4,0x93,0x4D,0xB6,0x93,0x69,0x24,0x93,0x69,0x26,0x93,0x69,0x34,0x93,0x69,0x36,0x93,0x69,0xA4,0x93,0x69,0xA6,0x93,0x69,0xB4,0x93,0x69,0xB6,0x93,0x6D,0x24,0x93,0x6D,0x26,0x93,0x6D,0x34,0x93,0x6D,0x36,0x93,0x6D,0xA4,0x93,0x6D,0xA6,0x93,0x6D,0xB4,0x93,0x6D,0xB6,0x9A,0x49,0x24,0x9A,0x49,0x26,0x9A,0x49,0x34,0x9A,0x49,0x36,0x9A,0x49,0xA4,0x9A,0x49,0xA6,0x9A,0x49,0xB4,0x9A,0x49,0xB6,0x9A,0x4D,0x24,0x9A,0x4D,0x26,0x9A,0x4D,0x34,0x9A,0x4D,0x36,0x9A,0x4D,0xA4,0x9A,0x4D,0xA6,0x9A,0x4D,0xB4,0x9A,0x4D,0xB6,0x9A,0x69,0x24,0x9A,0x69,0x26,0x9A,0x69,0x34,0x9A,0x69,0x36,0x9A,0x69,0xA4,0x9A,0x69,\ + 0xA6,0x9A,0x69,0xB4,0x9A,0x69,0xB6,0x9A,0x6D,0x24,0x9A,0x6D,0x26,0x9A,0x6D,0x34,0x9A,0x6D,0x36,0x9A,0x6D,0xA4,0x9A,0x6D,0xA6,0x9A,0x6D,0xB4,0x9A,0x6D,0xB6,0x9B,0x49,0x24,0x9B,0x49,0x26,0x9B,0x49,0x34,0x9B,0x49,0x36,0x9B,0x49,0xA4,0x9B,0x49,0xA6,0x9B,0x49,0xB4,0x9B,0x49,0xB6,0x9B,0x4D,0x24,0x9B,0x4D,0x26,0x9B,0x4D,0x34,0x9B,0x4D,0x36,0x9B,0x4D,0xA4,0x9B,0x4D,0xA6,0x9B,0x4D,0xB4,0x9B,0x4D,0xB6,0x9B,0x69,0x24,0x9B,0x69,0x26,0x9B,0x69,0x34,0x9B,0x69,0x36,0x9B,0x69,0xA4,0x9B,0x69,0xA6,0x9B,0x69,0xB4,0x9B,0x69,0xB6,0x9B,0x6D,0x24,0x9B,0x6D,0x26,0x9B,0x6D,0x34,0x9B,0x6D,0x36,0x9B,0x6D,0xA4,0x9B,0x6D,0xA6,0x9B,0x6D,0xB4,0x9B,0x6D,0xB6,0xD2,0x49,0x24,0xD2,0x49,0x26,0xD2,0x49,0x34,0xD2,0x49,0x36,0xD2,0x49,0xA4,0xD2,0x49,0xA6,0xD2,0x49,0xB4,0xD2,0x49,0xB6,0xD2,0x4D,0x24,0xD2,0x4D,0x26,0xD2,0x4D,0x34,0xD2,0x4D,0x36,0xD2,0x4D,0xA4,0xD2,0x4D,0xA6,0xD2,0x4D,0xB4,0xD2,0x4D,0xB6,0xD2,0x69,0x24,0xD2,0x69,0x26,0xD2,0x69,0x34,0xD2,0x69,0x36,0xD2,0x69,0xA4,0xD2,0x69,0xA6,0xD2,0x69,0xB4,0xD2,0x69,0xB6,0xD2,0x6D,0x24,0xD2,0x6D,0x26,0xD2,0x6D,0x34,0xD2,0x6D,0x36,0xD2,0x6D,0xA4,0xD2,0x6D,0xA6,0xD2,0x6D,0xB4,0xD2,0x6D,0xB6,0xD3,0x49,0x24,0xD3,0x49,0x26,0xD3,0x49,0x34,0xD3,0x49,0x36,0xD3,0x49,0xA4,0xD3,0x49,0xA6,0xD3,0x49,0xB4,0xD3,0x49,0xB6,0xD3,0x4D,0x24,0xD3,0x4D,0x26,0xD3,0x4D,0x34,0xD3,\ + 0x4D,0x36,0xD3,0x4D,0xA4,0xD3,0x4D,0xA6,0xD3,0x4D,0xB4,0xD3,0x4D,0xB6,0xD3,0x69,0x24,0xD3,0x69,0x26,0xD3,0x69,0x34,0xD3,0x69,0x36,0xD3,0x69,0xA4,0xD3,0x69,0xA6,0xD3,0x69,0xB4,0xD3,0x69,0xB6,0xD3,0x6D,0x24,0xD3,0x6D,0x26,0xD3,0x6D,0x34,0xD3,0x6D,0x36,0xD3,0x6D,0xA4,0xD3,0x6D,0xA6,0xD3,0x6D,0xB4,0xD3,0x6D,0xB6,0xDA,0x49,0x24,0xDA,0x49,0x26,0xDA,0x49,0x34,0xDA,0x49,0x36,0xDA,0x49,0xA4,0xDA,0x49,0xA6,0xDA,0x49,0xB4,0xDA,0x49,0xB6,0xDA,0x4D,0x24,0xDA,0x4D,0x26,0xDA,0x4D,0x34,0xDA,0x4D,0x36,0xDA,0x4D,0xA4,0xDA,0x4D,0xA6,0xDA,0x4D,0xB4,0xDA,0x4D,0xB6,0xDA,0x69,0x24,0xDA,0x69,0x26,0xDA,0x69,0x34,0xDA,0x69,0x36,0xDA,0x69,0xA4,0xDA,0x69,0xA6,0xDA,0x69,0xB4,0xDA,0x69,0xB6,0xDA,0x6D,0x24,0xDA,0x6D,0x26,0xDA,0x6D,0x34,0xDA,0x6D,0x36,0xDA,0x6D,0xA4,0xDA,0x6D,0xA6,0xDA,0x6D,0xB4,0xDA,0x6D,0xB6,0xDB,0x49,0x24,0xDB,0x49,0x26,0xDB,0x49,0x34,0xDB,0x49,0x36,0xDB,0x49,0xA4,0xDB,0x49,0xA6,0xDB,0x49,0xB4,0xDB,0x49,0xB6,0xDB,0x4D,0x24,0xDB,0x4D,0x26,0xDB,0x4D,0x34,0xDB,0x4D,0x36,0xDB,0x4D,0xA4,0xDB,0x4D,0xA6,0xDB,0x4D,0xB4,0xDB,0x4D,0xB6,0xDB,0x69,0x24,0xDB,0x69,0x26,0xDB,0x69,0x34,0xDB,0x69,0x36,0xDB,0x69,0xA4,0xDB,0x69,0xA6,0xDB,0x69,0xB4,0xDB,0x69,0xB6,0xDB,0x6D,0x24,0xDB,0x6D,0x26,0xDB,0x6D,0x34,0xDB,0x6D,0x36,0xDB,0x6D,0xA4,0xDB,0x6D,0xA6,0xDB,0x6D,0xB4,0xDB,0x6D,0xB6}; + +class WS2812B { + public: + + // Constructor: number of LEDs + WS2812B (uint16_t number_of_leds);// Constuctor + ~WS2812B(); + void + begin(void), + show(void), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b), + // setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w), + setPixelColor(uint16_t n, uint32_t c), + setBrightness(uint8_t), + clear(), + updateLength(uint16_t n); + uint8_t +// *getPixels(void) const, + getBrightness(void) const; + uint16_t + numPixels(void) const; + static uint32_t + Color(uint8_t r, uint8_t g, uint8_t b), + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w); + // uint32_t + // getPixelColor(uint16_t n) const; + inline bool + canShow(void) { return (micros() - endTime) >= 300L; } + + private: + + boolean + begun; // true if begin() previously called + uint16_t + numLEDs, // Number of RGB LEDs in strip + numBytes; // Size of 'pixels' buffer + + uint8_t + brightness, + *pixels, // Holds the current LED color values, which the external API calls interact with 9 bytes per pixel + start + end empty bytes + *doubleBuffer, // Holds the start of the double buffer (1 buffer for async DMA transfer and one for the API interaction. + rOffset, // Index of red byte within each 3- or 4-byte pixel + gOffset, // Index of green byte + bOffset, // Index of blue byte + wOffset; // Index of white byte (same as rOffset if no white) + uint32_t + endTime; // Latch timing reference +}; + + +#endif // WS2812B_H From aef93f74cde8f1127eb9b4c571d9f8fe910854b8 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Tue, 13 Jun 2017 10:39:42 +1000 Subject: [PATCH 23/25] Moved encoderLookup LUT from flash to RAM to give 30% speed increase of setPixelColor() - at the expense of 768 bytes of RAM --- STM32F1/libraries/WS2812B/src/WS2812B.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STM32F1/libraries/WS2812B/src/WS2812B.h b/STM32F1/libraries/WS2812B/src/WS2812B.h index 30bc875..3a6bcb6 100644 --- a/STM32F1/libraries/WS2812B/src/WS2812B.h +++ b/STM32F1/libraries/WS2812B/src/WS2812B.h @@ -25,7 +25,7 @@ static const uint8_t byte2Lookup[256]={0x24,0x26,0x34,0x36,0xA4,0xA6,0xB4,0xB6,0 */ // New version uses one large LUT as its faster index into sequential bytes for the GRB pattern -static const uint8_t encoderLookup[256*3]={ 0x92,0x49,0x24,0x92,0x49,0x26,0x92,0x49,0x34,0x92,0x49,0x36,0x92,0x49,0xA4,0x92,0x49,0xA6,0x92,0x49,0xB4,0x92,0x49,0xB6,0x92,0x4D,0x24,0x92,0x4D,0x26,0x92,0x4D,0x34,0x92,0x4D,0x36,0x92,0x4D,0xA4,0x92,0x4D,0xA6,0x92,0x4D,0xB4,0x92,0x4D,0xB6,0x92,0x69,0x24,0x92,0x69,0x26,0x92,0x69,0x34,0x92,0x69,0x36,0x92,0x69,0xA4,0x92,0x69,0xA6,0x92,0x69,0xB4,0x92,0x69,0xB6,0x92,0x6D,0x24,0x92,0x6D,0x26,0x92,0x6D,0x34,0x92,0x6D,0x36,0x92,0x6D,0xA4,0x92,0x6D,0xA6,0x92,0x6D,0xB4,0x92,0x6D,0xB6,0x93,0x49,0x24,0x93,0x49,0x26,0x93,0x49,0x34,0x93,0x49,0x36,0x93,0x49,0xA4,0x93,0x49,0xA6,0x93,0x49,0xB4,0x93,0x49,0xB6,0x93,0x4D,0x24,0x93,0x4D,0x26,0x93,0x4D,0x34,0x93,0x4D,0x36,0x93,0x4D,0xA4,0x93,0x4D,0xA6,0x93,0x4D,0xB4,0x93,0x4D,0xB6,0x93,0x69,0x24,0x93,0x69,0x26,0x93,0x69,0x34,0x93,0x69,0x36,0x93,0x69,0xA4,0x93,0x69,0xA6,0x93,0x69,0xB4,0x93,0x69,0xB6,0x93,0x6D,0x24,0x93,0x6D,0x26,0x93,0x6D,0x34,0x93,0x6D,0x36,0x93,0x6D,0xA4,0x93,0x6D,0xA6,0x93,0x6D,0xB4,0x93,0x6D,0xB6,0x9A,0x49,0x24,0x9A,0x49,0x26,0x9A,0x49,0x34,0x9A,0x49,0x36,0x9A,0x49,0xA4,0x9A,0x49,0xA6,0x9A,0x49,0xB4,0x9A,0x49,0xB6,0x9A,0x4D,0x24,0x9A,0x4D,0x26,0x9A,0x4D,0x34,0x9A,0x4D,0x36,0x9A,0x4D,0xA4,0x9A,0x4D,0xA6,0x9A,0x4D,0xB4,0x9A,0x4D,0xB6,0x9A,0x69,0x24,0x9A,0x69,0x26,0x9A,0x69,0x34,0x9A,0x69,0x36,0x9A,0x69,0xA4,0x9A,0x69,\ +static uint8_t encoderLookup[256*3]={ 0x92,0x49,0x24,0x92,0x49,0x26,0x92,0x49,0x34,0x92,0x49,0x36,0x92,0x49,0xA4,0x92,0x49,0xA6,0x92,0x49,0xB4,0x92,0x49,0xB6,0x92,0x4D,0x24,0x92,0x4D,0x26,0x92,0x4D,0x34,0x92,0x4D,0x36,0x92,0x4D,0xA4,0x92,0x4D,0xA6,0x92,0x4D,0xB4,0x92,0x4D,0xB6,0x92,0x69,0x24,0x92,0x69,0x26,0x92,0x69,0x34,0x92,0x69,0x36,0x92,0x69,0xA4,0x92,0x69,0xA6,0x92,0x69,0xB4,0x92,0x69,0xB6,0x92,0x6D,0x24,0x92,0x6D,0x26,0x92,0x6D,0x34,0x92,0x6D,0x36,0x92,0x6D,0xA4,0x92,0x6D,0xA6,0x92,0x6D,0xB4,0x92,0x6D,0xB6,0x93,0x49,0x24,0x93,0x49,0x26,0x93,0x49,0x34,0x93,0x49,0x36,0x93,0x49,0xA4,0x93,0x49,0xA6,0x93,0x49,0xB4,0x93,0x49,0xB6,0x93,0x4D,0x24,0x93,0x4D,0x26,0x93,0x4D,0x34,0x93,0x4D,0x36,0x93,0x4D,0xA4,0x93,0x4D,0xA6,0x93,0x4D,0xB4,0x93,0x4D,0xB6,0x93,0x69,0x24,0x93,0x69,0x26,0x93,0x69,0x34,0x93,0x69,0x36,0x93,0x69,0xA4,0x93,0x69,0xA6,0x93,0x69,0xB4,0x93,0x69,0xB6,0x93,0x6D,0x24,0x93,0x6D,0x26,0x93,0x6D,0x34,0x93,0x6D,0x36,0x93,0x6D,0xA4,0x93,0x6D,0xA6,0x93,0x6D,0xB4,0x93,0x6D,0xB6,0x9A,0x49,0x24,0x9A,0x49,0x26,0x9A,0x49,0x34,0x9A,0x49,0x36,0x9A,0x49,0xA4,0x9A,0x49,0xA6,0x9A,0x49,0xB4,0x9A,0x49,0xB6,0x9A,0x4D,0x24,0x9A,0x4D,0x26,0x9A,0x4D,0x34,0x9A,0x4D,0x36,0x9A,0x4D,0xA4,0x9A,0x4D,0xA6,0x9A,0x4D,0xB4,0x9A,0x4D,0xB6,0x9A,0x69,0x24,0x9A,0x69,0x26,0x9A,0x69,0x34,0x9A,0x69,0x36,0x9A,0x69,0xA4,0x9A,0x69,\ 0xA6,0x9A,0x69,0xB4,0x9A,0x69,0xB6,0x9A,0x6D,0x24,0x9A,0x6D,0x26,0x9A,0x6D,0x34,0x9A,0x6D,0x36,0x9A,0x6D,0xA4,0x9A,0x6D,0xA6,0x9A,0x6D,0xB4,0x9A,0x6D,0xB6,0x9B,0x49,0x24,0x9B,0x49,0x26,0x9B,0x49,0x34,0x9B,0x49,0x36,0x9B,0x49,0xA4,0x9B,0x49,0xA6,0x9B,0x49,0xB4,0x9B,0x49,0xB6,0x9B,0x4D,0x24,0x9B,0x4D,0x26,0x9B,0x4D,0x34,0x9B,0x4D,0x36,0x9B,0x4D,0xA4,0x9B,0x4D,0xA6,0x9B,0x4D,0xB4,0x9B,0x4D,0xB6,0x9B,0x69,0x24,0x9B,0x69,0x26,0x9B,0x69,0x34,0x9B,0x69,0x36,0x9B,0x69,0xA4,0x9B,0x69,0xA6,0x9B,0x69,0xB4,0x9B,0x69,0xB6,0x9B,0x6D,0x24,0x9B,0x6D,0x26,0x9B,0x6D,0x34,0x9B,0x6D,0x36,0x9B,0x6D,0xA4,0x9B,0x6D,0xA6,0x9B,0x6D,0xB4,0x9B,0x6D,0xB6,0xD2,0x49,0x24,0xD2,0x49,0x26,0xD2,0x49,0x34,0xD2,0x49,0x36,0xD2,0x49,0xA4,0xD2,0x49,0xA6,0xD2,0x49,0xB4,0xD2,0x49,0xB6,0xD2,0x4D,0x24,0xD2,0x4D,0x26,0xD2,0x4D,0x34,0xD2,0x4D,0x36,0xD2,0x4D,0xA4,0xD2,0x4D,0xA6,0xD2,0x4D,0xB4,0xD2,0x4D,0xB6,0xD2,0x69,0x24,0xD2,0x69,0x26,0xD2,0x69,0x34,0xD2,0x69,0x36,0xD2,0x69,0xA4,0xD2,0x69,0xA6,0xD2,0x69,0xB4,0xD2,0x69,0xB6,0xD2,0x6D,0x24,0xD2,0x6D,0x26,0xD2,0x6D,0x34,0xD2,0x6D,0x36,0xD2,0x6D,0xA4,0xD2,0x6D,0xA6,0xD2,0x6D,0xB4,0xD2,0x6D,0xB6,0xD3,0x49,0x24,0xD3,0x49,0x26,0xD3,0x49,0x34,0xD3,0x49,0x36,0xD3,0x49,0xA4,0xD3,0x49,0xA6,0xD3,0x49,0xB4,0xD3,0x49,0xB6,0xD3,0x4D,0x24,0xD3,0x4D,0x26,0xD3,0x4D,0x34,0xD3,\ 0x4D,0x36,0xD3,0x4D,0xA4,0xD3,0x4D,0xA6,0xD3,0x4D,0xB4,0xD3,0x4D,0xB6,0xD3,0x69,0x24,0xD3,0x69,0x26,0xD3,0x69,0x34,0xD3,0x69,0x36,0xD3,0x69,0xA4,0xD3,0x69,0xA6,0xD3,0x69,0xB4,0xD3,0x69,0xB6,0xD3,0x6D,0x24,0xD3,0x6D,0x26,0xD3,0x6D,0x34,0xD3,0x6D,0x36,0xD3,0x6D,0xA4,0xD3,0x6D,0xA6,0xD3,0x6D,0xB4,0xD3,0x6D,0xB6,0xDA,0x49,0x24,0xDA,0x49,0x26,0xDA,0x49,0x34,0xDA,0x49,0x36,0xDA,0x49,0xA4,0xDA,0x49,0xA6,0xDA,0x49,0xB4,0xDA,0x49,0xB6,0xDA,0x4D,0x24,0xDA,0x4D,0x26,0xDA,0x4D,0x34,0xDA,0x4D,0x36,0xDA,0x4D,0xA4,0xDA,0x4D,0xA6,0xDA,0x4D,0xB4,0xDA,0x4D,0xB6,0xDA,0x69,0x24,0xDA,0x69,0x26,0xDA,0x69,0x34,0xDA,0x69,0x36,0xDA,0x69,0xA4,0xDA,0x69,0xA6,0xDA,0x69,0xB4,0xDA,0x69,0xB6,0xDA,0x6D,0x24,0xDA,0x6D,0x26,0xDA,0x6D,0x34,0xDA,0x6D,0x36,0xDA,0x6D,0xA4,0xDA,0x6D,0xA6,0xDA,0x6D,0xB4,0xDA,0x6D,0xB6,0xDB,0x49,0x24,0xDB,0x49,0x26,0xDB,0x49,0x34,0xDB,0x49,0x36,0xDB,0x49,0xA4,0xDB,0x49,0xA6,0xDB,0x49,0xB4,0xDB,0x49,0xB6,0xDB,0x4D,0x24,0xDB,0x4D,0x26,0xDB,0x4D,0x34,0xDB,0x4D,0x36,0xDB,0x4D,0xA4,0xDB,0x4D,0xA6,0xDB,0x4D,0xB4,0xDB,0x4D,0xB6,0xDB,0x69,0x24,0xDB,0x69,0x26,0xDB,0x69,0x34,0xDB,0x69,0x36,0xDB,0x69,0xA4,0xDB,0x69,0xA6,0xDB,0x69,0xB4,0xDB,0x69,0xB6,0xDB,0x6D,0x24,0xDB,0x6D,0x26,0xDB,0x6D,0x34,0xDB,0x6D,0x36,0xDB,0x6D,0xA4,0xDB,0x6D,0xA6,0xDB,0x6D,0xB4,0xDB,0x6D,0xB6}; From 8af6002812e15379ac75e1494558a894ed24970b Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Mon, 19 Jun 2017 17:11:04 +1000 Subject: [PATCH 24/25] Added link to gitter chat room in the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 019cb2b..b6c60f6 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ This repo contains, the "Hardware" files to support STM32 based boards on Arduin ***PRIMARY SUPPORT FORUM: http://www.stm32duino.com/*** +*** We are also on Gitter https://gitter.im/stm32duino/Lobby *** + ##Background & Support: * Based on https://github.com/bobc/maple-asp, which is in turn based on LibMaple by Leaflabs * **Please read the wiki (https://github.com/rogerclarkmelbourne/Arduino_STM32/wiki) for full details** From 6fa060c05a702f5d68c4c63752cdb2911b0764b1 Mon Sep 17 00:00:00 2001 From: Roger Clark Date: Mon, 19 Jun 2017 17:11:31 +1000 Subject: [PATCH 25/25] Updated readme for gitter (again) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6c60f6..b77db01 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repo contains, the "Hardware" files to support STM32 based boards on Arduin ***PRIMARY SUPPORT FORUM: http://www.stm32duino.com/*** -*** We are also on Gitter https://gitter.im/stm32duino/Lobby *** +***We are also on Gitter https://gitter.im/stm32duino/Lobby/*** ##Background & Support: * Based on https://github.com/bobc/maple-asp, which is in turn based on LibMaple by Leaflabs