ШИМ произвольного разрешения на UNO/Nano (328)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Столкнулся с проблемой - не хватало разрешения стандартного 8-бит ШИМа. Изменение скважности на 1 давало слишком большой эффект. Потребовался ШИМ более высокого разрешения (второй параметр analogWrite нужен не 0-255, а 0-8192 в моём случае).

Написал три функции, которые позволяют сделать на пинах 9 и 10 ШИМ с разрешением от 2 бит (второй параметр analogWrite в пределах 0-3) до 16 бит  (второй параметр analogWrite - 0-65 535).

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

Вот код.

inline void pwmInit(const int8_t bitRate = 16) {
	noInterrupts();
	TCCR1A = bit(COM1A1) |  bit(COM1B1) | bit(WGM11);
	TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS10);
	TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
	ICR1 = (1u << bitRate) - 1u;
	interrupts();
}
inline void pwmWrite9(const uint16_t v) { DDRB |= bit(PB1); OCR1A = v; }
inline void pwmWrite10(const uint16_t v) { DDRB |= bit(PB2); OCR1B = v; }

Функцию pwmInit нужно вызвать один раз (например, в setup). Её параметр - разрядность ШИМ. Может быть от 2 до 16. Функция не проверяет корректность параметра - на Вашей совести.

После этого можно вызывать функции pwmWrite9 (для 9-го пина) и/или pwmWrite10 (для 10-го) пина и устанавливать скважность точно также, как мы вызываем analogWrite. Значение 0 - сигнала нет, максимальное значение - сигнал сплошной.

Допустимые максимальные значения (как и частота ШИМ) зависят от разрядности (параметра, который передавали функции pwmInit).

В таблице первый столбец - разрядность (параметр, который передаём pwmInit), второй столбец - максимальное значение, которое можно передавать pwmWrite9 и pwmWrite10 (то, при котором сигнал будет сплошным) в столбцах - частоты ШИМ при тех или иных тактовых частотах микроконтроллера. Для стандартной ардуины следует смотреть столбец 16МГц (второй справа).

Бит Макс Частота контроллера (МГц)
1 2 4 8 16 20
2 3 250 000,0 500 000,0 1 000 000,0 2 000 000,0 4 000 000,0 5 000 000,0
3 7 125 000,0 250 000,0 500 000,0 1 000 000,0 2 000 000,0 2 500 000,0
4 15 62 500,0 125 000,0 250 000,0 500 000,0 1 000 000,0 1 250 000,0
5 31 31 250,0 62 500,0 125 000,0 250 000,0 500 000,0 625 000,0
6 63 15 625,0 31 250,0 62 500,0 125 000,0 250 000,0 312 500,0
7 127 7 812,5 15 625,0 31 250,0 62 500,0 125 000,0 156 250,0
8 255 3 906,3 7 812,5 15 625,0 31 250,0 62 500,0 78 125,0
9 511 1 953,1 3 906,3 7 812,5 15 625,0 31 250,0 39 062,5
10 1 023 976,6 1 953,1 3 906,3 7 812,5 15 625,0 19 531,3
11 2 047 488,3 976,6 1 953,1 3 906,3 7 812,5 9 765,6
12 4 095 244,1 488,3 976,6 1 953,1 3 906,3 4 882,8
13 8 191 122,1 244,1 488,3 976,6 1 953,1 2 441,4
14 16 383 61,0 122,1 244,1 488,3 976,6 1 220,7
15 32 767 30,5 61,0 122,1 244,1 488,3 610,4
16 65 535 15,3 30,5 61,0 122,1 244,1 305,2

Используется режим Fast-PWM, если это кому-то важно. Кто не знает, что это такое - либо забейте, либо посмотрите в даташите.

Кому надо, пользуйтесь.

Финальное замечание: В принципе, функции pwmWrite9 и pwmWrite10 нафиг не нужны. Если вместо их использовать стандартные analogWrite(9, …) и analogWrite(10, …), всё будет работать ничуть не хуже. Можете попробовать. А выделил я их вот для чего. Дело в том, что после вызова pwmInit у Вас изменились разрешение и частота и разрешение ШИМ только на пинах 9 и 10. На остальных всё осталось как было раньше. Поэтому, если Вы, например, вызвали pwmInit(16); то с этого момента второй параметр analogWrite для пинов 9 и 10 имеет право быть от 0 до 65 535 в то время как для остальных пинов, он по прежнему должен быть в границах 0-255. Так вот, чтобы было поменьше путаницы, я и ввёл специальные функции для 9-го и 10-го пинов. Хотя, стандартные тоже нормально работают.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Блин, забыл написать - частота ШИМ в таблице дана в герцах.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Работающий пример можно?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, не знаю, чего там примерять-то? Ну, ... вот ...

