Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 1 | #include "cape/uart_dma.h" |
| 2 | #include "cape/uart_common_private.h" |
| 3 | |
| 4 | #include "cape/util.h" |
| 5 | #include "cape/uart_common.h" |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 6 | #include "cape/led.h" |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 7 | |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 8 | #define DMA DMA2 |
| 9 | #define DMA_Stream DMA2_Stream7 |
| 10 | #define DMA_SR DMA2->HISR |
| 11 | #define DMA_FCR DMA2->HIFCR |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 12 | #define DMA_SR_SHIFT 3 |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 13 | #define DMA_Stream_IRQHandler DMA2_Stream7_IRQHandler |
| 14 | #define DMA_Stream_IRQn DMA2_Stream7_IRQn |
| 15 | #define DMA_CHANNEL_NUMBER 4 |
| 16 | #define RCC_AHB1ENR_DMAEN RCC_AHB1ENR_DMA2EN |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 17 | |
| 18 | #define DMA_SR_BIT(bit) (1 << (bit + 6 * DMA_SR_SHIFT)) |
| 19 | |
| 20 | void uart_dma_callback(uint8_t *new_buffer) __attribute__((weak)); |
| 21 | void uart_dma_callback(uint8_t *new_buffer) {} |
| 22 | |
| 23 | static uint8_t *volatile buffer1, *volatile buffer2; |
| 24 | |
| 25 | void DMA_Stream_IRQHandler(void) { |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 26 | led_set(LED_DB, 1); |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 27 | uint32_t status = DMA_SR; |
| 28 | if (status & DMA_SR_BIT(5)) { // transfer completed |
| 29 | DMA_FCR = DMA_SR_BIT(5); |
| 30 | uart_dma_callback(((DMA_Stream->CR & DMA_SxCR_CT) == 0) ? buffer2 |
| 31 | : buffer1); |
| 32 | } else if (status & DMA_SR_BIT(3)) { // transfer error |
| 33 | DMA_FCR = DMA_SR_BIT(3); |
| 34 | // Somebody probably wrote to the wrong buffer, which disables the DMA, so |
| 35 | // we now need to re-enable it. |
| 36 | // If we're fighting somebody else writing stuff, we'll do this a bunch of |
| 37 | // times, but oh well. |
| 38 | DMA_Stream->CR |= DMA_SxCR_EN; |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 39 | led_set(LED_ERR, 1); |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 40 | } |
| 41 | } |
| 42 | |
Brian Silverman | df49fe3 | 2013-12-11 14:21:37 -0800 | [diff] [blame] | 43 | void uart_dma_configure(int bytes, uint8_t *buffer1_in, uint8_t *buffer2_in) { |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 44 | buffer1 = buffer1_in; |
| 45 | buffer2 = buffer2_in; |
| 46 | uart_dma_callback(buffer1); |
| 47 | |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 48 | UART->CR3 = USART_CR3_DMAT; |
Brian Silverman | 53f2918 | 2013-12-21 15:16:27 -0800 | [diff] [blame^] | 49 | UART->CR1 |= USART_CR1_TE; |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 50 | |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 51 | RCC->AHB1ENR |= RCC_AHB1ENR_DMAEN; |
Brian Silverman | 53f2918 | 2013-12-21 15:16:27 -0800 | [diff] [blame^] | 52 | DMA_Stream->CR = 0; |
| 53 | while (DMA_Stream->CR & DMA_SxCR_EN); // make sure it's disabled |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 54 | DMA_Stream->PAR = (uint32_t)&UART->DR; |
| 55 | DMA_Stream->M0AR = (uint32_t)buffer1; |
| 56 | DMA_Stream->M1AR = (uint32_t)buffer2; |
| 57 | // This is measured in chunks of PSIZE bytes, not MSIZE. |
| 58 | DMA_Stream->NDTR = bytes; |
Brian Silverman | 53f2918 | 2013-12-21 15:16:27 -0800 | [diff] [blame^] | 59 | DMA_Stream->CR = DMA_CHANNEL_NUMBER << 25 | |
| 60 | DMA_SxCR_DBM /* enable double buffer mode */ | |
| 61 | 2 << 16 /* priority */ | |
| 62 | 2 << 13 /* memory data size = 32 bits */ | |
| 63 | 0 << 11 /* peripherial data size = 8 bits */ | |
| 64 | DMA_SxCR_MINC /* increment memory address */ | |
| 65 | 1 << 6 /* memory to peripherial */ | |
| 66 | //DMA_SxCR_PFCTRL /* peripherial controls flow */ | |
| 67 | DMA_SxCR_TCIE | DMA_SxCR_TEIE; |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 68 | DMA_Stream->FCR = |
| 69 | DMA_SxFCR_DMDIS /* disable direct mode (enable the FIFO) */ | |
Brian Silverman | 53f2918 | 2013-12-21 15:16:27 -0800 | [diff] [blame^] | 70 | 1 /* 1/2 full threshold */; |
| 71 | UART->SR = ~USART_SR_TC; |
| 72 | DMA_FCR = 0xF << DMA_SR_SHIFT; |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 73 | DMA_Stream->CR |= DMA_SxCR_EN; // enable it |
Brian Silverman | 95244d8 | 2013-12-14 12:15:46 -0800 | [diff] [blame] | 74 | NVIC_SetPriority(DMA_Stream_IRQn, 8); |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 75 | NVIC_EnableIRQ(DMA_Stream_IRQn); |
| 76 | |
| 77 | uart_dma_callback(buffer2); |
Brian Silverman | ed03006 | 2013-12-20 21:03:47 -0800 | [diff] [blame] | 78 | led_set(LED_Z, 1); |
Brian Silverman | 2df8441 | 2013-12-10 14:00:40 -0800 | [diff] [blame] | 79 | } |