Нестабильность генерации, microsы и такты

Ругается, каккую-то строку хочет …

sketch error: expected string-literal before ‘nop’

А дигиталрид эти отличия не воспринимает? Внутренняя частота LGT8 в два раза больше - 32 МГц.

   if (RND < 1) PORTB &= ~(1 << 2); // установить 0 в бите 2 (10-й пин), пара микросекунд
   else PORTB |= (1 << 2);          // установить 1 в бите 2 (10-й пин)

   asm volatile(“nop”);
   asm volatile(“nop”);

    int dat = (PINB & (1 << 3));

    // int dat = digitalRead(RANDOM_IN);

   if (dat < 1) PORTD &= ~(1 << 5); // установить 0 в бите PD5 (5-й пин) - трансляция c 3-го бита PB
   else PORTD |= (1 << 5);                      // установить 1 в бите PD5 (5-й пин)

Что с ней разбираться? Ставишь STM32CubeIDE и настраиваешь что тебе нужно от таймера. А в программе просто указываешь что сделать.

Опиши свою хотелку подробно.
Если завтра будет время, намалюю тебе в стм.

Ну завтра уже 31-е, вроде кто-то уже пьёт ) … (но не я). И тут с дигиталридом тоже работает.

Нужны 3 выхода +1:

  • - тактовая частота CKC - желательно до 100 кГц, но можно и 20, желательно меандр, регулируемая внешним потенциометром,
    • выход данных (RND) - типа “случайные”, на самом деле до лампочки, просто чтобы явно не повторялись,
    • дополнительный выход обработанных данных (два if’а, по одному на полупериод)

if (RND < 1 && RND_OLD < 1) PORTD = PORTD^0b01000000; // смена уровня на выходе в начале бита данных, если второй 0 подряд

if (RND > 0) PORTD = PORTD^0b01000000; // смена уровня на выходе в середине бита по спаду CLC , т.е. CLC должен быть меандром

ну и с выхода данных (RND) через резистор на другой пин (чтобы можно было вместо внутреннего RND подавать внешние данные и чтобы эти if’ы уже их обрабатывали. Просто транслировать RND с выхода на другой вход и потом на другой выход.

блин, редактировать нельзя …

тактовая частота CLC (от 1 до желательно 100 кГц)

Похоже что-то с кавычками не так.
Пробуйте вручную набрать

Если и дальше будет ругаться, пробуйте просто несколько “nop” -ов, без volatile.

Да, кавычки.

С одним asm volatile(“nop”); ничего не меняется. С двумя - вообще другие данные получаются.

А с 5-ю нопами стало нормально :smiley: (просто сразу 5 поставил)

Команды прямого чтения/записи порта очень быстрые. Что бы что-то прочитать- сначала надо “надёжно” записать))
P.S.Извиняюсь, я ушёл. Если что - отвечу позже

Ну да, я тоже думал в эту сторону (последовательная запись в разные регистры), но недостаточно профессионально )

Я ставлю ”лайки” на полезные ответы, может ещё как-то надо выделить?

А ещё может быть задержка подачи сигнала на вход через резистор 3,9К. У пина ведь тоже есть входная ёмкость, хоть и маленькая. Я смотрю сигнал на выходе, а на входе он может немного запоздать. И ёмкость щупов осциллографа без делителя больше 100 пФ. Но и частоты вроде не бешеные мегагерцы … Надо будет потом попробовать просто замкнуть.

Итого:

Без нопов: - с резистором - задержка данных на такт (осциллограф ни при чём)

  • резистор замкнут - задержка данных на такт

С одним нопом: - с резистором - задержка данных на такт

  • резистор замкнут - данные искажаются
    (Длительность положительных полупериодов увеличивается на 1 такт за счёт отрицательных,
    фронты на месте. Если длительность отрицательных 1 такт, то они вообще пропадают)

С 2-я нопами: - с резистором - данные искажаются (как и с двумя volatile)

  • резистор замкнут - нормальная передача данных

С 3-я нопами: - с резистором - нормальная передача данных

  • резистор замкнут - нормальная передача данных

С 4-я нопами: - с резистором - нормальная передача данных

  • резистор замкнут - нормальная передача данных

С 5-ю нопами: - с резистором - нормальная передача данных

  • резистор замкнут - нормальная передача данных

Обзор от ИИ

На Arduino нет встроенного “флага подтверждения записи” для аппаратного порта :face_with_raised_eyebrow:

ушёл спать …

Глянул даташит - нет не отличается. Всё так же, как на Атмеге328р.
Синхронизация перед чтением - максимум полтора такта. Т.е. 1 NOP-a должно хватить, если учесть, что инструкция тоже, выполняется не мгновенно.
Вообщем, 2 NOP-a должно хватить с запасом.
Остальное, видимо, задержка от RC.
Может, кто ещё чего умного скажет))

Вот код для Ардуино ИДЕ (нужно поставить аддон STM32Duino https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json )

#define analogPin   PA0

