Embedded HandbookПриложения
Все разделы › III. Периферия
STM32F103 · уровень средний · 30 мин чтения
Открыть в интерактивном справочнике

13в. 16-канальный ШИМ PCA9685 по I2C: сервоприводы и светодиоды без таймеров микроконтроллера

В разделе 13а таймер TIM2 вёл четыре сервопривода, а пятому нужен уже второй таймер (13а.3). В 13б таймер играл ноты. Когда приводов или светодиодов десятки, а таймеры заняты, ШИМ выносят во внешнюю микросхему. PCA9685 — самая ходовая: 16 каналов по 12 бит, свой генератор и I2C, то есть два провода на все каналы. Здесь — что у неё внутри, как подключить её к F103 и разбудить, как задать частоту и почему генератор надо проверять, как перевести импульс привода в отсчёты, как сделать яркость светодиода ровной на глаз и как обновлять все 16 каналов разом.

Опоры: RM0008 (DocID13902 Rev 17): после сброса выводы — вход без подтяжки, с. 160; I2C — до 100 кГц в стандартном режиме и до 400 кГц в быстром, с. 752; приём одного байта ведущим, с. 764; I2C_CCR, с. 783–784; I2C_TRISE, с. 784. DocID13587 Rev 17 (STM32F103x8/xB): PA8 с допуском 5 В, с. 31; PB6 — I2C1_SCL, PB7 — I2C1_SDA, оба FT, с. 32; FT — допуск 5 В, с. 33. Документа на PCA9685 в проекте нет: регистры, биты, значения после включения, адреса, поведение SLEEP, RESTART и ALLCALL взяты из документации производителя и заложены в модель. Адреса регистров и биты MODE1 и MODE2 одинаковы в трёх открытых драйверах на диске — Zephyr 4.4.2, rusEFI и Klipper, но это не документ. Поэтому всё ниже проверяет согласие драйвера с моделью, а не с микросхемой. Все числа — прогон драйвера _verify/pca13d/pca13d.c на компьютере (gcc 12.2.0) со своей моделью PCA9685 как ведомого I2C на Python 3.11; прошивка собрана armclang 6.24 под Cortex-M3.

Как это называется в документации

Оригинал Перевод
LED controller драйвер светодиодов — так PCA9685 называет производитель
prescaler (PRE_SCALE) предделитель: делит генератор до частоты ШИМ
auto-increment (AI) автоинкремент: номер регистра растёт после каждого байта
LEDn_ON, LEDn_OFF отсчёт, на котором канал n включается и выключается
full ON / full OFF бит 12: канал всегда включён / всегда выключен
totem pole / open-drain (OUTDRV) двухтактный выход / открытый сток
output invert (INVRT) инверсия выходов
ALL CALL address общий адрес: на него отвечают все PCA9685 на шине
output enable (OE) разрешение выходов, активен нулём
sleep, restart сон — генератор выключен; перезапуск каналов после сна
phase shift сдвиг фазы: импульсы каналов начинаются не в один момент

13в.1 Микросхема: 16 каналов, 12 бит, свой генератор

По документации производителя внутри PCA9685 — генератор на 25 МГц, предделитель и один 12-битный счётчик на все каналы: он идёт от 0 до 4095 и начинает сначала. У каждого из 16 каналов два числа: ON — на каком отсчёте выход включить, OFF — на каком выключить. Если OFF меньше ON, импульс идёт через конец периода. Число 12-битное, а тринадцатый бит (бит 4 старшего байта) — «всегда включён» в ON и «всегда выключен» в OFF.

Отсюда главное отличие от таймера F103: частота одна на все 16 каналов, её задаёт PRE_SCALE. Приводам нужно 50 Гц, светодиодам — сотни герц, чтобы не мигали. Приводы и светодиоды на одной микросхеме будут на одной частоте; если это мешает — вторая микросхема с другим адресом.

Регистр Номер Что в нём
MODE1 0x00 RESTART (бит 7), AI (бит 5), SLEEP (бит 4), ALLCALL (бит 0)
MODE2 0x01 INVRT (бит 4), OCH (бит 3), OUTDRV (бит 2)
LEDn_ON_L … LEDn_OFF_H 0x06 + 4n ON и OFF канала n, младший байт первым
ALL_LED_ON_L … 0xFA…0xFD запись сразу во все 16 каналов
PRE_SCALE 0xFE предделитель, от 3 до 255

После включения в модели — по документации производителя — MODE1 = 0x11: микросхема спит, общий адрес включён, автоинкремента нет. MODE2 = 0x04 — двухтактные выходы. PRE_SCALE = 0x1E — это 196,89 Гц, у каждого канала OFF_H = 0x10 — «всегда выключен». ALLCALLADR = 0xE0 — общий адрес 0x70, сдвинутый на бит.

Вверху — подключение PCA9685 к STM32F103: PB6 — SCL, PB7 — SDA на I2C1 на 400 кГц, перемычки A5…A0 на GND задают адрес 0x40, OE на GND; логика микросхемы питается от 3,3 В платы, шестнадцать выходов LED0…LED15 идут к приводам, а плюс приводов V+ 5 В даёт свой источник при общей земле; выход LED15 перемычкой заведён на PA8 с допуском 5 В, по нему прошивка один раз меряет период для калибровки. Внизу — импульс 1500 мкс на четырёх каналах при 50 Гц: PRE_SCALE 121, частота 50,029 Гц, отсчёт 4,88 мкс, импульс 307 отсчётов; у LED0 ON 0 и OFF 307, у LED1 ON 256 и OFF 563, у LED2 ON 512 и OFF 819, у LED15 ON 3840 и OFF 51 — импульс идёт через конец периода; фронты каналов не совпадают, а приводу важна только ширина


13в.2 Подключение к F103 и инициализация

