" В электронике все — это либо АЦП либо ЦАП.
Аудио кодеки, CAN, RS485, ethernet‑трансиверы.
Тот же GPIO можно рассматривать, как однобитный АЦП. "

Пролог

Как то раз мне принесли электронную плату без отладочного UART. Я привык отлаживать прошивки через UART Shell и это стало проблемой. Хотел было отлаживаться через Segger RTT, но отсутствие прав админа на РС не позволяли подключатся к сокету на локальном PC.

Зато на плате были просто GPIO пины на PLD вилке. И тут, то я понял, что надо запускать программный UART. Я стал писать программный UART. Пока я писал прием байта через программный UART задался вопросом.

Можно ли на STM32 реализовать однобитный ADC через DMA? Причем без использования аппаратного ADC. Надо сделать так, чтобы GPIO пин читался со строго определенным микросекундным периодом по команде аппаратного таймера и сохранял значения прочитанных логических уровней в массив uint8_t. При этом во время чтения N однобитных семплов должно произойти не более 2х аппаратных прерываний: половина чтения и полное чтение. Это необходимо, чтобы не нагружать основную программу в main. Как может выглядеть код такого однобитного ADC на основе HAL SDK от STm?

Как может работать однобитный ADC?
Мы включаем аппаратный таймер. Генерируем прерывания c частотой скажем 76800 Hz. В обработчике прерываний по таймеру считываем логический уровень на GPIO и помещаем его в RAM массив. В обработчике прерываний по таймеру можно также уменьшить счетчик уже принятых семплов для последующей автоматической приостановки аппаратного таймера. Здорово? Забудьте про это! Основная прошивка зависнет и процессор будет только лишь вызывать прерывания по переполнению аппаратного таймера для выхватывания семплов.

В связи с этим возникает задача.

Постановка задачи
Есть микроконтроллер STM32F407ZG. Есть GPIO пин PA1. Надо при возникновении на PA1 отрицательного перепада напряжения включить импровизированный однобитный ADC. То есть в режиме DMA записывать регистр GPIOx_IDR в массив типа uint16, расположенный в RAM памяти. Для определенности частота дискретизации выборки значений должна быть равна 76800 Hz (78,8 kHz). Надо управлять DMA каналом через master timer. Запись должна быть однократная. То есть после чтения массива DMA должен остановиться. Прерывание должно быть только одно: по окончанию заполнения массива. При написании кода использовать HAL SDK от STm.

Реализация
У микроконтроллера STM32 есть DMA. DMA умеет работать в режиме перемещения данных от периферии к RAM памяти. В качестве периферии будет выступать регистры GPIO портов.

Фаза 1: Настройка GPIO
Первым делом надо настроить GPIO.
1) Надо подать тактирование на GPIO. В моем случае это GPIOA
2) Определить на вход нужные нам пины. В моем случае это PA1.
3) Установить скорость пина на максимум.
4) Установить подтяжку напряжения. В моем случае Pull Up.
5) Отключить альтернативную функцию на GPIO пине. Установить значение ноль.
6) Подключить к пину внешние прерывания по отрицательному перепаду напряжения. GPIO_MODE_IT_FALLING

Надо сразу запомнить, что в STM32 значения пинов расположены именно в регистре GPIOx_IDR. Именно регистр GPIOx_IDR мы и будем опрашивать по DMA.

Фаза 2: Настройка аппаратного таймера
Аппаратный таймер для GPIO выступает в роли метронома для пианиста, чтобы не выбиваться из ритма. Именно аппаратный таймер дает команду на считывание логического уровня на GPIO.

Прежде всего надо понять какой номер аппаратного таймера мы выберем для тактирования DMA. Отталкиваться надо от таблицы каналов DMA (Table 42, Table 43). Мы можем выбрать таймеры с номерами 1, 2, 3, 4, 5, 6, 7, 8. Я решил выбрать TIM8.

1) Подать тактирование на аппаратный таймер.
2) Отключить прерывания по таймеру. Это важно. Иначе обработчики прерываний заблокируют всю остальную прошивку. Особенность однобитного ADC как раз в том, что частоты дискретизации могут быть весьма и весьма высокими.
3) Определить таймер в режим мастера
4) Установить счет в верх
5) Установить период таймера. В моем случае это 76.8 kHz.
6) Определить пределитель аппаратного таймера
7) Определить событие по которому аппаратный таймер подает сигнал. В моем случае надо ставить событие переполнения таймера (update event).
8) В регистре TIMx_DIER активировать бит 8 UDE: Update DMA request enable.