void setup() {

  // 1. Включаем тактирование для GPIOA и TIM1
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_TIM1EN;

  delayMicroseconds(10);  // Даём время на стабилизацию тактирования

  // 2. Настраиваем PA8 как альтернативную функцию для ШИМ
  // Для PA8 используем регистр CRH (пины 8–15)
  GPIOA->CRH &= ~(0x0F << ((8 - 8) * 4));  // Очищаем биты для PA8 (позиция 0 в CRH)
  GPIOA->CRH |= (0x0A << ((8 - 8) * 4));   // CNF8=10, MODE8=11

  // 3. Настройка параметров таймера
  TIM1->PSC = 1;        // Предделитель = 2 (тактовая = 72 МГц)
  TIM1->ARR = 640;    // Период: 640 тактов → 50 кГц

  // 4. Настройка канала 1 для ШИМ Mode 1
  TIM1->CCMR1 &= ~TIM_CCMR1_OC1M;  // Очищаем поле OC1M
  TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;  // OC1M = 110 (PWM Mode 1)
  TIM1->CCMR1 |= TIM_CCMR1_OC1PE;  // Включаем буферизацию (OC1PE = 1)

  // 5. Включение выхода канала 1
  TIM1->CCER |= TIM_CCER_CC1E;  // CC1E = 1 (разрешаем выход канала 1)

  // 6. Установка коэффициента заполнения 50 %
  TIM1->CCR1 = 320;

  // 7. ЗАПУСК ТАЙМЕРА: включаем счётчик и буферизацию ARR
  TIM1->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE;

  // 8. ЗАПУСК ВЫХОДА: для TIM1 ОБЯЗАТЕЛЬНО включаем основной выход
  TIM1->BDTR |= TIM_BDTR_MOE;
}

void loop() {
  static uint16_t oldValue;
  uint16_t reg = analogRead(analogPin);
  reg = map(reg, 0, 4096, 320, 32000);

// Думал вот так изменять частоту ШИМ, но у меня почему-то ошибка: error: expression cannot be used as a function
// @МММ больше в STM32 разбирается, может поможет.
/*
  if (reg != oldValue) {
    oldValue = reg;
    TIM1->ARR = reg;          // новое значение частоты
    TIM1->CCR1 = (reg / 2);   // сохраняем заполнение 50%
    TIM1->CNT = 0;            // сброс счетчика
*/
  }
}

Так-то генерация есть, но у меня почему-то не получилось реализовать изменение частоты при чтении с аналогового порта (может сам разберешься или поможет кто). На твои “плюшки” времени не хватило… )

При счете 320 - максимальная частота (~100кГц), при счете 32000 - минимальная (~1кГц).

Покажи точно сообщение об ошибке. Судя по нему, проблема в синтаксисе и не имеет отношения к регистрам СТМ32

ЗЫ частоты как-то странно у тебя подсчитаны… При ARR =320 будет примерно 225 КГц, а не ~100k

Магия какая-то ))
При попытке повторить ошибку - всё откомпилировалось. Спасибо!

#define analogPin   PA0

void setup() {

  // 1. Включаем тактирование для GPIOA и TIM1
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_TIM1EN;

  delayMicroseconds(10);  // Даём время на стабилизацию тактирования

  // 2. Настраиваем PA8 как альтернативную функцию для ШИМ
  // Для PA8 используем регистр CRH (пины 8–15)
  GPIOA->CRH &= ~(0x0F << ((8 - 8) * 4));  // Очищаем биты для PA8 (позиция 0 в CRH)
  GPIOA->CRH |= (0x0A << ((8 - 8) * 4));   // CNF8=10, MODE8=11

  // 3. Настройка параметров таймера
  TIM1->PSC = 1;        // Предделитель = 2 (тактовая = 72 МГц)
  TIM1->ARR = 640;    // Период: 640 тактов → 50 кГц

  // 4. Настройка канала 1 для ШИМ Mode 1
  TIM1->CCMR1 &= ~TIM_CCMR1_OC1M;  // Очищаем поле OC1M
  TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;  // OC1M = 110 (PWM Mode 1)
  TIM1->CCMR1 |= TIM_CCMR1_OC1PE;  // Включаем буферизацию (OC1PE = 1)

  // 5. Включение выхода канала 1
  TIM1->CCER |= TIM_CCER_CC1E;  // CC1E = 1 (разрешаем выход канала 1)

  // 6. Установка коэффициента заполнения 50 %
  TIM1->CCR1 = 320;

  // 7. ЗАПУСК ТАЙМЕРА: включаем счётчик и буферизацию ARR
  TIM1->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE;

  // 8. ЗАПУСК ВЫХОДА: для TIM1 ОБЯЗАТЕЛЬНО включаем основной выход
  TIM1->BDTR |= TIM_BDTR_MOE;
}

void loop() {
  static uint16_t oldValue;
  uint16_t reg = analogRead(analogPin);
  reg = map(reg, 0, 4096, 320, 32000);

// Меняем частоту ШИМ
  if (reg != oldValue) {
    oldValue = reg;
    TIM1->ARR = reg;          // новое значение частоты
    TIM1->CCR1 = (reg / 2);   // сохраняем заполнение 50%
    TIM1->CNT = 0;            // сброс счетчика
  }
}

Скетч использует 11600 байт (17%) памяти устройства. Всего доступно 65536 байт.
Глобальные переменные используют 1092 байт (5%) динамической памяти, оставляя 19388 байт для локальных переменных. Максимум: 20480 байт.

Генерацию ШИМ проверял, работу с аналоговым портом - нет (писал выше).

Ну и отлично. Явных ошибок в логике не вижу, правда правильность употребления имен регистров не проверял, полагаюсь на комментарии :slight_smile:
Сброс счетчика в последней строке лишний.

И да, частоты посмотри

Во первых используется предделитель на 2 (хотел изначально уложить весь диапазон без map(), но понял что не получится, да и оставил):

А то, что при 320 100кГц - этого я не понял. В кубе было иначе. Возможно wiring какие-то вносит погрешности (я не нашел в аддоне где частота тактовая задается).

да, согласен

Ты в Ардуино собираешь? Посмотри в настройках платы частоту. Значение по умолчанию для блюпила в Кларковском аддоне сменили зачем-то с 72 МГц на 64Мгц. Возможно и в СТМ-овском тоже

Я у себя замучился каждый раз менять, забываешь же! - поправил в аддоне, чтобы дефолт был 72, как раньше