Вывод PCA9685 STM32F103 и прочее Назначение
SCL PB6 такт I2C1
SDA PB7 данные I2C1
VDD, GND 3,3 В и земля питание логики
V+ отдельный источник 5 В плюс приводов, к логике не идёт
A5…A0 GND адрес 0x40
OE GND выходы разрешены
LED15 PA8 через перемычку замер периода для калибровки (13в.3)

PB6 и PB7 — выводы I2C1, оба FT, то есть терпят 5 В (DocID13587, с. 32): плата, у которой подтяжки к 5 В, их не сожжёт. Но проще питать логику PCA9685 от 3,3 В — тогда и выходы каналов 3,3 В. Приводы берут ток рывками, и их питание V+ с логикой не смешивают: отдельный источник, общая земля, конденсатор у разъёма — как в 13а.6.

Адрес по документации производителя — 0x40 плюс код на перемычках A5…A0. В модели перемычки 000000 дали 0x40 (байт в линии 0x80, 17.2), 000001 — 0x41, 111111 — 0x7F; по чужому адресу pca_init вернула отказ PCA_E_NACK. I2C1 — как в 15б.2: 400 кГц, CCR = F/S | 30, TRISE = 11; больше F103 не умеет — до 400 кГц в быстром режиме (RM0008, с. 752).

Инициализация — частота, режим выходов и выключенный общий адрес:

int pca_init(pca_t *d, uint8_t addr7, uint32_t freq_hz, uint8_t mode2)
{
    int rc;
    d->addr = addr7;
    d->xfers = d->errors = 0;
    if (d->osc_hz == 0u)
        d->osc_hz = PCA_OSC_HZ;
    if ((rc = pca_reg(d, PCA_MODE2, mode2)) != PCA_OK)     /* заодно: отвечает ли кто-то по адресу */
        return rc;
    return pca_set_freq(d, freq_hz);
}

Частоту задаёт pca_set_freq, и порядок в ней обязательный (по документации производителя): PRE_SCALE пишется только во сне, после пробуждения генератору нужно 500 мкс, а если каналы работали до сна, их перезапускает запись 1 в RESTART:

/* PRE_SCALE пишется только во сне; после пробуждения — 500 мкс генератору и RESTART, если он взведён */
int pca_set_freq(pca_t *d, uint32_t freq_hz)
{
    uint8_t m, pre = pca_prescale(d->osc_hz, freq_hz);
    int rc;
    if ((rc = pca_get(d, PCA_MODE1, &m)) != PCA_OK)
        return rc;
    m &= (uint8_t)~(PCA_RESTART | PCA_SLEEP | PCA_ALLCALL);     /* общий адрес 0x70 — выключить */
    if ((rc = pca_reg(d, PCA_MODE1, m | PCA_AI | PCA_SLEEP)) != PCA_OK)
        return rc;
    if ((rc = pca_reg(d, PCA_PRE_SCALE, pre)) != PCA_OK)
        return rc;
    if ((rc = pca_reg(d, PCA_MODE1, m | PCA_AI)) != PCA_OK)    /* проснуться */
        return rc;
    pca_port_delay_us(PCA_WAKE_US);
    if ((rc = pca_get(d, PCA_MODE1, &m)) != PCA_OK)
        return rc;
    if (m & PCA_RESTART)                                /* каналы работали до сна: запустить их снова */
        rc = pca_reg(d, PCA_MODE1, m);
    if (rc == PCA_OK)
        d->prescale = pre;
    return rc;
}

В модели pca_init(&pca, 0x40, 50, PCA_OUTDRV) после включения — 6 обменов: 80 01 04 (MODE2), чтение MODE1, 80 00 30 (сон и автоинкремент), 80 FE 79 (PRE_SCALE = 121), 80 00 20 (проснуться), чтение MODE1. На шине 485 мкс, вместе с паузой — 985 мкс. После этого MODE1 = 0x20, частота 50,029 Гц. Чтение одного байта на F103 идёт по особому порядку (RM0008, с. 764): снять ACK до сброса ADDR, потом STOP, потом ждать RXNE — так сделан pca_port_read в прошивке.


13в.3 Частота ШИМ: предделитель и генератор

Период — 4096 отсчётов, отсчёт — (PRE_SCALE + 1) тактов генератора. Отсюда предделитель по документации производителя: PRE_SCALE = round(osc / (4096 × f)) − 1, в пределах от 3 до 255.

/* PRE_SCALE = round(osc / (4096 × f)) − 1, в пределах 3…255 */
uint8_t pca_prescale(uint32_t osc_hz, uint32_t freq_hz)
{
    uint64_t div = (uint64_t)PCA_STEPS * (freq_hz ? freq_hz : 1u);
    uint64_t p = ((uint64_t)osc_hz + div / 2u) / div;   /* с округлением */
    if (p < PCA_PRE_MIN + 1u)
        p = PCA_PRE_MIN + 1u;
    if (p > PCA_PRE_MAX + 1u)
        p = PCA_PRE_MAX + 1u;
    return (uint8_t)(p - 1u);
}

Делитель целый, поэтому частота выходит не та, что просили. В модели при 25 МГц:

Просили PRE_SCALE Вышло Ошибка Без «− 1»
24 Гц 253 24,030 Гц +0,123 % 23,935 Гц
50 Гц 121 50,029 Гц +0,058 % 49,622 Гц
200 Гц 30 196,888 Гц −1,556 % 190,735 Гц
1000 Гц 5 1017,253 Гц +1,725 % 871,931 Гц
1526 Гц 3 1525,879 Гц −0,008 % 1220,703 Гц

Пределы — 23,842 Гц при 255 и 1525,879 Гц при 3: просьба о 10 Гц дала 255, о 2000 Гц — 3. На высоких частотах шаг делителя грубый: 1000 Гц не получить точнее 1,7 %. Для привода это неважно — ему нужна ширина импульса, а не частота (13а.1), — но ширину драйвер считает по фактическому PRE_SCALE, а не по заказанной частоте.