Регистры таймера 8 могут иметь вот такие значения

-->timer_diag_raw_reg 8
I,[Timer],Base:0x40010400,Cnt:20
+-----+-------------+-------+------------+------------+-------------------------------------------+
|  N  | Name        |offset |    Addr    |   ValHex   |                  ValBin                   |
+-----+-------------+-------+------------+------------+-------------------------------------------+
|   1 |    TIMx_CR1 | 0x000 | 0x40010400 | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 |
|   2 |    TIMx_CR2 | 0x004 | 0x40010404 | 0x00000020 | 0b0000_0000|0000_0000|0000_0000|0010_0000 |
|   3 |   TIMx_SMCR | 0x008 | 0x40010408 | 0x00000080 | 0b0000_0000|0000_0000|0000_0000|1000_0000 |
|   4 |   TIMx_DIER | 0x00c | 0x4001040c | 0x00000101 | 0b0000_0000|0000_0000|0000_0001|0000_0001 |
|   5 |     TIMx_SR | 0x010 | 0x40010410 | 0x0000001f | 0b0000_0000|0000_0000|0000_0000|0001_1111 |
|   6 |    TIMx_EGR | 0x014 | 0x40010414 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   7 |  TIMx_CCMR1 | 0x018 | 0x40010418 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   8 |  TIMx_CCMR2 | 0x01c | 0x4001041c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   9 |   TIMx_CCER | 0x020 | 0x40010420 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  10 |    TIMx_CNT | 0x024 | 0x40010424 | 0x0000019f | 0b0000_0000|0000_0000|0000_0001|1001_1111 |
|  11 |    TIMx_PSC | 0x028 | 0x40010428 | 0x00000003 | 0b0000_0000|0000_0000|0000_0000|0000_0011 |
|  12 |    TIMx_ARR | 0x02c | 0x4001042c | 0x00000221 | 0b0000_0000|0000_0000|0000_0010|0010_0001 |
|  13 |    TIMx_RCR | 0x030 | 0x40010430 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  14 |   TIMx_CCR1 | 0x034 | 0x40010434 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  15 |   TIMx_CCR2 | 0x038 | 0x40010438 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  16 |   TIMx_CCR3 | 0x03c | 0x4001043c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  17 |   TIMx_CCR4 | 0x040 | 0x40010440 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  18 |   TIMx_BDTR | 0x044 | 0x40010444 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  19 |    TIMx_DCR | 0x048 | 0x40010448 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|  20 |   TIMx_DMAR | 0x04c | 0x4001044c | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 |
+-----+-------------+-------+------------+------------+-------------------------------------------+

Таймер будет генерировать импульсы с точной частотой, запуская синхронные GPIO считывания.

Фаза 3: Настройка DMA канала для опроса GPIO

DMA — Сердце потоковой записи. Будет перекачивать (uint16_t) данные от GPIOA в RAM память.
1) Выбрать DMA канал, который можно триггерить событием по переполнению таймера 8. В нашем случае это DMA2 Stream1 Channel7.

Здесь стоит обратить внимание на одну важную особенность контроллеров STM32F4 — в контроллере есть два модуля DMA, и только контроллер DMA2 имеет доступ к периферийным модулям, находящимся на шине AHB1.

Модули GPIO соединены именно с шиной AHB1.

2) Подать тактирование на DMA2.
3) Указать, что данные берутся из регистра GPIOx->IDR (Input Data Register). Для порта А получается физический адрес 0x40020010.

4) Указать, что данные надо копировать в SRAM память. В массив 16-битных слов.
5) Определить смешение по 16 бит. Это как раз разрядность GPIO на STM32.
6) Определить направление передачи данных от периферии к памяти.
7) отключить увеличение адреса периферии.
8) Включить увеличение адреса памяти.
9) Определить количество семплов, которые мы будем считывать за один прогон.
10) Определить одиночный режим работы DMA. То есть запись с последующей остановкой DMA.
11) Активировать прерывани на событие окончания передачи по DMA (Transfer-Complete).
12) Активировать DMA прерывания в настройках NVIC.

