Как стать автором
Обновить

Часы на базе микроконтроллера ch32v003 (часть 2)

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров3.6K

Эта статья, продолжение статьи часть 1. Как правильно в ней заметил @mozg37 в комментариях, RC генератор не обеспечивает достаточной точности хода часов. Благо на aliexpress можно заказать готовую плату nanoCH32V003 с кварцем на борту. В итоге получилось немного модернизировать плату и собрать проект в корпусе, распечатав его.

Внешний вид часов, пока без крышки
Внешний вид часов, пока без крышки

Корпус нарисовал в FreeCad вспомнив, что я когда-то все же на инженера учился. Сама моделька лежит тут Clock6.FCStd и готовый нарезанный на слои проект тут box_body.stl. Чуть позже выложу крышку, пока еще не нарисовал.

Вид сверху
Вид сверху

Схема

Как видно на картинке сверху мне удалось спалить схему по преобразованию 3,7в -> 5в. В итоге в готовое устройство попала только схема зарядки li-pol аккумулятора. На плате микроконтроллера снял предохранитель и в разрез подключил схему зарядки аккумулятора. Индикатор и зуммер тоже подключил к аккумулятору, позже планирую добавить предохранитель, либо схему защиты. Хотя в аккумуляторе уже есть схема защиты, её же должно хватить?

На плате микроконтроллера есть линейный стабилизатор на 3,3в, при напряжении ниже 3,3в на микроконтроллер подается ниже 3,3в. Сам микроконтроллер работает до 2,7в, потом уходит в защиту, судя по документации. Фатального разряда аккумулятора по идее не должно случится. В режиме ниже 3,3в продолжают работать только контроллер и зуммер, чип индикатора выключается, т.к. рассчитан на работу от 3,3-5в.

Индикатор подсоединил к Port D линии 2 (clk) и 3 (dio), на плате к 6 линии подсоединен светодиод. Первая линяя порта используется для отладчика, а 7 занята кнопкой сброса. К 0 линии подсоединил схему зуммера.

На Port C на линии 0-4 подсоединил кнопки, в исходниках программы можно посмотреть какая кнопка куда подсоединена. Почитал про энкодер, советовали в комментариях, но что-то пока решил оставить кнопки.

И собственно вопрос к профессионалам, как повысить отзывчивость на нажатие кнопок? Я понимаю, что существует дребезг контактов и логично сканировать кнопки за время большее этого дребезга контактов. Однако по нажатию нужно также успеть на индикаторе перерисовать показания. На саму плату с TM1637 на CLK & DIO напаяли огромные конденсаторы от помех, в итоге он работает не очень быстро, как бы мог в оригинале.

Генерация мелодий

В качестве хранения мелодий я выбрал стандарт RTTTL. Такой формат хранения мелодий активно использовался в таких не убиваемых телефонах как Nikia 3310. Ноты и октавы переопределил в файле rtttl.h, где у меня получилось 4 октавы по 12 нот, включая пониженные и повышенные полутона. Далее создал таблицу маппинга для нот в файле rtttl.c. Получилось что-то типа (последние 0 для выравнивания внутренней структуры).

/* Nota -> Freq */
const uint16_t freq[] = {
//  P   C     C#    D     D#    E     F     F#    G     G#    A     A#    B
    0,  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,  0,  0,  0,
    0,  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,  0,  0,  0,
    0,  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,  0,  0,  0,
    0,  1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 0,  0,  0,
    0,  2093, 2218, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 0,  0,  0,
};

Теперь у нас есть частоты и нам нужно сгенерировать синусоиду, для этого у нас есть TIM1, который может генерировать импульсы в формате PWM, либо можно с помощью ШИМ сгенерировать синусойду, где-то подобные проекты я видел. Моя нетерпеливость вынудила меня сгенерировать прямоугольный сигнал и для контроля над длительностью генерирования ноты я пошел по пути переключения порта вывода. Т.к. частота сигнала генерирования не будет выше 20 кГц, это предел слуха для большинства людей, то выставляем следующие параметры.