Генератор. Внутренний генератор работает без кварца, и насколько его частота отличается от 25 МГц у конкретной микросхемы, при какой температуре и питании, — здесь не проверено: документа нет. Модель берёт отклонение 3 % в обе стороны (допущение). Ошибка генератора входит в ширину импульса один к одному: драйвер считает отсчёты для 25 МГц, а они короче или длиннее.

Лечится замером. Выход LED15 с заполнением 50 % заведён на PA8, и прошивка один раз меряет восемь периодов счётчиком тактов DWT. PA8 после сброса — вход без подтяжки (RM0008, с. 160) и терпит 5 В (DocID13587, с. 31):

/* период выхода LED15 в наносекундах: восемь периодов по счётчику тактов DWT */
static uint32_t period_ns(void)
{
    uint32_t a, b = 0;
    if (!edge(&a))
        return 0;
    for (int i = 0; i < 8; i++)
        if (!edge(&b))
            return 0;
    return (uint32_t)((uint64_t)(b - a) * 1000u / (72u * 8u));
}

Из периода — настоящая частота генератора, и PRE_SCALE заново:

/* по измеренному периоду выхода: osc = 4096 × (PRE_SCALE + 1) / T */
uint32_t pca_calibrate(pca_t *d, uint32_t period_ns)
{
    uint64_t n = (uint64_t)PCA_STEPS * (d->prescale + 1u) * 1000000000u;
    if (period_ns)
        d->osc_hz = (uint32_t)((n + period_ns / 2u) / period_ns);
    return d->osc_hz;
}
Генератор в модели До калибровки: частота 1500 мкс стали Угол Период После: PRE_SCALE Частота Импульс Угол
25,75 МГц (+3 %) 51,530 Гц 1454,5 мкс 81,8° 19 406 291 нс 125 49,894 Гц 1502,2 мкс 90,4°
24,25 МГц (−3 %) 48,528 Гц 1544,5 мкс 98,0° 20 606 680 нс 117 50,173 Гц 1498,7 мкс 89,8°

Угол — по модели привода из 13а.1: 1000 мкс — 0°, 2000 мкс — 180°. До калибровки привод промахивается на 8°, после — меньше чем на полградуса; частоту генератора замер дал до герца: 25 750 000 и 24 250 000 Гц. Остаток — шаг делителя и шаг отсчёта, а не генератор.


13в.4 Сервоприводы: импульс в отсчётах и разнесение фаз

При 50 Гц и PRE_SCALE = 121 отсчёт — 122 / 25 МГц = 4,88 мкс. Драйвер переводит микросекунды в отсчёты с округлением, по той частоте генератора, которой верит:

/* микросекунды → отсчёты при текущих PRE_SCALE и частоте генератора, с округлением */
uint16_t pca_us_ticks(const pca_t *d, uint32_t us)
{
    uint64_t div = 1000000ull * (d->prescale + 1u);
    uint64_t t = ((uint64_t)us * d->osc_hz + div / 2u) / div;
    return (uint16_t)(t > PCA_STEPS - 1u ? PCA_STEPS - 1u : t);
}
Импульс 500 мкс 1000 мкс 1500 мкс 2000 мкс 2500 мкс
Отсчётов 102 205 307 410 512
На выходе в модели 497,76 мкс 1000,4 мкс 1498,16 мкс 2000,8 мкс 2498,56 мкс

На ход 1000…2000 мкс приходится 205 отсчётов — 0,878° на отсчёт, на широкий ход 500…2500 мкс — 410 отсчётов, 0,439°. У TIM2 в 13а.2 шаг был 1 мкс, 0,18°: PCA9685 грубее впятеро, но мёртвая зона модели привода — 1° (13а.1), и шага 0,878° ей хватает.

Разнесение фаз. Если у всех каналов ON = 0, все 16 выходов включаются на одном отсчёте. Приводу всё равно, где начинается импульс, — важна ширина. Поэтому драйвер сдвигает начало на 256 отсчётов на канал, и фронты расходятся по периоду:

/* заполнение 0…4096 → ON и OFF; spread — сдвиг начала на 256 отсчётов на канал */
void pca_duty(uint32_t ch, uint32_t duty, int spread, uint16_t *on, uint16_t *off)
{
    if (duty == 0u) {
        *on = 0;
        *off = PCA_FULL;                                /* всегда выключен */
    } else if (duty >= PCA_STEPS) {
        *on = PCA_FULL;                                 /* всегда включён */
        *off = 0;
    } else {
        *on = (uint16_t)(spread ? (ch % PCA_CHANNELS) * (PCA_STEPS / PCA_CHANNELS) : 0u);
        *off = (uint16_t)((*on + duty) % PCA_STEPS);    /* OFF < ON — импульс через конец периода */
    }
}

Канал 3 при 1500 мкс — ON = 768, OFF = 1075; канал 15 при 2000 мкс — ON = 3840, OFF = 154, импульс через конец периода, и в модели он всё равно 2000,8 мкс. Что даёт сдвиг, модель считает по форме всех 16 выходов; ток — допущение: 20 мА на выход, пока он включён, как у светодиода. Сколько берёт сигнальный вход привода, не проверено.

16 каналов Без сдвига: включено разом фронтов на одном отсчёте пик тока Со сдвигом: включено разом фронтов пик тока Средний ток
привод, 1500 мкс 16 16 320 мА 2 1 40 мА 24 мА
заполнение 25 % 16 16 320 мА 4 2 80 мА 80 мА
заполнение 50 % 16 16 320 мА 8 2 160 мА 160 мА

Средний ток один и тот же, а пик со сдвигом меньше в 2–8 раз, и вместо 16 фронтов разом — один-два. Для приводов главный ток — двигатели на V+, их рывки сдвиг не убирает: тут помогают отдельный источник и пуск по очереди (13а.6).


