Skip to content

Commit 6bffa78

Browse files
committed
pbio/drv/display_ev3: Use EDMA3 for data transfer.
With 178*128/3 bytes to send per display frame, this should reduce overhead considerably. Fixes pybricks/support#2152 This also sets things up for other peripherals that can use DMA, such as UART.
1 parent 191b715 commit 6bffa78

File tree

3 files changed

+254
-49
lines changed

3 files changed

+254
-49
lines changed

lib/pbio/drv/display/display_ev3.c

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
#include <pbio/error.h>
2222
#include <pbio/util.h>
2323

24+
#include <tiam1808/edma.h>
2425
#include <tiam1808/spi.h>
2526
#include <tiam1808/psc.h>
2627
#include <tiam1808/hw/soc_AM1808.h>
2728
#include <tiam1808/hw/hw_types.h>
29+
#include <tiam1808/hw/hw_edma3cc.h>
2830
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
31+
#include <tiam1808/armv5/am1808/edma_event.h>
2932
#include <tiam1808/armv5/am1808/interrupt.h>
3033

3134
#include "../drv/gpio/gpio_ev3.h"
@@ -338,50 +341,75 @@ static pbdrv_display_st7586s_action_t init_script[] = {
338341
{ ST7586S_ACTION_WRITE_COMMAND, ST7586_RAMWR},
339342
};
340343

341-
static uint32_t tx_size;
342-
static uint32_t tx_progress;
343-
static uint8_t *tx_data;
344-
345344
/**
346-
* SPI interrupt service routine for transmitting data.
345+
* Called on SPI1 DMA transfer completion.
346+
*
347+
* @param [in] status Status of the transfer.
347348
*/
348-
void SPIIsr(void) {
349-
uint32_t intCode = 0;
350-
IntSystemStatusClear(SYS_INT_SPINT1);
351-
352-
while ((intCode = SPIInterruptVectorGet(SOC_SPI_1_REGS))) {
353-
if (intCode != SPI_TX_BUF_EMPTY) {
354-
continue;
355-
}
356-
357-
SPITransmitData1(SOC_SPI_1_REGS, *tx_data++);
358-
359-
if (++tx_progress == tx_size) {
360-
SPIIntDisable(SOC_SPI_1_REGS, SPI_TRANSMIT_INT);
361-
spi_status = SPI_STATUS_COMPLETE;
362-
process_poll(&pbdrv_display_ev3_init_process);
363-
}
364-
}
349+
void pbdrv_display_ev3_spi1_tx_complete(uint32_t status) {
350+
SPIIntDisable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
351+
spi_status = SPI_STATUS_COMPLETE;
352+
process_poll(&pbdrv_display_ev3_init_process);
365353
}
366354

367355
/**
368-
* Begin writing data to the display.
356+
* Begin writing data to the display via DMA.
369357
*
370-
* @param data Data to write.
371-
* @param size Size of the data.
358+
* @param [in] data Data to write.
359+
* @param [in] size Size of the data.
372360
*/
373361
void pbdrv_display_st7586s_write_data_begin(uint8_t *data, uint32_t size) {
374-
tx_data = data;
375-
tx_size = size;
376-
tx_progress = 0;
377362
spi_status = SPI_STATUS_WAIT;
378363
pbdrv_gpio_out_low(&pin_lcd_cs);
379-
SPIEnable(SOC_SPI_1_REGS);
380-
SPIIntEnable(SOC_SPI_1_REGS, SPI_TRANSMIT_INT);
364+
365+
// Parameter object must be volatile since it is copied byte-by-byte in the
366+
// TI API, causing it to be optimized out.
367+
volatile EDMA3CCPaRAMEntry paramSet = {
368+
// Address of the data to be sent.
369+
.srcAddr = (uint32_t)data,
370+
// Address of the destination register.
371+
.destAddr = SOC_SPI_1_REGS + SPI_SPIDAT1,
372+
// Number of bytes in an array.
373+
.aCnt = 1,
374+
// Number of such arrays to be transferred.
375+
.bCnt = size,
376+
// Number of frames of aCnt*bBcnt bytes to be transferred.
377+
.cCnt = 1,
378+
// The srcBidx should be incremented by aCnt number of bytes since the
379+
// source used here is memory.
380+
.srcBIdx = 1,
381+
// A sync Transfer Mode is set in OPT. srCIdx and destCIdx set to
382+
// zero since ASYNC Mode is used.
383+
.srcCIdx = 0,
384+
// Linking transfers in EDMA3 are not used.
385+
.linkAddr = 0xFFFF,
386+
// bCntReload is not used.
387+
.bCntReload = 0,
388+
// Options for the transfer. SAM field in OPT is set to zero since
389+
// source is memory and memory pointer needs to be incremented. DAM
390+
// field in OPT is set to zero since destination is not a FIFO.
391+
.opt = (
392+
// Transfer completion code.
393+
((EDMA3_CHA_SPI1_TX << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC) |
394+
// EDMA3 Interrupt is enabled and Intermediate Interrupt Disabled.
395+
(1 << EDMA3CC_OPT_TCINTEN_SHIFT)
396+
),
397+
};
398+
399+
// Now write the PaRam Set to EDMA3.
400+
EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI1_TX, (EDMA3CCPaRAMEntry *)&paramSet);
401+
402+
// EDMA3 Transfer is Enabled.
403+
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI1_TX, EDMA3_TRIG_MODE_EVENT);
404+
405+
// Enable SPI controller to generate DMA events.
406+
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
381407
}
382408

