# HG changeset patch # User Paul Boddie # Date 1539895555 -7200 # Node ID 0c0f7da8b56558932352c2866d9a47aaa7ab3c0b # Parent 06ce1f92f3e02402687ac37b99330ab4c2a64b86 Added output compare support, demonstrating its use with DMA cell transfers. Tidied interrupt initialisation, making it consistent between peripherals. Fixed various parameter names and adjusted the formatting in places. diff -r 06ce1f92f3e0 -r 0c0f7da8b565 init.c --- a/init.c Thu Oct 18 21:05:18 2018 +0200 +++ b/init.c Thu Oct 18 22:45:55 2018 +0200 @@ -204,7 +204,8 @@ /* Set interrupt priorities. */ - REG(DMAIPC) = (REG(DMAIPC) & ~(DMA_IPC_PRI(channel, 7, 3))) | + REG(DMAIPC) = (REG(DMAIPC) & + ~(DMA_IPC_PRI(channel, 7, 3))) | DMA_IPC_PRI(channel, pri, sub); /* Enable interrupt. */ @@ -226,6 +227,71 @@ +/* Output compare configuration. */ + +void oc_init(int unit, uint8_t mode, int timer) +{ + if ((unit < OCMIN) || (unit > OCMAX)) + return; + + REG(OC_REG(unit, OCxCON)) = (timer == 3 ? (1 << 3) : 0) | (mode & 0b111); +} + +/* Set the start value for the pulse. */ + +void oc_set_pulse(int unit, uint32_t start) +{ + if ((unit < OCMIN) || (unit > OCMAX)) + return; + + REG(OC_REG(unit, OCxR)) = start; +} + +/* Set the end value for the pulse. */ + +void oc_set_pulse_end(int unit, uint32_t end) +{ + if ((unit < OCMIN) || (unit > OCMAX)) + return; + + REG(OC_REG(unit, OCxRS)) = end; +} + +/* Configure interrupts caused by the unit. */ + +void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub) +{ + if ((unit < OCMIN) || (unit > OCMAX)) + return; + + /* Disable interrupt and clear interrupt flag. */ + + CLR_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); + CLR_REG(OCIFS, OC_INT_FLAGS(unit, OCxIF)); + + /* Set interrupt priorities. */ + + REG(OC_IPC_REG(unit)) = (REG(OC_IPC_REG(unit)) & + ~(OC_IPC_PRI(unit, 7, 3))) | + OC_IPC_PRI(unit, pri, sub); + + /* Enable interrupt. */ + + SET_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); +} + +/* Enable a unit. */ + +void oc_on(int unit) +{ + if ((unit < OCMIN) || (unit > OCMAX)) + return; + + SET_REG(OC_REG(unit, OCxCON), 1 << 15); +} + + + /* Timer configuration. */ void timer_init(int timer, uint8_t prescale, uint16_t limit) @@ -244,6 +310,11 @@ if ((timer < TIMERMIN) || (timer > TIMERMAX)) return; + /* Disable interrupt and clear interrupt flag. */ + + CLR_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); + CLR_REG(TIMERIFS, TIMER_INT_FLAGS(timer, TxIF)); + /* Set interrupt priorities. */ REG(TIMER_IPC_REG(timer)) = (REG(TIMER_IPC_REG(timer)) & @@ -291,11 +362,6 @@ */ REG(UART_REG(uart, UxBRG)) = (FPB / (16 * baudrate)) - 1; - - /* Disable interrupt (UxRIE) and clear flag (UxRIF). */ - - CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxRIE)); - CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxRIF)); } /* Configure interrupts caused by the UART. */ @@ -306,10 +372,16 @@ if ((uart < UARTMIN) || (uart > UARTMAX)) return; + /* Disable interrupts and clear interrupt flags. */ + + CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxTIE | UxRIE | UxEIE)); + CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxTIF | UxRIF | UxEIF)); + /* Set priorities: UxIP = pri; UxIS = sub */ - REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & ~UART_IPC_PRI(uart, 7, 3)) | - UART_IPC_PRI(uart, pri, sub); + REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & + ~UART_IPC_PRI(uart, 7, 3)) | + UART_IPC_PRI(uart, pri, sub); /* Enable interrupts. */ @@ -357,6 +429,36 @@ return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); } +/* Return encoded output compare interrupt priorities for combining with a register. */ + +uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub) +{ + (void) unit; + return PRI(pri, sub) << OCIPCBASE; +} + +/* Return the output compare interrupt priorities register. */ + +uint32_t OC_IPC_REG(int unit) +{ + switch (unit) + { + case 1: return OC1IPC; + case 2: return OC2IPC; + case 3: return OC3IPC; + case 4: return OC4IPC; + case 5: return OC5IPC; + default: return 0; /* should not occur */ + } +} + +/* Return the output compare interrupt flags for combining with a register. */ + +int OC_INT_FLAGS(int unit, uint8_t flags) +{ + return (flags & 0b1) << (OCINTBASE + (unit - OCMIN) * OCINTSTEP); +} + /* Return encoded timer interrupt priorities for combining with a register. */ uint32_t TIMER_IPC_PRI(int timer, uint8_t pri, uint8_t sub) diff -r 06ce1f92f3e0 -r 0c0f7da8b565 init.h --- a/init.h Thu Oct 18 21:05:18 2018 +0200 +++ b/init.h Thu Oct 18 22:45:55 2018 +0200 @@ -47,6 +47,23 @@ /* Timer configuration. */ +void oc_init(int unit, uint8_t mode, int timer); + +void oc_set_pulse(int unit, uint32_t start); + +void oc_set_pulse_end(int unit, uint32_t end); + +void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub); + +void oc_on(int unit); + +int OC_INT_FLAGS(int unit, uint8_t flags); + +uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub); +uint32_t OC_IPC_REG(int unit); + +/* Timer configuration. */ + void timer_init(int timer, uint8_t prescale, uint16_t limit); void timer_init_interrupt(int timer, uint8_t pri, uint8_t sub); diff -r 06ce1f92f3e0 -r 0c0f7da8b565 main.c --- a/main.c Thu Oct 18 21:05:18 2018 +0200 +++ b/main.c Thu Oct 18 22:45:55 2018 +0200 @@ -47,27 +47,40 @@ HW_PHYSICAL(UART_REG(1, UxTXREG)), 1, 1); - /* Enable DMA on the preceding channel's completion, with Timer3 initiating - transfers, raising a transfer completion interrupt. */ + /* Enable DMA on the preceding channel's completion, with OC1 initiating + transfers, raising a transfer completion interrupt to be handled. */ dma_init(1, 3); dma_set_chaining(1, dma_chain_previous); - dma_set_interrupt(1, T3, 1); + dma_set_interrupt(1, OC1, 1); dma_set_transfer(1, PHYSICAL((uint32_t) message2), sizeof(message2) - 1, HW_PHYSICAL(UART_REG(1, UxTXREG)), 1, 1); dma_init_interrupt(1, 0b00001000, 7, 3); - /* Configure timers. */ + /* Configure a timer for the first DMA channel whose interrupt condition + drives the transfer but is not handled (having a lower priority than the + CPU. */ timer_init(2, 0b111, 60000); timer_init_interrupt(2, 1, 3); timer_on(2); - timer_init(3, 0b111, 40000); - timer_init_interrupt(3, 1, 3); + /* Configure a timer for the output compare unit below. */ + + timer_init(3, 0b111, 20000); timer_on(3); + /* Configure output compare in dual compare (continuous output) mode using + Timer3 as time base. The interrupt condition drives the second DMA + channel but is not handled (having a lower priority than the CPU). */ + + oc_init(1, 0b101, 3); + oc_set_pulse(1, 10000); + oc_set_pulse_end(1, 20000); + oc_init_interrupt(1, 1, 3); + oc_on(1); + /* Set UART interrupt priority above CPU priority to process events. */ uart_init(1, 115200); diff -r 06ce1f92f3e0 -r 0c0f7da8b565 pic32.h --- a/pic32.h Thu Oct 18 21:05:18 2018 +0200 +++ b/pic32.h Thu Oct 18 22:45:55 2018 +0200 @@ -8,16 +8,6 @@ * PIC32MX1XX/2XX 28/36/44-pin Family Data Sheet */ -#define OC1CON 0xBF803000 -#define OC1R 0xBF803010 -#define OC1RS 0xBF803020 -#define OC2CON 0xBF803200 -#define OC2R 0xBF803210 -#define OC2RS 0xBF803220 -#define OC3CON 0xBF803400 -#define OC3R 0xBF803410 -#define OC3RS 0xBF803420 - #define PMCON 0xBF807000 #define PMMODE 0xBF807010 #define PMADDR 0xBF807020 @@ -110,10 +100,46 @@ #define DMAIEC IEC1 #define DMAIFS IFS1 #define DMAINTBASE 28 + #define DMAIPC IPC10 #define DCHIPCBASE 0 #define DCHIPCSTEP 8 +/* Output compare conveniences. */ + +#define OC1CON 0xBF803000 +#define OC2CON 0xBF803200 +#define OC3CON 0xBF803400 +#define OC4CON 0xBF803600 +#define OC5CON 0xBF803800 + +#define OCMIN 1 +#define OCMAX 5 +#define OCBASE OC1CON +#define OCSTEP (OC2CON - OC1CON) + +#define OCxCON 0x00 +#define OCxR 0x10 +#define OCxRS 0x20 + +#define OCIEC IEC0 + +#define OCxIE 1 + +#define OCIFS IFS0 + +#define OCxIF 1 + +#define OCINTBASE 7 +#define OCINTSTEP 5 + +#define OC1IPC IPC1 +#define OC2IPC IPC2 +#define OC3IPC IPC3 +#define OC4IPC IPC4 +#define OC5IPC IPC5 +#define OCIPCBASE 16 + /* Timer conveniences. */ #define T1CON 0xBF800600 @@ -195,6 +221,11 @@ #define DMA1 61 #define DMA2 62 #define DMA3 63 +#define OC1 7 +#define OC2 12 +#define OC3 17 +#define OC4 22 +#define OC5 27 #define T1 4 #define T2 9 #define T3 14 diff -r 06ce1f92f3e0 -r 0c0f7da8b565 pic32_c.h --- a/pic32_c.h Thu Oct 18 21:05:18 2018 +0200 +++ b/pic32_c.h Thu Oct 18 22:45:55 2018 +0200 @@ -47,14 +47,19 @@ return DCHBASE + reg + (channel - DCHMIN) * DCHSTEP; } -static inline uint32_t TIMER_REG(int channel, uint32_t reg) +static inline uint32_t OC_REG(int unit, uint32_t reg) { - return TIMERBASE + reg + (channel - TIMERMIN) * TIMERSTEP; + return OCBASE + reg + (unit - OCMIN) * OCSTEP; } -static inline uint32_t UART_REG(int channel, uint32_t reg) +static inline uint32_t TIMER_REG(int timer, uint32_t reg) { - return UARTBASE + reg + (channel - UARTMIN) * UARTSTEP; + return TIMERBASE + reg + (timer - TIMERMIN) * TIMERSTEP; +} + +static inline uint32_t UART_REG(int uart, uint32_t reg) +{ + return UARTBASE + reg + (uart - UARTMIN) * UARTSTEP; } /* Convenience types. */