inline void pwmInit(const int8_t bitRate = 16) {
	noInterrupts();
	TCCR1A = bit(COM1A1) |  bit(COM1B1) | bit(WGM11);
	TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS10);
	TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
	ICR1 = (1u << bitRate) - 1u;
	interrupts();
}
inline void pwmWrite9(const uint16_t v) { DDRB |= bit(PB1); OCR1A = v; }
inline void pwmWrite10(const uint16_t v) { DDRB |= bit(PB2); OCR1B = v; }

void setup(void) {
	pwmInit(13);
}

void loop(void) {
	int i, step = 1024;
	for (i = step; i < 8192; i += step) {
		pwmWrite9(i);
		pwmWrite10(8192 - i);
		delay(1000);
	}
	for (i -= step; i > 0; i -= step) {
		pwmWrite9(i);
		pwmWrite10(8192 - i);
		delay(1000);
	}
}

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

:) Интересный подход .. а каким макаром у Вас, Евгений,  16-и разрядное "v" передается в 8-и разрядный OCR1A? Там, вроде как по даташиту, требуется делать 2 пересылки в строго заданной последовательности И с запрещеннымии прерываниями .. макрос из io.h ( OCR1A=..) разве на такое способен?

:)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы что-то перепутали, OCR1A - 16-ти  разрядный. См. п. 16.11.5 даташита (ссылка на даташит есть в исходном посте темы).

Там, похоже, ошибка другого рода имеется, как выловлю, выложу правку. При записи 0 (причём, когда там и так ноль был) иногда откуда-то высокий уровень проскакивает. Не часто и всплеск короткий - но непорядок. Ноль он должен и в Африке быть нулём. Сейчас пытаюсь отловить проблему и найти решение. 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ЕвгенийП, ну проблемой то это трудно назвать, так и должно быть. Ноль в регистре сравнения -это не ничего, это когда TCNT1==0  :) В ардуиновской функции этот ноль специально отлавливается, и таймер просто останавливается.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Спасибо, что-то подобное я и предполагал, что в момент равенства должен на 1 такт вылетать импульс.

Другое дело, почему он не вылетает, когда я ничего туда не пишу? Вот сидит там себе 0 и никого не трогает и ничего не вылетает. ... Хотя, я уже начал слмневаться, не помню поставил ли я режим "пик детект" на осциллографе. А без него такой импульс нетрудно и пропустить.

В любом случае спасибо, в выходной займусь и как победю - выложу правку.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ЕвгенийП, пик должен быть периодическим, каждый раз при переходе через ноль. Можно позаимствовать метод "борьбы" из wiring_analog.c

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, я уже проверил, спасибо. Действительно я просто не включал пик-детект и потому мне казалось, что эффект "плавающий". И когда собственно понял причину всё думал, но блин он должен быть всегда, почему он только иногда проявляется :))) Спасибо. Насчёт метода посмотрю - подумаю. В выходные выложу обновление. Там, на самом деле, если пожертвовать 16-битным режимом, можно просто вместо 0 зафигачивать туда "нечто большее, чем ICR1  - проблема уйдёт. Но 16-битный режим жалко, хотя прямо сейчас он мне и не нужен. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Добрый день, коллеги.

Как и обещал, в выходной вернулся к задаче и подробно исследовал проблему.

Значит, таки да, если параметр, отвечающий за скважность (впредь я и буду просто говорить «скважность») установить в 0 (или в максимальное значение), то это таки не LOW (HIHG) а просто минимальная (максимальная) длительность высокого уровня.

Ниже три осциллограммы, которые это показывают. При нуле (верхняя картинка) длительность импульса 62 наносекунды, при единице (средняя картинка) – 124 нс, а при двойке (нижняя картинка) – 186 нс.


pwmWrite9(0);


pwmWrite9(1);


pwmWrite9(2);

Таким образом, получается, что если делать так, как сделано в wrining_analog.c (т.е. при нуле и при максимальном значении просто нагло вставлять туда digitalWrite) то, мы получим скачок скважности между 0 и 1 вдвое больший, чем, между, скажем 1 и 2 или любыми другими соседними числами.

