Вопрос: Как создать недорогой логический анализатор с открытым исходным кодом менее чем за 15 долларов?
Ответ: объедините плату Freedom KL25Z с OLS !
В последнее время я много имею дело с DMA (см. « Учебное пособие: PWM с DMA на ARM / Kinetis »), поэтому я нашел время, чтобы реорганизовать код для лучшего использования DMA. В « Freedom Logic Analyzer с DMA » мне нужно было соединить PTD2 и PTD0, потому что это было маршрутизация часов для триггера DMA. На самом деле, в этом нет необходимости, и это можно сделать прямо из таймера / DMA :-).
Общая концепция такова:
- AtimeronTPM0 / Channel 1 (pinPTD1) генерирует сигнал ШИМ (50%), который запускает передачу DMA. Поскольку PTD1 также является синим светодиодом RGB, я легко могу проверить скорость передачи DMA с помощью другого логического анализатора:
- Частота TPM0 / Channel1 определяет частоту дискретизации DMA вOLS:
- В приложении я настраиваю DMA и настраиваю частоту дискретизации по мере необходимости.
- Клиент OLS / SUMP делает все остальное: запускает и настраивает частоту, затем передает результаты клиенту.
Компоненты
Чтобы упростить программное обеспечение, я использую компоненты Processor Expert. Компонент Init_TPM инициализирует мой ШИМ, который затем активирует DMA:
При этом у меня базовая частота 48 МГц, генерирующая ШИМ с максимальной частотой 24 МГц с включенным запросом DMA.
Внутри приложения я использую макросы PDD для изменения частоты:
static void TMR_SetTimerValue(uint32_t val) { TPM_PDD_WriteModuloReg(TPM0_BASE_PTR, val); /* set period of TPM0 */ TPM_PDD_WriteChannelValueReg(TPM0_BASE_PTR, 1, val/2); /* channel 1: PWM low 50% */ }
И он инициализируется с периодом 1 МГц и включенной передачей DMA для канала DMA 1
static void TMR_Init(void) { TMR_SetTimerValue(LOG_TMR_FREQ/1000000); /* default of 1 MHz */ TPM_PDD_EnableChannelDma(TPM0_BASE_PTR, 1); /* enable DMA for channel */ }
Сам DMA настроен с адресами источника и назначения как это:
static void InitDMA(void) { /* enable DMA MUX0: */ DMAMUX_PDD_EnableChannel(DMAMUX0_BASE_PTR, 0, PDD_ENABLE); /* enable DMA MUX0 */ /* PIT triggering for DMA0: */ DMAMUX_PDD_EnableTrigger(DMAMUX0_BASE_PTR, 0, PDD_DISABLE); /* disable PIT Trigger */ /* use TPM0 overflow for DMA0 request: */ DMAMUX_PDD_SetChannelSource(DMAMUX0_BASE_PTR, 0, 25); /* KL25Z reference manual, 3.4.8.1, p64: source number 25 TPM0 CH1 DMA source */ /* DMA channel 0 source configuration: */ DMA_PDD_SetSourceAddress(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, (uint32_t)&GPIOC_PDIR); /* set source address */ DMA_PDD_SetSourceAddressModulo(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, DMA_PDD_CIRCULAR_BUFFER_DISABLED); /* no circular buffer */ DMA_PDD_EnableSourceAddressIncrement(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, PDD_DISABLE); /* source address will be incremented by transfer size */ DMA_PDD_SetSourceDataTransferSize(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, DMA_PDD_8_BIT); /* Transfer size from source */ /* DMA channel 0 destination configuration: */ DMA_PDD_SetDestinationAddress(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, (uint32_t)&sampleBuffer[0]); /* set destination address */ DMA_PDD_SetDestinationAddressModulo(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, DMA_PDD_CIRCULAR_BUFFER_DISABLED); /* no circular buffer */ DMA_PDD_EnableDestinationAddressIncrement(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, PDD_ENABLE); /* auto-increment for destination address */ DMA_PDD_SetDestinationDataTransferSize(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, DMA_PDD_8_BIT); /* Transfer to destination size */ /* DMA channel 0 transfer configuration: */ DMA_PDD_EnableTransferCompleteInterrupt(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, PDD_ENABLE); /* request interrupt at the end of the DMA transfer */ (void)DMA_PDD_GetRequestAutoDisableEnabled(DMA_BASE_PTR, DMA_PDD_CHANNEL_0); /* disable DMA request at the end of the sequence */ }
В конце передачи DMA (с заполненным образцом буфера) он вызовет прерывание, где я установил флаг:
void LOGIC_OnComplete(void) { finishedSampling = TRUE; }
Последний этап — начать выборку сигналов, что довольно просто: установите адрес назначения и количество байтов для передачи, а затем перейдите:
static void TransferDMA(void) { DMA_PDD_SetDestinationAddress(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, (uint32_t)&sampleBuffer[0]); /* set destination address */ DMA_PDD_SetByteCount(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, bufferSize); /* set number of bytes to transfer */ DMA_PDD_EnablePeripheralRequest(DMA_BASE_PTR, DMA_PDD_CHANNEL_0, PDD_ENABLE); /* enable request from peripheral */ }
Все остальное сохраняется как есть, с незначительным рефакторингом. Дополнительным преимуществом является то, что количество исходных файлов уменьшено, и теперь все намного чище :-).
Резюме
Теперь у меня есть еще лучшее и более простое решение DMA для моего недорогого логического анализатора, которое хорошо работает с частотой дискретизации до 2 МГц. С 4 и до 24 МГц точность не очень хорошая, но до сих пор не ясно, что является причиной этого. Во всяком случае, это недорогой анализатор, и 2 МГц вполне прилично менее чем за 15 долларов :-). Единственное, что KL25Z имеет только 16 Кбайт оперативной памяти. Я рассматриваю возможность его портирования на FRDM-K64F, который имеет 256 Кбайт оперативной памяти и более быстрый процессор.
Проект был обновлен на GitHub. В проекте также есть файл S19, так что если у вас есть плата, вы можете просто запрограммировать плату с помощью загрузчика OpenSDA .
Счастливое нюхание ?