https://radiokot.ru/forum/viewtopic.php?f=57&t=184276&e=0

forregister84

//------------------------------------------------------------------------------
Версия 0.7
ШИМ-генератор с LCD и управлением кнопками

Возможно кому-нибудь пригодится

LCD 8х2 WH0802A или 16х2 WH1602A
Кнопка Mode - вкл/выкл выхода.
Кнопки Freq-, Freq+ изменение частоты. Шаг 1 Гц (10, 100 Гц).
Кнопки Duty-, Duty+ изменение коэффициента заполнения ШИМ. Шаг 1% (5%).
Если долго держать кнопку, шаг увеличивается.

Частота и ШИМ сохраняются в EEPROM через 2 сек после изменения частоты или ШИМ (чтобы не записывать слишком часто)
На низкой частоте используется делитель /8, /64, /256 

	Версия с одним каналом PB2: ATMega8_PWM_LCD
Частота: 1 Гц...1.6 МГц ((F_CPU/10)). Можно поднять до Fmax= F_CPU/4 по datasheet
Fast PWM Mode
Длительность импульса минимум 1 такт. ШИМ 1...99%

	Версия с двумя каналами PB1, PB2: ATMega8_PWM_2CH_LCD
Частота: 1 Гц...800 кГц ((F_CPU/20)). Fmax= F_CPU/8
Phase and Frequency Correct PWM Mode
Длительность импульса минимум 2 такта. ШИМ 1...50%

2-х канальный ШИМ сыроват и возможны глюки на высоких частотах
из-за особенностей ICR1.
Наверное лучше 1 канал ШИМ + схема деления на канала: 74AC74 + 74AC08.

//------------------------------------------------------------------------------
ATMega8_PWM_LCD
  Версия 0.8 - исправлено в conv_period_freq(): не было перерасчета T1_OCR (OCR1B) при F>F_CPU/100 
Из-за этого коэффициент заполнения ШИМ сильно изменялся при изменении частоты и можно было получить даже 100 %.
(а должен слабо менятся, с учетом ограничений дискретности)
  Версия 0.81 - Кнопка Mode на PB0. Основные настройки в main.h. #define FREQ_MAX (F_CPU/4).
  Версия 0.82 - макс. шаг 10% периода при (Freq>FREQ_MAX_DF1Hz). 
Исправлен глюк при увеличении Freq до FREQ_MAX_DF1Hz+1. Нужно было нажать 2 раза для увеличения 4000 -> 4001 Гц

ATMega8_PWM_2CH_LCD - аналогичные исправления

//------------------------------------------------------------------------------
ATMega8_PWM_LCD_E - Версия с одним каналом
  ШИМ-генератор с LCD, 2-мя энкодерами и кнопкой
  Версия 0.81 - Кнопка Mode на PB0. Шаг только 1 Гц/1 такт.
  Версия 0.82 - Шаг частоты 1/10/100/1000 Гц. Шаг ШИМ 0,01/ 0,1 / 1 / 5 %. Переключение кнопкой энкодера по кругу.
  (нужно добавить мигание соответствующего разряда неск. сек.)
  Версия 0.83 - исправлено: когда минимальный шаг 0,03% приходилось 3 раза крутить энкодер на 0,01%
  Версия 0.85 - Шаг частоты 1/10/100/1000 Гц. Шаг ШИМ 0,01/ 0,1 / 1 / 5 %. Переключение кнопкой энкодера по кругу.
  добавлено мигание соответствующего разряда. Когда шаг 1000 Гц невозможен разряд все равно мигает тот же. 
  Версия 0.86 - исправлено: атомарный доступ к T1_TOP, T1_OCR (иногда устанавливался завышенный ШИМ). 
  Dead Time 0, 1, 2 такта F_CPU (легко перекомпилировать на //#define PWM_MAX 10000 //100%)
  
  Версия 0.87. F_CPU = 8...16 МГц (цикл while(1) = 2,15...1,5 мс)
  - if (dFreq>step) -> if (dFreq>=step)
  - минимизирована задержка обновления делителя таймера 1: около 570 нс (asm), на C было 1320 нс min.
  Т.к. приоритет прерываний таймера 2 (опрос энкодера) выше чем у таймера 1(обновление делителя), то 
  все вынесено в main и таймер 2 больше не используется.
  при переключении 245->244, 244->245 Гц рекомендуется ШИМ не менее 0,2% иначе импульс сильно укорачивается/удлиняется
  - При изменении частоты, если делитель = 1: 
    на частоте < 500 кГц) гарантируется отсутствие глитчей. 
    На большей частоте однократное увеличение/уменьшение периода или длительности импульса на 1 такт.
	Задержка от начала периода до окончания обновления около 812 нс.
    Пояснения в datasheet, когда нужно обновить только младший байт:
  17.3 Accessing 16-bit Registers
  17.3.1 Reusing the Temporary High Byte Register
  If writing to more than one 16-bit register where the High byte is the same for all registers written, then the High