К сожалению, для моей задачи это неприемлемо и я от этого отказался.

Решил в целом оставить как есть, а если нужны чистые LOW и HIGH, то пользоваться аналогом digitalWrite напрямую.Написал для этих двух пинов digitalWrite9 и digitalWrite10. 

Получилось несколько менее удобно, чем в среде Ардуино, т.к. нужно самому заботиться о том, чтобы явно вызвать digitalWrite9/10 для постоянного низкого или высокого уровней. Зато шаг между низким уровнем и 0 точно такой же, как между 0 и 1 и т.д., т.е. регулировка равномерная на всём диапазоне, а для меня это сейчас важно и стоит того удобства.

Оформил include’файлом (кому нравится, могут называть это библиотекой).

#ifndef	FlexPWM_h
#define	FlexPWM_h

#define __bit(b) (1u << (b))

inline void pwmInit(const int8_t bitRate = 16) {
	noInterrupts();
	TCCR1A = __bit(WGM11);
	TCCR1B = __bit(WGM13) | __bit(WGM12) | __bit(CS10);
	TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
	ICR1 = (1u << bitRate) - 1u;
	interrupts();
}

inline void pwmWrite9(const uint16_t v) { 
	DDRB |= __bit(PB1);
	TCCR1A |= __bit(COM1A1);
	OCR1A = v;
}

inline void digitalWrite9(const uint8_t v) {
	DDRB |= __bit(PB1);
	TCCR1A &= ~__bit(COM1A1); 
	if (v) PORTB |= __bit(PB1); else PORTB &= ~__bit(PB1);
}

inline void pwmWrite10(const uint16_t v) {
	DDRB |= __bit(PB2);
	TCCR1A |= __bit(COM1B1);
	OCR1B = v;
}

inline void digitalWrite10(const uint8_t v) {
	DDRB |= __bit(PB2);
	TCCR1A &= ~__bit(COM1B1);
	if (v) PORTB |= __bit(PB2); else PORTB &= ~__bit(PB2);
}

#endif	// FlexPWM_h

Вот пример. На экране осциллографа сигнал растягивается/стягивается как гармошка, а на чистых HIGH и LOW замирает на 5 секунд.

#include "FlexPWM.h"

void setup(void) {
	pwmInit(13);
}

void loop(void) {
	digitalWrite10(LOW);
	delay(5000);
	int duty = 0;
	for (; duty < 8192; duty++) {
		pwmWrite10(duty);
		delay(1);
	}
	digitalWrite10(HIGH);
	delay(5000);
	for (duty--; duty >=0; duty--) {
		pwmWrite10(duty);
		delay(1);
	}
}

 

Gregor-R
Offline
Зарегистрирован: 10.07.2017

ЕвгенийП пишет:
Написал три функции, которые позволяют сделать на пинах 9 и 10 ШИМ с разрешением от 2 бит (второй параметр analogWrite в пределах 0-3) до 16 бит  (второй параметр analogWrite - 0-65 535).

Вот код.

inline void pwmInit(const int8_t bitRate = 16) {
	noInterrupts();
	TCCR1A = bit(COM1A1) |  bit(COM1B1) | bit(WGM11);
	TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS10);
	TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
	ICR1 = (1u << bitRate) - 1u;
	interrupts();
}
inline void pwmWrite9(const uint16_t v) { DDRB |= bit(PB1); OCR1A = v; }
inline void pwmWrite10(const uint16_t v) { DDRB |= bit(PB2); OCR1B = v; }

Функцию pwmInit нужно вызвать один раз (например, в setup). Её параметр - разрядность ШИМ. Может быть от 2 до 16. Функция не проверяет корректность параметра - на Вашей совести.

После этого можно вызывать функции pwmWrite9 (для 9-го пина) и/или pwmWrite10 (для 10-го) пина и устанавливать скважность точно также, как мы вызываем analogWrite. Значение 0 - сигнала нет, максимальное значение - сигнал сплошной.

Допустимые максимальные значения (как и частота ШИМ) зависят от разрядности (параметра, который передавали функции pwmInit).

Вопросы по коду программы:

- Входной параметр функции определен как константа, для чего это предусмотрено?

const int8_t bitRate = 16

- Можно менять частоту ШИМ на меньшую, чем указано в таблице и как ?

- Какая расчетная формула  для частот ШИМ  в таблице?

- Что означает "1u"  в строке 6 ?

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Gregor-R пишет:

- Входной параметр функции определен как константа, для чего это предусмотрено?