13в.5 Светодиоды: яркость, гамма, полное вкл/выкл, выходы

Яркость — заполнение от 0 до 4096. Глаз видит яркость не линейно: половина заполнения кажется много ярче половины. Поэтому уровень пересчитывают по гамме — здесь 2,2, это допущение о восприятии, а не данные документа. В драйвере таблица на 17 точек и линейная вставка между ними:

/* уровень 0…1024 → заполнение 0…4096 по гамме 2,2: таблица и линейная вставка между точками */
uint16_t pca_gamma(uint32_t level)
{
    if (level >= 1024u)
        return PCA_STEPS;
    uint32_t i = level >> 6, f = level & 63u;
    uint32_t a = gamma22[i], b = gamma22[i + 1u];
    return (uint16_t)(a + ((b - a) * f + 32u) / 64u);
}
Уровень 0 16 32 64 128 256 512 768 1000 1024
Заполнение 0 2 5 9 42 194 891 2175 3893 4096

От точной кривой 4096 × (уровень / 1024)^2,2 вставка отходит не больше чем на 5,8 отсчёта. Первый светящийся уровень — 4-й; из 1025 уровней различных заполнений 936. Вот где нужны 12 бит: если то же заполнение округлить до 8 бит, первые 53 уровня — полная темнота, свет появляется только с 54-го.

Полное вкл и выкл. Обычным импульсом 100 % не получить: ON = 0, OFF = 4095 — выход включён 0,999756 периода, на один отсчёт меньше. Поэтому pca_duty для 4096 ставит бит 12 в ON («всегда включён»), а для 0 — бит 12 в OFF. Если стоят оба, по документации производителя главнее «выключен» — в модели выход 0.

Двухтактный выход или открытый сток. OUTDRV в MODE2: 1 — выход сам даёт и 0, и 1, 0 — открытый сток: он только тянет к земле, а вместо единицы отпускает вывод. INVRT переворачивает выход. Светодиод бывает подключён от вывода на землю (горит при 1) или от плюса к выводу (горит при 0). В модели при заполнении 25 %:

MODE2 Выход Светодиод на землю Светодиод от плюса
0x04 двухтактный 0,25 0,75
0x14 двухтактный, INVRT 0,75 0,25
0x00 открытый сток 0 0,75
0x10 открытый сток, INVRT 0 0,25

Открытый сток светодиод на землю не зажжёт вовсе. Светодиод от плюса — это «наоборот»: без INVRT 25 % дают 75 % света. Его включают с INVRT — или считают заполнение как 4096 − яркость. Ток выхода и резистор — по документации производителя на микросхему и светодиод, здесь не считались.


13в.6 Все 16 каналов одной транзакцией

С автоинкрементом номер регистра растёт после каждого байта, и 16 каналов — это одна запись: номер 0x06 и 64 байта.

/* все 16 каналов одной транзакцией: 1 + 64 байта, выходы меняются вместе по STOP */
int pca_write_all(pca_t *d, const uint16_t on[16], const uint16_t off[16])
{
    uint8_t b[1 + 4 * PCA_CHANNELS];
    b[0] = PCA_LED0_ON_L;
    for (uint32_t i = 0; i < PCA_CHANNELS; i++) {
        b[1 + 4 * i] = (uint8_t)on[i];
        b[2 + 4 * i] = (uint8_t)(on[i] >> 8);
        b[3 + 4 * i] = (uint8_t)off[i];
        b[4 + 4 * i] = (uint8_t)(off[i] >> 8);
    }
    return xfer_done(d, pca_port_write(d->addr, b, sizeof b));
}

По документации производителя выходы берут новые значения по STOP (или по ACK каждого байта, если OCH = 1). Значит, одна транзакция — все 16 каналов меняются разом. Время на шине в модели:

Запись Тактов SCL 100 кГц 400 кГц Доля кадра 20 мс, 100 / 400 кГц
16 каналов одной транзакцией 596 5960 мкс 1490 мкс 29,8 % / 7,45 %
16 каналов по одному 16 × 56 8960 мкс 2240 мкс 44,8 % / 11,2 %
один канал 56 560 мкс 140 мкс
ALL_LED — все разом одним значением 56 560 мкс 140 мкс
чтение одного регистра 39 390 мкс 97,5 мкс

Синхронность. Каналы, записанные по одному, получают новые значения в разные моменты, и часть из них — уже в следующем периоде. В модели новое значение действует со следующего периода после STOP (допущение); 400 кадров со случайной фазой счётчика:

Как писали 100 кГц 400 кГц
одной транзакцией 0 из 400 0 из 400
16 транзакциями 159 из 400 (39,8 %) 48 из 400 (12 %)

В двух кадрах из пяти на 100 кГц часть приводов сдвинулась на период позже остальных. Одна транзакция этого не допускает.

Плавное движение — как в 13а.4, только шаг делает не прерывание таймера, а кадр раз в 20 мс: прошивка считает 16 импульсов и пишет их одной транзакцией. Ход 1000 → 2000 мкс по 20 мкс за кадр — 50 кадров, 1 с; отсчёты идут от 209 до 410 шагами по 4–5 (20 мкс — это 4,1 отсчёта). На 400 кГц кадр занимает шину на 1490 мкс — 7,45 % времени, и на той же шине остаётся место для датчиков.


13в.7 Грабли

PRE_SCALE «не записался». Записали его, не усыпив микросхему, — по документации производителя запись пропадает. В модели PRE_SCALE остался 30 (0x1E, после включения), период — 5079 мкс вместо 20 мс, а драйвер, уверенный в 121, дал 307 отсчётов — импульс 380,7 мкс вместо 1500. Привод упирается в край. Только pca_set_freq: сон, PRE_SCALE, пробуждение.

