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)

  Тест на глитч: 
Прошивка F_CPU = 1 Мгц. Изменение периода 5T -> 4T с максимальным ШИМ (200 Кгц 80% -> 250 Кгц 75%)
Т.к. Запись OCR1AL, OCR1BL занимает 2 такта, 3 NOP не приводит к глитчу (5-2=3), а 4 приводит.
Значит синхронизация настроена правильно.


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)
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Кнопка 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 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
//------------------------------------------------------------------------------