383409
/**
384-
* Initialize the display SPI driver. Pinmux is already set up in platform.c.
410+
* Initialize the display SPI driver.
411+
*
412+
* Pinmux and common EDMA handlers are already set up in platform.c.
385413
*/
386414
void pbdrv_display_init(void) {
387415

@@ -395,11 +423,6 @@ void pbdrv_display_init(void) {
395423
// Waking up the SPI1 instance.
396424
PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_SPI1, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
397425

398-
// Register the ISR in the Interrupt Vector Table.
399-
IntRegister(SYS_INT_SPINT1, SPIIsr);
400-
IntChannelSet(SYS_INT_SPINT1, 2);
401-
IntSystemEnable(SYS_INT_SPINT1);
402-
403426
// Reset.
404427
SPIReset(SOC_SPI_1_REGS);
405428
SPIOutOfReset(SOC_SPI_1_REGS);
@@ -416,6 +439,13 @@ void pbdrv_display_init(void) {
416439
SPIDelayConfigure(SOC_SPI_1_REGS, 0, 0, 10, 10);
417440
SPIIntLevelSet(SOC_SPI_1_REGS, SPI_RECV_INTLVL | SPI_TRANSMIT_INTLVL);
418441

442+
// Request DMA Channel and TCC for SPI Transmit with queue number 0.
443+
EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, 0);
444+
445+
// Enable the SPI controller.
446+
SPIEnable(SOC_SPI_1_REGS);
447+
448+
// Start SPI process and ask pbdrv to wait until it is initialized.
419449
pbdrv_init_busy_up();
420450
process_start(&pbdrv_display_ev3_init_process);
421451
}

lib/pbio/drv/display/display_ev3.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2025 The Pybricks Authors
3+
4+
#ifndef _INTERNAL_PBDRV_DISPLAY_EV3_H_
5+
#define _INTERNAL_PBDRV_DISPLAY_EV3_H_
6+
7+
#include <stdint.h>
8+
9+
#include "pbdrvconfig.h"
10+
11+
void pbdrv_display_ev3_spi1_tx_complete(uint32_t status);
12+
13+
#endif // _INTERNAL_PBDRV_DISPLAY_EV3_H_

lib/pbio/platform/ev3/platform.c

Lines changed: 175 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,55 @@
33
//
44
// SPDX-License-Identifier: MPL-1.0
55
// Copyright (c) 2016 Tobias Schießl (System Init / partial pinmux / boot order / exceptionhandler)
6-
7-
#include <tiam1808/psc.h>
6+
//
7+
/*
8+
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
9+
*
10+
* Redistribution and use in source and binary forms, with or without
11+
* modification, are permitted provided that the following conditions
12+
* are met:
13+
*
14+
* Redistributions of source code must retain the above copyright
15+
* notice, this list of conditions and the following disclaimer.
16+
*
17+
* Redistributions in binary form must reproduce the above copyright
18+
* notice, this list of conditions and the following disclaimer in the
19+
* documentation and/or other materials provided with the
20+
* distribution.
21+
*
22+
* Neither the name of Texas Instruments Incorporated nor the names of
23+
* its contributors may be used to endorse or promote products derived
24+
* from this software without specific prior written permission.
25+
*
26+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37+
*/
38+
39+
#include <tiam1808/armv5/am1808/edma_event.h>
40+
#include <tiam1808/armv5/am1808/evmAM1808.h>
841
#include <tiam1808/armv5/am1808/interrupt.h>
9-
#include <tiam1808/hw/soc_AM1808.h>
10-
#include <tiam1808/hw/hw_types.h>
42+
#include <tiam1808/edma.h>
43+
#include <tiam1808/hw/hw_edma3cc.h>
1144
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
1245
#include <tiam1808/hw/hw_syscfg1_AM1808.h>
13-
#include <tiam1808/armv5/am1808/evmAM1808.h>
46+
#include <tiam1808/hw/hw_types.h>
47+
#include <tiam1808/hw/soc_AM1808.h>
48+
#include <tiam1808/psc.h>
1449

1550
#include <pbdrv/ioport.h>
1651
#include <pbio/port_interface.h>
1752

1853
#include "../../drv/button/button_gpio.h"
54+
#include "../../drv/display/display_ev3.h"
1955
#include "../../drv/gpio/gpio_ev3.h"
2056
#include "../../drv/led/led_array_pwm.h"
2157
#include "../../drv/led/led_dual.h"
@@ -361,6 +397,127 @@ void copy_vector_table(void) {
361397
}
362398
}
363399