Записали канал — выход молчит. Нет AI: после включения автоинкремент выключен, и все четыре байта ложатся в один регистр LED0_ON_L. В модели после записи 1500 мкс регистры канала — 01 00 00 10: бит «всегда выключен» на месте, импульсов за 100 мс — 0. pca_set_freq ставит AI в MODE1.

Чужое устройство на 0x70 переписывает PCA9685. После включения ALLCALL = 1, и микросхема отвечает на 0x70. В модели запись двух байт 00 01 устройству с адресом 0x70 сменила MODE1 у PCA9685 с 0x11 на 0x01 — она проснулась. После pca_init (ALLCALL снят) та же запись MODE1 = 0x20 не тронула. Два ответа на один адрес — ещё и спор на линии при чтении.

После сна каналы стоят. Уснули при работающих каналах — взводится RESTART. В модели привод выдавал 5 импульсов за 100 мс; после сна MODE1 = 0xB0, после пробуждения — 0xA0 и 0 импульсов. Запись 1 в RESTART из pca_set_freq вернула 0x20 и 5 импульсов. Запись 0 в RESTART ничего не меняет — его снимает только единица, и только через 500 мкс после пробуждения.

Забыли «− 1». PRE_SCALE = 122 вместо 121 — 49,622 Гц, а импульс, посчитанный для 121, вышел 1510,4 мкс вместо 1500. Мелочь для привода, но на 1000 Гц то же «− 1» даёт 871,931 Гц вместо 1017,253 (13в.3).

Контроллер сбрасывается, когда едут приводы. V+ взят от тех же 5 В или 3,3 В, что логика. Отдельный источник для V+, общая земля, конденсатор у разъёма (13а.6).

5 В и подтяжки. Плата PCA9685 от 5 В с подтяжками к 5 В: PB6 и PB7 — FT (с. 32), PA8 — тоже (с. 31), а PA0…PA3 — нет (13а.6), выход канала на них заводить нельзя. Проще всего — логика от 3,3 В.

Светодиод горит наоборот. Подключён от плюса к выводу, а INVRT нет: 25 % дают 75 % света (13в.5).


13в.8 Итоги

Полные исходники

pca13d.h — регистры, биты MODE1 и MODE2, порт и функции драйвера.

/* pca13d.h — раздел 13в: драйвер PCA9685 (16 каналов ШИМ по I2C) без регистров микроконтроллера.
 * Драйвер говорит с микросхемой только через три функции порта (pca_port_*): в прошивке fw13d.c их
 * делает I2C1 STM32F103, в стенде host13d.c — модель model13d.py.
 * Карта регистров и биты — по документации производителя; документа в проекте нет, адреса регистров
 * и биты совпадают с открытыми драйверами Zephyr, rusEFI и Klipper; с микросхемой не сверено. */
#ifndef PCA13D_H
#define PCA13D_H
#include <stdint.h>

/* ── регистры ─────────────────────────────────────────────────────────── */
#define PCA_MODE1         0x00u
#define PCA_MODE2         0x01u
#define PCA_LED0_ON_L     0x06u         /* канал n: 0x06 + 4n — ON_L, ON_H, OFF_L, OFF_H */
#define PCA_ALL_LED_ON_L  0xFAu         /* запись сразу во все 16 каналов */
#define PCA_PRE_SCALE     0xFEu         /* пишется только при SLEEP = 1 */

/* ── MODE1 ────────────────────────────────────────────────────────────── */
#define PCA_RESTART       0x80u         /* 1 — каналы остановлены сном; запись 1 перезапускает */
#define PCA_AI            0x20u         /* автоинкремент номера регистра */
#define PCA_SLEEP         0x10u         /* генератор выключен */
#define PCA_ALLCALL       0x01u         /* отвечать и на общий адрес 0x70 */
/* ── MODE2 ────────────────────────────────────────────────────────────── */
#define PCA_INVRT         0x10u         /* инверсия выходов */
#define PCA_OCH           0x08u         /* 0 — выходы меняются по STOP, 1 — по ACK */
#define PCA_OUTDRV        0x04u         /* 1 — двухтактный выход, 0 — открытый сток */

#define PCA_FULL          0x1000u       /* бит 12 в ON или OFF: канал всегда включён или всегда выключен */
#define PCA_STEPS         4096u         /* 12 бит: отсчётов на период */
#define PCA_CHANNELS      16u
#define PCA_OSC_HZ        25000000u     /* внутренний генератор */
#define PCA_ADDR_BASE     0x40u         /* A5…A0 на GND */
#define PCA_ADDR_ALLCALL  0x70u
#define PCA_PRE_MIN       3u
#define PCA_PRE_MAX       255u
#define PCA_WAKE_US       500u          /* генератору после SLEEP = 0 */

enum { PCA_OK = 0, PCA_E_NACK = -1, PCA_E_BUS = -2 };

typedef struct {
    uint8_t addr;                       /* семибитный адрес: 0x40 + перемычки */
    uint8_t prescale;                   /* что драйвер записал в PRE_SCALE */
    uint32_t osc_hz;                    /* частота генератора, которой верит драйвер: 25 МГц или после калибровки */
    uint32_t xfers, errors;             /* обменов по I2C и ошибок */
} pca_t;

/* ── порт: в прошивке — I2C1, в стенде — строки к модели ──────────────── */
/* записать n байт одной транзакцией: START, адрес, байты, STOP; 0, PCA_E_NACK или PCA_E_BUS */
int pca_port_write(uint8_t addr7, const uint8_t *tx, uint32_t n);
/* прочитать один байт регистра reg: запись номера, повторный старт, чтение */
int pca_port_read(uint8_t addr7, uint8_t reg, uint8_t *v);
void pca_port_delay_us(uint32_t us);

