atmel-sam: Use DMA for ParallelImageCapture

This allows ctrl-c to interrupt the capture, and ensures we handle
interrupts & background tasks.
This commit is contained in:
Jeff Epler 2021-04-20 13:49:40 -05:00
parent d50feebd2c
commit 46c9b1bc37

View File

@ -38,6 +38,10 @@
#include "hal/include/hal_gpio.h"
#include "atmel_start_pins.h"
#include "audio_dma.h"
#include "samd/clocks.h"
#include "samd/events.h"
#define GPIO_PIN_FUNCTION_PCC (GPIO_PIN_FUNCTION_K)
#define PIN_PCC_D0 (PIN_PA16)
@ -136,15 +140,37 @@ bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallel
return self->data_count == 0;
}
static void setup_dma(DmacDescriptor* descriptor, size_t count, uint32_t *buffer) {
descriptor->BTCTRL.reg = DMAC_BTCTRL_VALID |
DMAC_BTCTRL_BLOCKACT_NOACT |
DMAC_BTCTRL_EVOSEL_BLOCK |
DMAC_BTCTRL_DSTINC |
DMAC_BTCTRL_BEATSIZE_WORD;
descriptor->BTCNT.reg = count;
descriptor->DSTADDR.reg = (uint32_t)buffer + 4*count;
descriptor->SRCADDR.reg = (uint32_t)&PCC->RHR.reg;
descriptor->DESCADDR.reg = 0;
}
#include <string.h>
void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize)
{
uint8_t dma_channel = audio_dma_allocate_channel();
uint32_t *dest = buffer;
size_t count = bufsize / 4; // PCC receives 4 bytes (2 pixels) at a time
const volatile uint32_t *vsync_reg = self->vertical_sync == NO_PIN ? NULL : &PORT->Group[(self->vertical_sync / 32)].IN.reg;
uint32_t vsync_bit = 1 << (self->vertical_sync % 32);
turn_on_event_system();
setup_dma(dma_descriptor(dma_channel), count, dest);
dma_configure(dma_channel, PCC_DMAC_ID_RX, true);
if (self->vertical_sync) {
const volatile uint32_t *vsync_reg = &PORT->Group[(self->vertical_sync / 32)].IN.reg;
uint32_t vsync_bit = 1 << (self->vertical_sync % 32);
if (vsync_reg) {
while (*vsync_reg & vsync_bit)
{
// Wait for VSYNC low (frame end)
@ -152,36 +178,21 @@ void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_paralleli
RUN_BACKGROUND_TASKS;
// Allow user to break out of a timeout with a KeyboardInterrupt.
if (mp_hal_is_interrupted()) {
audio_dma_free_channel(dma_channel);
return;
}
}
}
common_hal_mcu_disable_interrupts();
dma_enable_channel(dma_channel);
if (vsync_reg) {
size_t j = 1000000; // Don't freeze forever (this is ballpark 100ms timeout)
while (!(*vsync_reg & vsync_bit)) {
// Wait for VSYNC high (frame start)
if (!--j) {
common_hal_mcu_enable_interrupts();
mp_raise_RuntimeError(translate("Timeout waiting for VSYNC"));
}
while (DMAC->Channel[dma_channel].CHCTRLA.bit.ENABLE) {
RUN_BACKGROUND_TASKS;
if (mp_hal_is_interrupted()) {
break;
}
}
// TODO: use DMA
for (size_t i = 0; i<count; i++) {
size_t j = 1000000; // Don't freeze forever (this is ballpark 100ms timeout)
while (!PCC->ISR.bit.DRDY) {
if (!--j) {
common_hal_mcu_enable_interrupts();
mp_raise_RuntimeError(translate("Timeout waiting for DRDY"));
}
// Wait for PCC data ready
}
*dest++ = PCC->RHR.reg; // Store 2 pixels
}
common_hal_mcu_enable_interrupts();
dma_disable_channel(dma_channel);
audio_dma_free_channel(dma_channel);
}