const int8_t bitRate = 16

Указание компилятроу на то, что я не собираюсь изменять значение bitRate внутри функции. В большинстве случаев не влияет ни на что, но в редких случаях помогает ему (компилятору) в оптимизации кода. Заведите себе хорошую привычку: если объект реально константа - не скрывать это от компилятора. Нет-нет, да принесёт пользу.

Gregor-R пишет:

- Можно менять частоту ШИМ на меньшую, чем указано в таблице и как ?

Можно. При помощи делителя частоты таймера. Но тогда, само собой,  поползёт таблица - её нужно будет пересчитывать.

Делитель частоты задаётся битами CS1x регистра TCCR1B. У меня в строке 4 задано CS10 == 1, а остальные (CS11 и CS12) оставлены нулями. Это соответсвует делителю, равному 1. Если нужно делить частоту (до 1024), то надо задать правильную комбинацию битов (см. таблицу 16-5 в даташите).

Gregor-R пишет:

- Какая расчетная формула  для частот ШИМ  в таблице?

Формула на стр. 125 даташита вверху при N=1 (это делитель частоты). В принципе, вот здесь можете взять готовый Excel, там есть эта таблица. Там же можно ввести другой делитель частоты и всё пересчитается.

Gregor-R пишет:

- Что означает "1u"  в строке 6 ?

u или U после числа (без пробела) означает, что число беззнаковое (unsigned).

 

GAMON
Offline
Зарегистрирован: 03.07.2017


 Пытался переделать исходный текст программы под ATmega2560. Но не смог. "Ошибка компиляции для платы Arduino/Genuino Mega or Mega 2560."

Укажите, пожалуйста, на ошибки

#ifndef  FlexPWM_h
#define FlexPWM_h

#define __bit(b) (1u << (b))

inline void pwmInit(const int8_t bitRate = 16) {
  noInterrupts();
  TCCR1A = __bit(WGM11);
  TCCR1B = __bit(WGM13) | __bit(WGM12) | __bit(CS10);
  TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
  ICR1 = (1u << bitRate) - 1u;
  interrupts();
}

inline void pwmWrite11(const uint16_t v) { 
  DDRB |= __bit(PB5);
  TCCR1A |= __bit(COM1A1);
  OCR1A = v;
}

inline void digitalWrite11(const uint8_t v) {
  DDRB |= __bit(PB5);
  TCCR1A &= ~__bit(COM1A1); 
  if (v) PORTB |= __bit(PB5); else PORTB &= ~__bit(PB5);
}

inline void pwmWrite12(const uint16_t v) {
  DDRB |= __bit(PB6);
  TCCR1A |= __bit(COM1B1);
  OCR1B = v;
}

inline void digitalWrite12(const uint8_t v) {
  DDRB |= __bit(PB6);
  TCCR1A &= ~__bit(COM1B1);
  if (v) PORTB |= __bit(PB6); else PORTB &= ~__bit(PB6);
}

#endif  // FlexPWM_h

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

GAMON пишет:

Укажите, пожалуйста, на ошибки

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

GAMON
Offline
Зарегистрирован: 03.07.2017

Jeka_M пишет:

GAMON пишет:

Укажите, пожалуйста, на ошибки

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

collect2.exe: error: ld returned 1 exit status

 

 Если это - что сие означает?

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014
GAMON
Offline
Зарегистрирован: 03.07.2017

 Может быть связь с тем, что на входе стоит CH340?

Другие программы норм работают; без проблем

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Нет, это вообще не при чём. Почитайте внимательно по тем ссылкам, что я дал.

GAMON
Offline
Зарегистрирован: 03.07.2017

Jeka_M пишет:

Нет, это вообще не при чём. Почитайте внимательно по тем ссылкам, что я дал.

 Я прочитал. Я hardware; начну лазить и удалять всякие .lto - вообще завалю все)

Спасибо за оперативный консалт

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Для начала попробуйте заменить ld.exe из другой версии Arduino IDE (более старой или более новой)

CatSoup
Offline
Зарегистрирован: 01.05.2017

Интересные осциллограммы, однако я снимал прямую линию с этим кодом при 100% коэфф. заполнения (обозвал его яркостью по аналогии с либой pwm):