byte only needs to be written once.

  Версия 0.87a. F_CPU = 8...16 МГц (цикл while(1) = 2,15...1,5 мс)
точная NOP-синхронизация с началом периода.
Обновление регистров происходит когда TCNT1=TOP, за 1 такт до переднего фронта импульса.
Потом (TCNT1=BOTTOM=0), появляется передний фронта импульса.
if (dFreq>step) -> if (dFreq>=step)
  Увеличил период мигания, т.к. даже пр +5C LCD медленно обновляется.
uint16_t blink_period;
#define SYM_BLINK_PERIOD 300
//------------------------------------------------------------------------------
  Тест на глитч: 
Прошивка F_CPU = 1 Мгц. Изменение периода 5T -> 4T с максимальным ШИМ (200 Кгц 80% -> 250 Кгц 75%)
Т.к. Запись OCR1AL, OCR1BL занимает 2 такта, 3 NOP не приводит к глитчу (5-2=3), а 4 приводит.
Значит синхронизация настроена правильно.
  Версия 0.87b. Тест. Запись в TIFR сразу после sei ставить нельзя, 
синхронизация не работает и есть глитч даже с 0 NOP и 1 NOP.
//------------------------------------------------------------------------------
Версия с двумя каналами
ATMega8_PWM_2CH_LCD_E - аналогичные исправления
  Версия 0.85 - нужно проработать вопрос Dead Time
  Версия 0.86 - исправлено: атомарный доступ к T1_TOP, T1_OCR. Dead Time 0...1, 1...2, 2..3 такта F_CPU
  Версия 0.86a - Исправлено: при увеличении частоты ICR1 может быть меньше TCNT1 и тогда счетчик считает до 0xFFFF.
Исправлено с помощью записи 1 в TCNT1, что приводит глитчу ВЧ.
Глитч НЧ при изменении делителя остался.
if (dFreq>step) -> if (dFreq>=step)
  Версия 0.87a. F_CPU = 8...16 МГц (цикл while(1) = 2,15...1,5 мс)
точная NOP-синхронизация с началом периода.
Обновление регистров происходит когда TCNT1=BOTTOM.
if (dFreq>step) -> if (dFreq>=step)
  Увеличил период мигания, т.к. даже пр +5C LCD медленно обновляется.
uint16_t blink_period;
#define SYM_BLINK_PERIOD 300
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Кнопка Mode на PB0 (так удобнее на моей макетке).
//------------------------------------------------------------------------------
Стоит обратить внимание на расчеты в Excel. Дискретность 1 Гц возможна на частоте F<=sqrt(F_CPU)
Т.е. С кварцем 16 МГц дискретность 1 Гц до частоты 4 кГц.
С кварцем 1 МГц дискретность 1 Гц до частоты 1кГц.
При увеличении дискретности в 10 раз частота увеличивается в sqrt(10)=3,162 раз:
4 кГц -> 12,649 кГц -> 40 кГц

Также обратите внимание на зависимость максимальной частоты от напряжения питания (datasheet ATmega8A)
Figure 26-1. Maximum Frequency vs. Vcc
11.2.3 Brown-out Detection (сброс при понижении питания)
26.5 System and Reset Characteristics

Т.е. С кварцем 16 МГц минимум 4,5 В и с кварцем 8 МГц минимум 2,7 В.
  Поэтому фъюз BODEN=0, BODLEVEL=0 
//------------------------------------------------------------------------------
    Пояснения в datasheet, когда нужно обновить только младший байт:
  17.3 Accessing 16-bit Registers
  17.3.1 Reusing the Temporary High Byte Register
  If writing to more than one 16-bit register where the High byte is the same for all registers written, then the High
byte only needs to be written once.
//------------------------------------------------------------------------------
  7.7 Reset and Interrupt Handling
  When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction
before any pending interrupt is served.
  When using the CLI instruction to disable interrupts, the interrupts will be immediately disabled. No interrupt will be
executed after the CLI instruction, even if it occurs simultaneously with the CLI instruction.
  When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pend-
ing interrupts
  7.7.1 Interrupt Response Time (4+3 такта или 4+2 ?)
The Vector is normally a jump to the interrupt routine, and this jump takes three clock cycles.
Это опечатка ? 
  31. Instruction Set Summary
RJMP: 2 Clocks
//------------------------------------------------------------------------------