400+
unsigned int EDMAVersionGet(void) {
401+
return 1;
402+
}
403+
404+
/**
405+
* Callback for completion of all EDMA3 transfers on this platform.
406+
*
407+
* @param tccNum [in] Transfer completion code number.
408+
* @param status [in] Status of the transfer. Currently only EDMA3_XFER_COMPLETE.
409+
*/
410+
static void Edma3CompleteCallback(unsigned int tccNum, unsigned int status) {
411+
if (tccNum == EDMA3_CHA_SPI1_TX) {
412+
pbdrv_display_ev3_spi1_tx_complete(status);
413+
}
414+
// Add other callbacks here as needed.
415+
}
416+
417+
/**
418+
* ISR for completion of all EDMA3 transfers on this platform.
419+
*
420+
* This is unchanged from the TI StarterWare example.
421+
*/
422+
static void Edma3ComplHandlerIsr(void) {
423+
volatile unsigned int pendingIrqs;
424+
volatile unsigned int isIPR = 0;
425+
426+
volatile unsigned int indexl;
427+
volatile unsigned int Cnt = 0;
428+
indexl = 1;
429+
IntSystemStatusClear(SYS_INT_CCINT0);
430+
isIPR = EDMA3GetIntrStatus(SOC_EDMA30CC_0_REGS);
431+
if (isIPR) {
432+
while ((Cnt < EDMA3CC_COMPL_HANDLER_RETRY_COUNT) && (indexl != 0)) {
433+
indexl = 0;
434+
pendingIrqs = EDMA3GetIntrStatus(SOC_EDMA30CC_0_REGS);
435+
while (pendingIrqs) {
436+
if ((pendingIrqs & 1) == TRUE) {
437+
// Here write to ICR to clear the corresponding IPR bits.
438+
EDMA3ClrIntr(SOC_EDMA30CC_0_REGS, indexl);
439+
Edma3CompleteCallback(indexl, EDMA3_XFER_COMPLETE);
440+
}
441+
++indexl;
442+
pendingIrqs >>= 1;
443+
}
444+
Cnt++;
445+
}
446+
}
447+
448+
}
449+
450+
/**
451+
* ISR for error handling of all EDMA3 transfers on this platform.
452+
*
453+
* This is unchanged from the TI StarterWare example.
454+
*/
455+
static void Edma3CCErrHandlerIsr(void) {
456+
volatile unsigned int pendingIrqs = 0;
457+
unsigned int Cnt = 0;
458+
unsigned int index = 1;
459+
unsigned int regionNum = 0;
460+
unsigned int evtqueNum = 0;
461+
462+
IntSystemStatusClear(SYS_INT_CCERRINT);
463+
464+
if ((HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR) != 0)
465+
|| (HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR) != 0)
466+
|| (HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR) != 0)) {
467+
// Loop for EDMA3CC_ERR_HANDLER_RETRY_COUNT number of time, breaks
468+
// when no pending interrupt is found.
469+
while ((Cnt < EDMA3CC_ERR_HANDLER_RETRY_COUNT) && (index != 0)) {
470+
index = 0;
471+
pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR);
472+
while (pendingIrqs) {
473+
// Process all the pending interrupts.
474+
if ((pendingIrqs & 1) == TRUE) {
475+
// Write to EMCR to clear the corresponding EMR bits.
476+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMCR) = (1 << index);
477+
// Clear any SER
478+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_SECR(regionNum)) = (1 << index);
479+
}
480+
++index;
481+
pendingIrqs >>= 1;
482+
}
483+
index = 0;
484+
pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR);
485+
while (pendingIrqs) {
486+
// Process all the pending interrupts.
487+
if ((pendingIrqs & 1) == TRUE) {
488+
// Here write to QEMCR to clear the corresponding QEMR bits.
489+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMCR) = (1 << index);
490+
// Clear any QSER
491+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_QSECR(0)) = (1 << index);
492+
}
493+
++index;
494+
pendingIrqs >>= 1;
495+
}
496+
index = 0;
497+
pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR);
498+
if (pendingIrqs != 0) {
499+
// Process all the pending CC error interrupts.
500+
// Queue threshold error for different event queues.
501+
for (evtqueNum = 0; evtqueNum < EDMA3_0_NUM_EVTQUE; evtqueNum++)
502+
{
503+
if ((pendingIrqs & (1 << evtqueNum)) != 0) {
504+
// Clear the error interrupt.
505+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = (1 << evtqueNum);
506+
}
507+
}
508+
509+
// Transfer completion code error.
510+
if ((pendingIrqs & (1 << EDMA3CC_CCERR_TCCERR_SHIFT)) != 0) {
511+
HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = \
512+
(0x01 << EDMA3CC_CCERR_TCCERR_SHIFT);
513+
}
514+
++index;
515+
}
516+
Cnt++;
517+
}
518+
}
519+
}
520+
364521
// Called from assembly code in startup.s. After this, the "main" function in
365522
// lib/pbio/sys/main.c is called. That contains all calls to the driver
366523
// initialization (low level in pbdrv, high level in pbio), and system level
@@ -371,21 +528,26 @@ void SystemInit(void) {
371528

372529
copy_vector_table();
373530

374-
/* Initialize AINTC */
531+
// Initialize advanced interrupt controller (AINTC)
375532
IntAINTCInit();
376-
377-
/* Enable IRQ for ARM (in CPSR)*/
378533
IntMasterIRQEnable();
379-
380-
/* Enable AINTC interrupts in GER */
381534
IntGlobalEnable();
382-
383-
/* Enable IRQ in AINTC */
384535
IntIRQEnable();
385-
386536
IntMasterFIQEnable();
387537
IntFIQEnable();
388538

539+
// Initialization of EDMA3
540+
PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_CC0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
541+
PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_TC0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
542+
EDMA3Init(SOC_EDMA30CC_0_REGS, 0); // Que num 0
543+
IntRegister(SYS_INT_CCINT0, Edma3ComplHandlerIsr);
544+
IntChannelSet(SYS_INT_CCINT0, 2);
545+
IntSystemEnable(SYS_INT_CCINT0);
546+
IntRegister(SYS_INT_CCERRINT, Edma3CCErrHandlerIsr);
547+
IntChannelSet(SYS_INT_CCERRINT, 2);
548+
IntSystemEnable(SYS_INT_CCERRINT);
549+
550+
389551
PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_GPIO, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
390552

391553
// Must set the power enable bin before disabling the pull up on the power

0 commit comments

Comments
 (0)