Регистры DMA2 Stream1 могут иметь вот такой вид .

-->dma_channel_reg_map 2 1
W,[DmaChannel]DMA2_Stream1,CH_0,, Regs
I,[DmaChannel],Base:0x40026428,Cnt:6
+-----+--------------+-------+------------+------------+-------------------------------------------+
|  N  |  Name        |offset |    Addr    |   ValHex   |                  ValBin                   |
+-----+--------------+-------+------------+------------+-------------------------------------------+
|   1 |     DMA_SxCR | 0x000 | 0x40026428 | 0x0e032c06 | 0b0000_1110|0000_0011|0010_1100|0000_0110 |
|   2 |   DMA_SxNDTR | 0x004 | 0x4002642c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   3 |    DMA_SxPAR | 0x008 | 0x40026430 | 0x40020010 | 0b0100_0000|0000_0010|0000_0000|0001_0000 |
|   4 |   DMA_SxM0AR | 0x00c | 0x40026434 | 0x20004d8c | 0b0010_0000|0000_0000|0100_1101|1000_1100 |
|   5 |   DMA_SxM1AR | 0x010 | 0x40026438 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   6 |    DMA_SxFCR | 0x014 | 0x4002643c | 0x00000020 | 0b0000_0000|0000_0000|0000_0000|0010_0000 |
+-----+--------------+-------+------------+------------+-------------------------------------------+

Фаза 4: Настройка внешних прерываний (EXT INT).
Для старта нашего однобитного ADC надо как-то дать ему сигнал. Это можно произвести через внешнее прерывание.
1) Выбрать пин для внешнего прерывания. В моем случае это тот же самый пин, который надо опрашивать: PA1.
2) Определить событие перепада. В моем случае это отрицательный перепад.
3) Определить обработчик внешнего прерывания по отрицательному перепаду напряжения на пине.

Недостатки однобитного ADC
1) Захватываются лишние биты. Происходит перерасход памяти. Если Вас интересует конкретный пин, то придется потом выделять этот бит из массива прочитанных слов.

Достоинства однобитного ADC
1) Центральный процессор не нагружается от потокового считывания GPIO. Всё происходит аппаратно в фоновом режиме для программы в main.

Приложения однобитного ADC
1) Вы можете сделать программный UART без существенного проседания производительности прошивки. Есть целая гора плат, где по разным причинам на разъёмы не выведен аппаратный UART. Захватывать поток однобитных семплов, а затем анализировать его. Делать синтаксический разбор байтов.
2) Логический анализатор. Вы можете захватывать поток цифровых данных с 16тибитной шины данных. Следить за параллельными шинами адресов и данных.
3) Черные ящики. Блоки телеметрии. Эти логические уровни можно писать в SD карту и потом анализировать переменные.
4) Диктофоны, фонографы. Сейчас много MEMS микрофонов с однобитным PDM интерфейсом. Можно одним DMA и одним GPIO портом опрашивать до 16 PDM микрофонов! Записывать в файл, пропускать через DeltaSigma модулятор и получать PCM сигналы. Учитывая, что GPIO обычно много (4+), то можно к одному микроконтроллеру подключать десятки и десятки цифровых MEMS PDM микрофонов. Как Вам диктофон с 64-мя микрофонами?
5) Мультиметр. На основе прочитанного дампа нулей и единиц можно вычислить частоту сигнала и скважность.
7) Можно захватывать для анализа прочие программные проводные интерфейсы и протоколы: 1-wire, NEC, I2C, CAN, SWD, PS/2, MDIO, DDR, PDM и т. д.
8) Можно захватывать I/Q выходы цифровых радио front end микросхем.
9) Можно передавать данные между MCU и FPGA по параллельной шине на одной PCB.

Отладка
Что ж, попробуем проверить как работает этот однобитный ADC... Для экспериментов я взял широко известную и распространенную учебно-тренировочную электронную плату JL_32F4XX.

Отладка на PCB JL_32F4XX
Отладка на PCB JL_32F4XX

Джампером соединил выход UART2_TX на PA1. Активировал включение ADC по отрицательному перепаду на PA1. Отправил по UART2 байт 0x55. Затем отправил команду для печати прочитанных по DMA семплов.

то же только текстом