/* ── драйвер ──────────────────────────────────────────────────────────── */
uint8_t pca_prescale(uint32_t osc_hz, uint32_t freq_hz);
uint32_t pca_freq_mhz(uint32_t osc_hz, uint8_t prescale);
int pca_reg(pca_t *d, uint8_t reg, uint8_t v);
int pca_get(pca_t *d, uint8_t reg, uint8_t *v);
int pca_init(pca_t *d, uint8_t addr7, uint32_t freq_hz, uint8_t mode2);
int pca_set_freq(pca_t *d, uint32_t freq_hz);
uint32_t pca_calibrate(pca_t *d, uint32_t period_ns);
uint16_t pca_us_ticks(const pca_t *d, uint32_t us);
void pca_duty(uint32_t ch, uint32_t duty, int spread, uint16_t *on, uint16_t *off);
void pca_servo(const pca_t *d, uint32_t ch, uint32_t us, int spread, uint16_t *on, uint16_t *off);
uint16_t pca_gamma(uint32_t level);
int pca_channel(pca_t *d, uint32_t ch, uint16_t on, uint16_t off);
int pca_write_all(pca_t *d, const uint16_t on[16], const uint16_t off[16]);
int pca_all_off(pca_t *d);

#endif

pca13d.c — драйвер: частота, калибровка, отсчёты, фазы, гамма, запись всех каналов.

/* pca13d.c — раздел 13в: драйвер PCA9685 по I2C. Один и тот же файл собирается в прошивку (armclang,
 * Cortex-M3) и в стенд на компьютере (gcc): микроконтроллера он не знает, только порт pca_port_*. */
#include "pca13d.h"

/* 4096 × (i/16)^2,2 — гамма 2,2 по 17 точкам */
static const uint16_t gamma22[17] = {0, 9, 42, 103, 194, 317, 473, 665, 891, 1155, 1456, 1796, 2175, 2594, 3053, 3554, 4096};

/* PRE_SCALE = round(osc / (4096 × f)) − 1, в пределах 3…255 */
uint8_t pca_prescale(uint32_t osc_hz, uint32_t freq_hz)
{
    uint64_t div = (uint64_t)PCA_STEPS * (freq_hz ? freq_hz : 1u);
    uint64_t p = ((uint64_t)osc_hz + div / 2u) / div;   /* с округлением */
    if (p < PCA_PRE_MIN + 1u)
        p = PCA_PRE_MIN + 1u;
    if (p > PCA_PRE_MAX + 1u)
        p = PCA_PRE_MAX + 1u;
    return (uint8_t)(p - 1u);
}

/* фактическая частота в миллигерцах: osc / (4096 × (PRE_SCALE + 1)) */
uint32_t pca_freq_mhz(uint32_t osc_hz, uint8_t prescale)
{
    uint64_t div = (uint64_t)PCA_STEPS * (prescale + 1u);
    return (uint32_t)(((uint64_t)osc_hz * 1000u + div / 2u) / div);
}

static int xfer_done(pca_t *d, int rc)
{
    d->xfers++;
    if (rc)
        d->errors++;
    return rc;
}

int pca_reg(pca_t *d, uint8_t reg, uint8_t v)
{
    uint8_t b[2] = {reg, v};
    return xfer_done(d, pca_port_write(d->addr, b, 2));
}

int pca_get(pca_t *d, uint8_t reg, uint8_t *v)
{
    return xfer_done(d, pca_port_read(d->addr, reg, v));
}

/* PRE_SCALE пишется только во сне; после пробуждения — 500 мкс генератору и RESTART, если он взведён */
int pca_set_freq(pca_t *d, uint32_t freq_hz)
{
    uint8_t m, pre = pca_prescale(d->osc_hz, freq_hz);
    int rc;
    if ((rc = pca_get(d, PCA_MODE1, &m)) != PCA_OK)
        return rc;
    m &= (uint8_t)~(PCA_RESTART | PCA_SLEEP | PCA_ALLCALL);     /* общий адрес 0x70 — выключить */
    if ((rc = pca_reg(d, PCA_MODE1, m | PCA_AI | PCA_SLEEP)) != PCA_OK)
        return rc;
    if ((rc = pca_reg(d, PCA_PRE_SCALE, pre)) != PCA_OK)
        return rc;
    if ((rc = pca_reg(d, PCA_MODE1, m | PCA_AI)) != PCA_OK)    /* проснуться */
        return rc;
    pca_port_delay_us(PCA_WAKE_US);
    if ((rc = pca_get(d, PCA_MODE1, &m)) != PCA_OK)
        return rc;
    if (m & PCA_RESTART)                                /* каналы работали до сна: запустить их снова */
        rc = pca_reg(d, PCA_MODE1, m);
    if (rc == PCA_OK)
        d->prescale = pre;
    return rc;
}

int pca_init(pca_t *d, uint8_t addr7, uint32_t freq_hz, uint8_t mode2)
{
    int rc;
    d->addr = addr7;
    d->xfers = d->errors = 0;
    if (d->osc_hz == 0u)
        d->osc_hz = PCA_OSC_HZ;
    if ((rc = pca_reg(d, PCA_MODE2, mode2)) != PCA_OK)     /* заодно: отвечает ли кто-то по адресу */
        return rc;
    return pca_set_freq(d, freq_hz);
}

/* по измеренному периоду выхода: osc = 4096 × (PRE_SCALE + 1) / T */
uint32_t pca_calibrate(pca_t *d, uint32_t period_ns)
{
    uint64_t n = (uint64_t)PCA_STEPS * (d->prescale + 1u) * 1000000000u;
    if (period_ns)
        d->osc_hz = (uint32_t)((n + period_ns / 2u) / period_ns);
    return d->osc_hz;
}