void setPWM (unsigned int frequency, unsigned int brightness){
  unsigned long halfavrfreq = 8000000;
  unsigned long top = 0;
  unsigned long regA = 0;
  unsigned long regB = 0;
  unsigned long tick = 0;
  top = halfavrfreq/frequency;
  ICR1=top;
  tick = top*brightness/100;
  regA = tick-1;
  regB = top-tick;
  OCR1A=regA; 
  OCR1B=regB;
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, так не должно быть. Это противоречит и даташиту и здравому смыслу. Я тоже видел прямые линии пока не поставил редим peak detect на осциллографе.

Код Ваш не смотрел, надеюсь там всё нормально, но если что, могу запустить у себя.

CatSoup
Offline
Зарегистрирован: 01.05.2017

Да там все нормально, кусок просто выдрал из скетча, инициализация в другом месте осталась)). Я делал 2х каналку - один счетчик включался по счету вверх выше значения регистра сравнения и отключался при счете вниз, другой - наоборот. Еще раз пролистал сабж и даташит - в сухом остатке мы имеем то, что при здании регистру сравнения топового значения, остаются щелчки на частоте, зависящей от опорной и количества попугаев в регистре TOP. Я их не видел, но раз Вы говорите что они есть и без peak-detect их не видно - то я их и не увижу, мой осцил старше меня. А если регистру сравнения здать нулевое значение, то при включении на счете вверх же ничего не должно щелкать, должен быть полный "0"? Почему задал вопрос - при изменении чстоты/скважности "на ходу", даже в последовательности обнулить регистры - подождать микросекунду - задть новые значения иногда бывают непредсказуемые "броски" с большой скважностью. Для большинства применений на это, конечно, можно было бы забить, но хорошие IGBT дороговты)

nickavia22
Offline
Зарегистрирован: 11.07.2018

Добрый вечер. Читаю все Ваши коменты и решил задать вопрос о замере сигнала датчика давления 50гц и преобразовании в 25гц с темже процентом шима.

Заранее благодарю   Николай

mail: nickavia@ukr.net    Очень нужно.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

nickavia22
Offline
Зарегистрирован: 11.07.2018

 

Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%

Тоесть шим в % должен быть такой  как в датчике давлания. С уважением Николай.

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

nickavia22 пишет:

 

Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%

Тоесть шим в % должен быть такой  как в датчике давлания. С уважением Николай.

скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)

SLKH
Offline
Зарегистрирован: 17.08.2015

ua6em пишет:

nickavia22 пишет:

 

Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%

Тоесть шим в % должен быть такой  как в датчике давлания. С уважением Николай.

скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)

а pulseIn() + micros()  - не? 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

SLKH пишет:

ua6em пишет:

nickavia22 пишет:

 

Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%

Тоесть шим в % должен быть такой  как в датчике давлания. С уважением Николай.

скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)

а pulseIn() + micros()  - не? 

не знаю, через pulseIn() + micros() ничего не делал, как-то всё напрямую )))

sadman41
Offline
Зарегистрирован: 19.10.2016

Не знаю, заглянут ли сюда специалисты, но спрошу...

В wiring_digital.c написано:

void digitalWrite(uint8_t pin, uint8_t val) {
...
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);
...
	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}
}

static void turnOffPWM(uint8_t timer) {

	switch (timer) {
...
		case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
...
}

Вопрос: при выключении PWM через turnOffPWM() -> cbi(TCCR2A, COM2A1) связанный с таймером (юзаю #2) вывод всегда приходит в определённое положение или же в случайное, а затем оно фиксится последующей записью в порт?

Я тут пробую делать простой вкл/выкл PWM в режиме Phase corrected и, на осциллографе, при отключении вывода от таймера (сбросом COM2A1), он четко ложится в LOW.

Однако, я помню, что в режиме CTC, на таймере #1 при схожем способе отключения, нога оставалась в последнем установленном положении и приходилось дополнительно делать TCCR1C = (1 << FOC1A);

В даташите написано, что "The FOC2A bit is only active when the WGM bits specify a non-PWM mode", т.е. неприменим в данном случае, а про гарантированный переход выхода в заданное состояние при остановке генерации я, что-то, не вижу. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

 а про гарантированный переход выхода в заданное состояние при остановке генерации я, что-то, не вижу. 

а мной тестовка вообще не воспринимается, исключительно, если разрисовать в жёсткой логике

sadman41
Offline
Зарегистрирован: 19.10.2016

Исходя из этого: "The extreme values for the OCR2A register represent special cases when generating a PWM waveform output in the phase correct PWM mode. If the OCR2A is set equal to BOTTOM, the output will be continuously low and if set equal to MAX the output will be continuously high for non-inverted PWM mode." - приостановка PWM с фиксацией вывода в состоянии LOW (при неинверсном режиме работы sbi(TCCR2A, COM2A1)) производится через OCR2A = 0x00.

Т.е. способ зафиксировать я нащупал, но академический вопрос всё же интересен - как становятся ноги при отключении от них генератора...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Исходя из этого: "The extreme values for the OCR2A register represent special cases when generating a PWM waveform output in the phase correct PWM mode. If the OCR2A is set equal to BOTTOM, the output will be continuously low and if set equal to MAX the output will be continuously high for non-inverted PWM mode." - приостановка PWM с фиксацией вывода в состоянии LOW (при неинверсном режиме работы sbi(TCCR2A, COM2A1)) производится через OCR2A = 0x00.

Т.е. способ зафиксировать я нащупал, но академический вопрос всё же интересен - как становятся ноги при отключении от них генератора...

ну если в Z состоянии, вход оборван, то практически обычно равнозначно что на этих пинах  "1", а дальше как логика распорядилась, даже в 155 логике неиспользуемые пины рекомендовалось тянуть к + питания через резистор до 10 ног, но практически в обслуживаемых мной устройствах разработчик оставлял их свободно болтающимися, особо помехи не ловились...

надо проэмулировать, однозначно там будет однозначность )))
извиняюсь за мой французский