Скрытый текст
-->tr sw_uart_rx_55 ; bin_adc_show_sample
cmd_unit_test_run() argc 1
102.572,+10454,228,I,[TEST],key [sw_uart_rx_55]
102.572,+0,229,W,[TEST],KeyWord:sw_uart_rx_55,NoNum
102.573,+1,230,I,[TEST],test_sw_uart_rx_55()
********* test,OkTest,61/74,[Name:[sw_uart_rx_55],Fun:0x803aa65],FailCnt:0
102.875,+302,231,I,[TEST],TestDuration:303 ms=0.303 s=0.0050499998 min
102.875,+0,232,I,[TEST],Report
102.876,+1,233,I,[TEST],ok:1/74
102.877,+1,234,I,[TEST],failed:0
102.877,+0,235,I,[TEST],All 1 tests passed!
1:42-->
102.897,+20,236,I,[BinAdc],TIM8,FS:76800 Hz,Mem:0x20004d8c,MemSZ:104,N:1,InPad:[PA1],Dma:[DMA2_Stream1,CH_7,[GPIO_READ]],BIN_ADC1
102.897,+0,237,I,[BinAdc],start_cnt:6,busy_cnt:9,Spin:0,Init:On,
102.898,+1,238,I,[BinAdc],StartCnt:6
+-----+----------+--------+---------------------+
| Num |    TS    |  Sam   |         BIN         |
+-----+----------+--------+---------------------+
|   0 |    0.000 | 0x5e18 | 0101_1110_0001_1000 |
|   1 |  13.020u | 0x5c18 | 0101_1100_0001_1000 |
|   2 |  26.041u | 0x5e18 | 0101_1110_0001_1000 |
|   3 |  39.062u | 0x5e18 | 0101_1110_0001_1000 |
|   4 |  52.083u | 0x5c18 | 0101_1100_0001_1000 |
|   5 |  65.104u | 0x5c1e | 0101_1100_0001_1110 |
|   6 |  78.125u | 0x5e1e | 0101_1110_0001_1110 |
|   7 |  91.145u | 0x5c1e | 0101_1100_0001_1110 |
|   8 |   0.104m | 0x5c1e | 0101_1100_0001_1110 |
|   9 |   0.117m | 0x5e1e | 0101_1110_0001_1110 |
|  10 |   0.130m | 0x5c1e | 0101_1100_0001_1110 |
|  11 |   0.143m | 0x5c1e | 0101_1100_0001_1110 |
|  12 |   0.156m | 0x5e1e | 0101_1110_0001_1110 |
|  13 |   0.169m | 0x5e18 | 0101_1110_0001_1000 |
|  14 |   0.182m | 0x5e18 | 0101_1110_0001_1000 |
|  15 |   0.195m | 0x5e18 | 0101_1110_0001_1000 |
|  16 |   0.208m | 0x5e18 | 0101_1110_0001_1000 |
|  17 |   0.221m | 0x5e18 | 0101_1110_0001_1000 |
|  18 |   0.234m | 0x5e18 | 0101_1110_0001_1000 |
|  19 |   0.247m | 0x5e18 | 0101_1110_0001_1000 |
|  20 |   0.260m | 0x5e18 | 0101_1110_0001_1000 |
|  21 |   0.273m | 0x5e1e | 0101_1110_0001_1110 |
|  22 |   0.286m | 0x5e1e | 0101_1110_0001_1110 |
|  23 |   0.299m | 0x5e1e | 0101_1110_0001_1110 |
|  24 |   0.312m | 0x5e1e | 0101_1110_0001_1110 |
|  25 |   0.325m | 0x5e1e | 0101_1110_0001_1110 |
|  26 |   0.338m | 0x5c1e | 0101_1100_0001_1110 |
|  27 |   0.351m | 0x5e1e | 0101_1110_0001_1110 |
|  28 |   0.364m | 0x5e1e | 0101_1110_0001_1110 |
|  29 |   0.377m | 0x5c18 | 0101_1100_0001_1000 |
|  30 |   0.390m | 0x5e18 | 0101_1110_0001_1000 |
|  31 |   0.403m | 0x5e18 | 0101_1110_0001_1000 |
|  32 |   0.416m | 0x5c18 | 0101_1100_0001_1000 |
|  33 |   0.429m | 0x5e18 | 0101_1110_0001_1000 |
|  34 |   0.442m | 0x5e18 | 0101_1110_0001_1000 |
|  35 |   0.455m | 0x5e18 | 0101_1110_0001_1000 |
|  36 |   0.468m | 0x5e18 | 0101_1110_0001_1000 |
|  37 |   0.481m | 0x5e1e | 0101_1110_0001_1110 |
|  38 |   0.494m | 0x5c1e | 0101_1100_0001_1110 |
|  39 |   0.507m | 0x5c1e | 0101_1100_0001_1110 |
|  40 |   0.520m | 0x5e1e | 0101_1110_0001_1110 |
|  41 |   0.533m | 0x5e1e | 0101_1110_0001_1110 |
|  42 |   0.546m | 0x5e1e | 0101_1110_0001_1110 |
|  43 |   0.559m | 0x5e1e | 0101_1110_0001_1110 |
|  44 |   0.572m | 0x5c1e | 0101_1100_0001_1110 |
|  45 |   0.585m | 0x5c18 | 0101_1100_0001_1000 |
|  46 |   0.598m | 0x5e18 | 0101_1110_0001_1000 |
|  47 |   0.611m | 0x5e18 | 0101_1110_0001_1000 |
|  48 |   0.625m | 0x5c18 | 0101_1100_0001_1000 |
|  49 |   0.638m | 0x5e18 | 0101_1110_0001_1000 |
|  50 |   0.651m | 0x5e18 | 0101_1110_0001_1000 |
|  51 |   0.664m | 0x5c18 | 0101_1100_0001_1000 |
|  52 |   0.677m | 0x5e18 | 0101_1110_0001_1000 |
|  53 |   0.690m | 0x5c1e | 0101_1100_0001_1110 |
|  54 |   0.703m | 0x5c1e | 0101_1100_0001_1110 |
|  55 |   0.716m | 0x5e1e | 0101_1110_0001_1110 |
|  56 |   0.729m | 0x5e1e | 0101_1110_0001_1110 |
|  57 |   0.742m | 0x5c1e | 0101_1100_0001_1110 |
|  58 |   0.755m | 0x5e1e | 0101_1110_0001_1110 |
|  59 |   0.768m | 0x5e1e | 0101_1110_0001_1110 |
|  60 |   0.781m | 0x5c1e | 0101_1100_0001_1110 |
|  61 |   0.794m | 0x5e18 | 0101_1110_0001_1000 |
|  62 |   0.807m | 0x5e18 | 0101_1110_0001_1000 |
|  63 |   0.820m | 0x5e18 | 0101_1110_0001_1000 |
|  64 |   0.833m | 0x5e18 | 0101_1110_0001_1000 |
|  65 |   0.846m | 0x5c18 | 0101_1100_0001_1000 |
|  66 |   0.859m | 0x5e18 | 0101_1110_0001_1000 |
|  67 |   0.872m | 0x5e18 | 0101_1110_0001_1000 |
|  68 |   0.885m | 0x5e18 | 0101_1110_0001_1000 |
|  69 |   0.898m | 0x5e1e | 0101_1110_0001_1110 |
|  70 |   0.911m | 0x5e1e | 0101_1110_0001_1110 |
|  71 |   0.924m | 0x5c1e | 0101_1100_0001_1110 |
|  72 |   0.937m | 0x5e1e | 0101_1110_0001_1110 |
|  73 |   0.950m | 0x5e1e | 0101_1110_0001_1110 |
|  74 |   0.963m | 0x5c1e | 0101_1100_0001_1110 |
|  75 |   0.976m | 0x5e1e | 0101_1110_0001_1110 |
|  76 |   0.989m | 0x5e1e | 0101_1110_0001_1110 |
|  77 |   1.002m | 0x5c1e | 0101_1100_0001_1110 |
|  78 |   1.015m | 0x5c1e | 0101_1100_0001_1110 |
|  79 |   1.028m | 0x5e1e | 0101_1110_0001_1110 |
|  80 |   1.041m | 0x5c1e | 0101_1100_0001_1110 |
|  81 |   1.054m | 0x5e1e | 0101_1110_0001_1110 |
|  82 |   1.067m | 0x5e1e | 0101_1110_0001_1110 |
|  83 |   1.080m | 0x5e1e | 0101_1110_0001_1110 |
|  84 |   1.093m | 0x5e1e | 0101_1110_0001_1110 |
|  85 |   1.106m | 0x5e1e | 0101_1110_0001_1110 |
|  86 |   1.119m | 0x5e1e | 0101_1110_0001_1110 |
|  87 |   1.132m | 0x5c1e | 0101_1100_0001_1110 |
|  88 |   1.145m | 0x5e1e | 0101_1110_0001_1110 |
|  89 |   1.158m | 0x5e1e | 0101_1110_0001_1110 |
|  90 |   1.171m | 0x5e1e | 0101_1110_0001_1110 |
|  91 |   1.184m | 0x5e1e | 0101_1110_0001_1110 |
|  92 |   1.197m | 0x5c1e | 0101_1100_0001_1110 |
|  93 |   1.210m | 0x5e1e | 0101_1110_0001_1110 |
|  94 |   1.223m | 0x5e1e | 0101_1110_0001_1110 |
|  95 |   1.236m | 0x5e1e | 0101_1110_0001_1110 |
|  96 |   1.250m | 0x5e1e | 0101_1110_0001_1110 |
|  97 |   1.263m | 0x5e1e | 0101_1110_0001_1110 |
|  98 |   1.276m | 0x5c1e | 0101_1100_0001_1110 |
|  99 |   1.289m | 0x5e1e | 0101_1110_0001_1110 |
| 100 |   1.302m | 0x5e1e | 0101_1110_0001_1110 |
| 101 |   1.315m | 0x5e1e | 0101_1110_0001_1110 |
| 102 |   1.328m | 0x5c1e | 0101_1100_0001_1110 |
| 103 |   1.341m | 0x5e1e | 0101_1110_0001_1110 |
+-----+----------+--------+---------------------+
1:43-->