/* микросекунды → отсчёты при текущих PRE_SCALE и частоте генератора, с округлением */
uint16_t pca_us_ticks(const pca_t *d, uint32_t us)
{
    uint64_t div = 1000000ull * (d->prescale + 1u);
    uint64_t t = ((uint64_t)us * d->osc_hz + div / 2u) / div;
    return (uint16_t)(t > PCA_STEPS - 1u ? PCA_STEPS - 1u : t);
}

/* заполнение 0…4096 → ON и OFF; spread — сдвиг начала на 256 отсчётов на канал */
void pca_duty(uint32_t ch, uint32_t duty, int spread, uint16_t *on, uint16_t *off)
{
    if (duty == 0u) {
        *on = 0;
        *off = PCA_FULL;                                /* всегда выключен */
    } else if (duty >= PCA_STEPS) {
        *on = PCA_FULL;                                 /* всегда включён */
        *off = 0;
    } else {
        *on = (uint16_t)(spread ? (ch % PCA_CHANNELS) * (PCA_STEPS / PCA_CHANNELS) : 0u);
        *off = (uint16_t)((*on + duty) % PCA_STEPS);    /* OFF < ON — импульс через конец периода */
    }
}

void pca_servo(const pca_t *d, uint32_t ch, uint32_t us, int spread, uint16_t *on, uint16_t *off)
{
    pca_duty(ch, pca_us_ticks(d, us), spread, on, off);
}

/* уровень 0…1024 → заполнение 0…4096 по гамме 2,2: таблица и линейная вставка между точками */
uint16_t pca_gamma(uint32_t level)
{
    if (level >= 1024u)
        return PCA_STEPS;
    uint32_t i = level >> 6, f = level & 63u;
    uint32_t a = gamma22[i], b = gamma22[i + 1u];
    return (uint16_t)(a + ((b - a) * f + 32u) / 64u);
}

/* один канал: номер регистра и четыре байта, младший первым; нужен автоинкремент (AI) */
int pca_channel(pca_t *d, uint32_t ch, uint16_t on, uint16_t off)
{
    uint8_t b[5] = {(uint8_t)(PCA_LED0_ON_L + 4u * (ch % PCA_CHANNELS)), (uint8_t)on, (uint8_t)(on >> 8),
                    (uint8_t)off, (uint8_t)(off >> 8)};
    return xfer_done(d, pca_port_write(d->addr, b, 5));
}

/* все 16 каналов одной транзакцией: 1 + 64 байта, выходы меняются вместе по STOP */
int pca_write_all(pca_t *d, const uint16_t on[16], const uint16_t off[16])
{
    uint8_t b[1 + 4 * PCA_CHANNELS];
    b[0] = PCA_LED0_ON_L;
    for (uint32_t i = 0; i < PCA_CHANNELS; i++) {
        b[1 + 4 * i] = (uint8_t)on[i];
        b[2 + 4 * i] = (uint8_t)(on[i] >> 8);
        b[3 + 4 * i] = (uint8_t)off[i];
        b[4 + 4 * i] = (uint8_t)(off[i] >> 8);
    }
    return xfer_done(d, pca_port_write(d->addr, b, sizeof b));
}

int pca_all_off(pca_t *d)
{
    uint8_t b[5] = {PCA_ALL_LED_ON_L, 0, 0, 0, PCA_FULL >> 8};
    return xfer_done(d, pca_port_write(d->addr, b, 5));
}

fw13d.c — прошивка STM32F103: I2C1 на 400 кГц, замер периода на PA8, 16 приводов кадрами по 20 мс.

/* fw13d.c — раздел 13в: PCA9685 на STM32F103 (BluePill), 72 МГц, APB1 = 36 МГц.
 * I2C1: PB6 — SCL, PB7 — SDA (открытый сток, 400 кГц). Перемычки A5…A0 на GND — адрес 0x40, OE на GND.
 * Выход LED15 перемычкой на PA8 (вход, допуск 5 В): по нему один раз меряется период и калибруется частота
 * генератора. Потом 16 приводов раз в 20 мс ходят 1000 ↔ 2000 мкс по 20 мкс за кадр, кадр — одна транзакция.
 * Тактирование до 72 МГц — как в разделе 6. */
#include "stm32f10x.h"
#include "pca13d.h"

#define CPU_HZ          72000000u
#define APB1_MHZ        36u
#define I2C_TIMEOUT_US  1000u           /* 65 байт на 400 кГц — 1490 мкс, но ждём каждый флаг отдельно */
#define FRAME_US        20000u

static pca_t pca;
volatile uint32_t bus_errors, period_ns_meas;

static int expired(uint32_t t0, uint32_t us)
{
    return DWT->CYCCNT - t0 >= us * (CPU_HZ / 1000000u);
}

void pca_port_delay_us(uint32_t us)
{
    uint32_t t0 = DWT->CYCCNT;
    while (!expired(t0, us)) {
    }
}

static void i2c1_init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    /* PB6, PB7 — альтернативная функция, открытый сток, 50 МГц (1111); PA8 — вход без подтяжки (0100, как после сброса) */
    GPIOB->CRL = (GPIOB->CRL & 0x00FFFFFFu) | 0xFF000000u;
    GPIOA->CRH = (GPIOA->CRH & ~0xFu) | 0x4u;
    I2C1->CR1 = 0;                                      /* настраивать при PE = 0 */
    I2C1->CR2 = APB1_MHZ;
    I2C1->CCR = I2C_CCR_FS | 30u;                       /* DUTY = 0: 36 МГц / (3 × 30) = 400 кГц */
    I2C1->TRISE = APB1_MHZ * 300u / 1000u + 1u;         /* 300 нс × 36 МГц = 10,8 → 10 + 1 = 11 */
    I2C1->CR1 = I2C_CR1_PE;
}