Ваш пример но с табличкой пояснения унутрях:
 

//https://arduino.ru/forum/programmirovanie/shim-proizvolnogo-razresheniya-na-unonano-328#comment-281263

/*
Бит Макс                               Частота контроллера (МГц)
             1            2           4             8             16           20
2   3      250 000,0   500 000,0  1 000 000,0     2 000 000,0   4 000 000,0   5 000 000,0
3   7      125 000,0   250 000,0    500 000,0     1 000 000,0   2 000 000,0   2 500 000,0
4   15      62 500,0   125 000,0    250 000,0       500 000,0   1 000 000,0   1 250 000,0
5   31      31 250,0    62 500,0    125 000,0       250 000,0     500 000,0     625 000,0
6   63      15 625,0    31 250,0     62 500,0       125 000,0     250 000,0     312 500,0
7   127      7 812,5    15 625,0     31 250,0        62 500,0     125 000,0     156 250,0
8   255      3 906,3     7 812,5     15 625,0        31 250,0      62 500,0      78 125,0
9   511      1 953,1     3 906,3      7 812,5        15 625,0      31 250,0      39 062,5
10  1 023      976,6     1 953,1      3 906,3         7 812,5      15 625,0      19 531,3
11  2 047      488,3       976,6      1 953,1         3 906,3       7 812,5       9 765,6
12  4 095      244,1       488,3        976,6         1 953,1       3 906,3       4 882,8
13  8 191      122,1       244,1        488,3           976,6       1 953,1       2 441,4
14  16 383      61,0       122,1        244,1           488,3         976,6       1 220,7
15  32 767      30,5        61,0        122,1           244,1         488,3         610,4
16  65 535      15,3        30,5         61,0           122,1         244,1         305,2
 */

 
inline void pwmInit(const int8_t bitRate = 16) {
  noInterrupts();
  TCCR1A = bit(COM1A1) |  bit(COM1B1) | bit(WGM11);
  TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS10);
  TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0;
  ICR1 = (1u << bitRate) - 1u;
  interrupts();
}
inline void pwmWrite9(const uint16_t v) { DDRB |= bit(PB1); OCR1A = v; }
inline void pwmWrite10(const uint16_t v) { DDRB |= bit(PB2); OCR1B = v; }

void setup(void) {
  pwmInit(13);
}

void loop(void) {
  int i, step = 1024;
  for (i = step; i < 8192; i += step) {
    pwmWrite9(i);
    pwmWrite10(8192 - i);
    delay(1000);
  }
  for (i -= step; i > 0; i -= step) {
    pwmWrite9(i);
    pwmWrite10(8192 - i);
    delay(1000);
  }
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Вроде я с этим разобрался - выход перекидывается из состояния в другое состоянии при достижении переключающей точки (OCRn, TOP, BOTTOM - от режима зависит). При отключении от генератора остается в том состоянии, в котором был на тот момент, специально ничего МК не делает, если не понудить его через FOC или установку состояния вручную. Присвоение TCNT значения, приводящего его в переключающую точку, ничего не дает, ибо сравнение не срабатывает. Видимо оно только на тике генератора производится.