Как можно заметить (по первому биту в колонке BIN), ADC прочитал UART сигнал 0_10101010_11. Это UART кадр. Стартовый бит, а потом байт 0x55 младшим битом вперед. Это явственно доказывает, что наш однобитный ADC в самом деле работает!

Итоги
Удалось научиться запускать однобитный ADC на основе сцепки GPIO, TIMER и DMA. Это позволяет разгрузить ядро микроконтроллера от рутинного сбора данных.

Такова специфика программирования микроконтроллеров. Собираешься делать одно (программный UART), а по ходу работы выясняется, что надо ещё целая куча всего другого (тот же однобитный ADC, внешние прерывания, DMA, таймеры и прочее).

Данная технология открывает дорогу для создания многочисленных sensitivity-приложений на базе микроконтроллера, начиная с логических анализаторов, программных UART-ов, заканчивая многоканальными цифровыми фонографами.

Словарь

Акроним

Расшифровка

GPIO

General-Purpose Input/Output

ADC

Analog-to-digital converter

STM32

ST Microelectronics 32-bit

UART

Universal Asynchronous Receiver-Transmitter

DMA

Direct memory access

RAM

Random Access Memory

Источники

Название

URL

Потоковая запись ADC семплов на STM32

https://habr.com/ru/articles/1025090/