#define _FREQ_OUT   (20000 /* Hz */ * 2 /* Ticks */)
#define _FREQ_TICKS (_FREQ_OUT / 2)
#define _FREQ_LEN   (2 /* Ticks */ * 60 /* Per minute */ * 4)
#define _TIM1_PSC   ((SystemCoreClock / _FREQ_OUT) - 1)

За каждое колебание мы должны 2 раза переключить порт вывода, поэтому тиков должно быть 40 000 в секунду, таким образом выбираем начальные данные для таймера TIM1. Далее можно выставлять начальные параметры для таймера на основе необходимой частоты генерации, которую мы знаем исходя из октавы и ноты. Таймер будет работать с удвоенной частотой для вызова прерывания.

void setup(uint16_t f) {
  TIM_Cmd(TIM1, DISABLE);

  TIM_TimeBaseInitTypeDef TIMBase_InitStruct1 = {0};
  TIMBase_InitStruct1.TIM_ClockDivision = TIM_CKD_DIV1;
  TIMBase_InitStruct1.TIM_Prescaler = _TIM1_PSC;
  TIMBase_InitStruct1.TIM_Period = ((_FREQ_TICKS / f) - 1);
  TIMBase_InitStruct1.TIM_CounterMode = TIM_CounterMode_Up;
  TIMBase_InitStruct1.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM1, &TIMBase_InitStruct1);

  TIM_Cmd(TIM1, ENABLE);
}

Однако нужно еще проигрывать ноту какое-то время, для этого заводим счетчик и в переменную uint32_t ticks и по таймеру отсчитываем кол-во тиков. Длительность тиков и начальные настройки рассчитывает калькулятор.

void calculate() {
  uint16_t f = freq[melody->notes[pos].nota];
  if (f > 0) {
    length = (((uint32_t) f) * _FREQ_LEN * melody->notes[pos].duration) / DURATION / melody->tempo;
    speaker = 1;
    setup(f);
  } else {
    length = (((uint32_t) SILENT_FREQ) * _FREQ_LEN * melody->notes[pos].duration) / DURATION / melody->tempo;
    speaker = 0;
    setup(SILENT_FREQ);
  }
}

Если ноты нету, то используем виртуальную частоту таймера для отсчета времени. Этот момент мне хочется переделать, чтобы выставить на таймере задержку без виртуальной частоты. Не делайте как я, хотя это и работает, но очень некрасиво получилось.

Отлично, теперь у нас все есть, чтобы из динамика услышать мелодии формата RTTTL. Однако в формате RTTTL есть один нюанс, это добавление точки для добавления половины от длительности звучания ноты, что не позволяет простым образом записать в один байт информацию. По итогу я выбрал минимальную длительность звучания ноты, это 1/64 и в таких длительностях указал длительность звучания мелодии, т.е. для 1/64 - это будет 1, для 1/32 будет 2, для 1/32. это будет 3. Кстати, сами мелодии находятся тут melody.c уже в этом формате. Таким образом получается 8 байт на длительность и 8 байт на ноту + октаву. Такое компактное хранение данных получилось в итоге.

Теперь у нас есть все, для генерации мелодий в формате rtttl и вывода мелодии на динамик, добавляем 20 будильников и храним все это добро в оперативной памяти. К сожалению, в данном микроконтроллере нету энергонезависимой памяти для хранения настроек, как, например в AVR микроконтроллерах. Микроконтроллер ch32v003 позволяет перезаписывать flash блоками по 4кб, однако я пока до этого момента не дошел. Возможно в следующей доработке появится.

Для превращения квадратных импульсов в синусоиду я параллельно телефонному капсулю подпаял конденсатор, но судя графикам на простеньком осциллографе DSO328 получилось немного сгладить сигнал, хотя rc цепочка на базе транзистора была бы эффективнее. Возможно, можно, попробовать рассчитать RCL цепочку с капсулем, чтобы подогнать колебательный контур в резонанс, но это будет уже другая история.

Всем спасибо за комментарии. Не относитесь к проекту серьезно, это просто хобби.

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+1
Комментарии18

Публикации

Ближайшие события