static int sr1_wait(uint16_t mask)
{
    uint32_t t0 = DWT->CYCCNT;
    while (!(I2C1->SR1 & mask)) {
        if (I2C1->SR1 & I2C_SR1_AF)
            return PCA_E_NACK;                          /* адрес или байт не подтверждён */
        if (expired(t0, I2C_TIMEOUT_US))
            return PCA_E_BUS;
    }
    return PCA_OK;
}

static int fail(int rc)
{
    I2C1->CR1 |= I2C_CR1_STOP;
    I2C1->SR1 = (uint16_t)~I2C_SR1_AF;                  /* AF снимается записью нуля */
    return rc;
}

/* START и байт адреса; ADDR не снят — его снимает вызывающий чтением SR2 */
static int start(uint8_t addr_byte)
{
    int rc;
    uint32_t t0 = DWT->CYCCNT;
    while (I2C1->SR2 & I2C_SR2_BUSY) {                  /* SDA держит ведомый — раздел 17.6 */
        if (expired(t0, I2C_TIMEOUT_US))
            return PCA_E_BUS;
    }
    I2C1->CR1 |= I2C_CR1_START;
    if ((rc = sr1_wait(I2C_SR1_SB)) != PCA_OK)
        return rc;
    I2C1->DR = addr_byte;
    return sr1_wait(I2C_SR1_ADDR);
}

int pca_port_write(uint8_t addr7, const uint8_t *tx, uint32_t n)
{
    int rc;
    if ((rc = start((uint8_t)(addr7 << 1))) != PCA_OK)
        return fail(rc);
    (void)I2C1->SR2;
    for (uint32_t i = 0; i < n; i++) {
        if ((rc = sr1_wait(I2C_SR1_TXE)) != PCA_OK)
            return fail(rc);
        I2C1->DR = tx[i];
    }
    if ((rc = sr1_wait(I2C_SR1_BTF)) != PCA_OK)
        return fail(rc);
    I2C1->CR1 |= I2C_CR1_STOP;                          /* по STOP выходы PCA9685 берут новые значения */
    return PCA_OK;
}

int pca_port_read(uint8_t addr7, uint8_t reg, uint8_t *v)
{
    int rc;
    if ((rc = start((uint8_t)(addr7 << 1))) != PCA_OK)
        return fail(rc);
    (void)I2C1->SR2;
    if ((rc = sr1_wait(I2C_SR1_TXE)) != PCA_OK)
        return fail(rc);
    I2C1->DR = reg;
    if ((rc = sr1_wait(I2C_SR1_BTF)) != PCA_OK)
        return fail(rc);
    I2C1->CR1 |= I2C_CR1_START;                         /* повторный старт: SR2.BUSY ещё 1, start() не годится */
    if ((rc = sr1_wait(I2C_SR1_SB)) != PCA_OK)
        return fail(rc);
    I2C1->DR = (uint8_t)(addr7 << 1 | 1u);
    if ((rc = sr1_wait(I2C_SR1_ADDR)) != PCA_OK)
        return fail(rc);
    I2C1->CR1 &= (uint16_t)~I2C_CR1_ACK;                /* один байт: RM0008, с. 764 — ACK снять до ADDR */
    (void)I2C1->SR2;
    I2C1->CR1 |= I2C_CR1_STOP;
    if ((rc = sr1_wait(I2C_SR1_RXNE)) != PCA_OK)
        return fail(rc);
    *v = (uint8_t)I2C1->DR;
    return PCA_OK;
}

/* фронт на PA8: дождаться нуля, потом единицы; 0 — нет сигнала за 50 мс */
static int edge(uint32_t *t)
{
    uint32_t t0 = DWT->CYCCNT;
    while (GPIOA->IDR & (1u << 8)) {
        if (expired(t0, 50000u))
            return 0;
    }
    while (!(GPIOA->IDR & (1u << 8))) {
        if (expired(t0, 50000u))
            return 0;
    }
    *t = DWT->CYCCNT;
    return 1;
}

/* период выхода LED15 в наносекундах: восемь периодов по счётчику тактов DWT */
static uint32_t period_ns(void)
{
    uint32_t a, b = 0;
    if (!edge(&a))
        return 0;
    for (int i = 0; i < 8; i++)
        if (!edge(&b))
            return 0;
    return (uint32_t)((uint64_t)(b - a) * 1000u / (72u * 8u));
}

int main(void)
{
    static uint16_t on[16], off[16];
    uint32_t us = 1000u, t0;
    int32_t dir = 20;
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    i2c1_init();
    while (pca_init(&pca, PCA_ADDR_BASE, 50u, PCA_OUTDRV) != PCA_OK)
        pca_port_delay_us(100000u);                     /* нет ответа: провод, адрес, питание */
    /* калибровка: LED15 — половина периода, его фронты считает PA8 */
    pca_duty(15u, PCA_STEPS / 2u, 0, &on[15], &off[15]);
    if (pca_channel(&pca, 15u, on[15], off[15]) == PCA_OK) {
        period_ns_meas = period_ns();
        if (period_ns_meas) {
            (void)pca_calibrate(&pca, period_ns_meas);
            (void)pca_set_freq(&pca, 50u);              /* PRE_SCALE заново — уже по настоящей частоте */
        }
    }
    t0 = DWT->CYCCNT;
    for (;;) {
        for (uint32_t ch = 0; ch < 16u; ch++)
            pca_servo(&pca, ch, us, 1, &on[ch], &off[ch]);
        if (pca_write_all(&pca, on, off) != PCA_OK)
            bus_errors++;
        us = (uint32_t)((int32_t)us + dir);
        if (us >= 2000u || us <= 1000u)
            dir = -dir;
        while (!expired(t0, FRAME_US)) {
        }
        t0 += FRAME_US * (CPU_HZ / 1000000u);
    }
}

Дальше: раздел 14 «АЦП» — оцифровка сигналов, режимы преобразования и связка с DMA и таймером.

Открыть в интерактивном справочнике