Обзор отладочной платы JL_32F4XX с STM32F4XX

https://stm32-base.org/boards/STM32F407ZGT6-STM32F4XX.html

Библиотека SoftwareSerial

https://lesson.iarduino.ru/learn/built-in-libraries/software-serial?ysclid=mu87uhki3o537063624

Захват видеосигнала от ZX Spectrum при помощи STM32F4.

@iliasam

http://we.easyelectronics.ru/citizen/zahvat-videosignala-ot-zx-spectrum-pri-pomoschi-stm32f4.html

Demo прошивка однобитного ADC

https://github.com/aabzel/Artifacts/tree/main/jl_32f4xx_bin_adc_gcc_m

“Сигма дельта” или как сделать хорошую звуковую карту из STM32F401 @Sdima1357

https://habr.com/ru/articles/662141/

AN4666 Application note
Parallel synchronous transmission using GPIO and DMA

https://www.st.com/resource/en/application_note/an4666-parallel-synchronous-transmission-using-gpio-and-dma-stmicroelectronics.pdf

Вопросы:
1) Почему разработчики микроконтроллеров не делают режим DMA peripheral to peripheral?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы настраивали однобитный ADC из GPIO, TIMER и DMA?
23.08%да3
76.92%нет10
Проголосовали 13 пользователей. Воздержавшихся нет.