Часы на телевидении, являются вторым по важности устройством, необходимым для точности вещания, а так-же синхронной работы нескольких подразделений. Особенностью таких часов является их точность и связность.
По сути часы на ТВ - это сеть, через которую таблички с цифровыми дисплеями, получают информацию о времени от центрального устройства, которое в свою очередь может синхронизироваться от внешних источников, типа GPS, NTP, ну или на худой конец - устанавливаться в ручную.
В основном, существующие на рынке часы основаны на LTC - по сути - кодированный 80-ти битный SMPTE таймкод, передающийся в виде симметричного аудио сигнала с частотой 48 кГц. Но не будем углубляться.
К этому проекту можно приделать и NTP сервер точного времени с GPS...
К этому проекту можно приделать и NTP сервер точного времени с GPS...
Пролог.
При создании проекта телеканала, передо мной встала задача выбора часов для компании. По скольку всё оборудование канала (вещательное) располагается в серверной - в одной комнате, то нет задачи выносить LTC за пределы серверной, как и нет задачи пользоваться LTC, так-как единственным устройством имеющим LTC вход/выход, является видеомикшер.
По этой причине отпадает надобность LTC в принципе. В данном проекте, часы больше нужны для визуального контроля времени. И тем более, существующие на рынке LTC часы стоят неоправданно дорого.
А что тогда использовать?
Первым в голову пришло применение микроконтроллера на базе Arduino. Дёшево и достаточно просто. Так и было принято решение о разработке. Надо заметить, что на данный момент, проект почти завершён, и полностью работоспособен. Но прийти к результату было не совсем просто.
Первоначально пришла в голову мысль - передавать данные между микроконтроллерами по SPI, но дальность связи при применении такого интерфейса - маловата. В теории длина всей трассы (провода) между дисплеями - 100 - 200 метров. Нужно было применить что-то универсальное и доступное к подключению ардуины, в том числе доступное в продаже. Самым подходящим оказался промышленный интерфейс CAN, и дисплеем - MAX7219. С этого и началась разработка.
Разработка
Первая задача сводилась к синхронизации часов Arduino и ПК (эфирной машины) через USB и выводу времени на цифровой дисплей MAX7219. За основу была взята плата Arduino Nano.
Для ардуино есть все необходимые примеры, при установке правильных библиотек.
И так!
Подгружаем в скетч ардуины библиотеку времени, библиотеку MAX7219, запускаем скетч в Processing, нажимаем на окошко Processing-a, и тут-же видим синхронизированное время. Даже с миллисекундами!
Таким образом мы видим на дисплее MAX7219 внутреннее время ардуины, которое было синхронизировано в момент нажатия клавиши мыши.
Так,- да не так!
Первое что бросилось в глаза - миллисекундная задержка времени от системных часов. Сначала возникло предположение что часы отображаемые на экране монитора отображаются с задержкой в связи с задержкой преобразования времени в картинку. Можно было бы этим пренебречь, до той поры пока клавиша мыши не была нажата ближе к концу окончания системной секунды, и задержка стала очевидной.
Загвоздка кроется в скетче Processing-a.
При нажатии кнопки мыши для синхронизации, Processing отправляет синхронизационный пакет времени в ардуину в формате Unix time. А это означает - что в момент времени, например в 11:59:59:995 была нажата клавиша мыши, и Processing отправит в Arduino время 11:59:59. Всё правильно. Только без миллисекунд. И визуально задержка будет почти секунду!
Пришлось исправить скетч Processing-a. По скольку миллисекунды в ардуину не передаются, а точность отображения времени от эфирного компьютера критична, было принято решение поправить скетч таким образом, чтобы после нажатия кнопки мыши (для синхронизации) скрипт ждал пока в системе не изменится секунда, и как только она изменяется, скрипт отправляет текущее время на Arduino. И это оказалось очень эффективно! Так-же в скрипт добавил индикацию времени, для наглядности.
Скетч Processing - TimeRTCSet.pde :
/**
* SyncArduinoClock.
*
* portIndex must be set to the port connected to the Arduino
*
* The current time is sent in response to request message from Arduino
* or by clicking the display window
*
* The time message is 11 ASCII text characters; a header (the letter 'T')
* followed by the ten digit system time (unix time)
*/
import processing.serial.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public static final short portIndex = 0; // select the com port, 0 is the first port
public static final String TIME_HEADER = "T"; //header for arduino serial time message
public static final char TIME_REQUEST = 7; // ASCII bell character
public static final char LF = 10; // ASCII linefeed
public static final char CR = 13; // ASCII linefeed
Serial myPort; // Create object from Serial class
void setup() {
size(200, 200);
println(Serial.list());
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this,Serial.list()[portIndex], 115200);
println(getTimeNow());
}
void draw()
{
background(200);
textSize(20);
textAlign(CENTER);
fill(0);
int m=minute(),s=second(),h=hour();
float sy=map(millis()%1000,0,1000,0,120);
text(h,50,30);
text(m,100,30);
text(s,150,30);
text("Click to send\nTime Sync\n", 0, 75, 200, 175);
if ( myPort.available() > 0) { // If data is available,
char val = char(myPort.read()); // read it and store it in val
if(val == TIME_REQUEST){
long t = getTimeNow();
sendTimeMessage(TIME_HEADER, t);
}
else
{
if(val == LF)
; //igonore
else if(val == CR)
println();
else
print(val); // echo everying but time request
}
}
if(millis()%1000==0){
sendTimeMessage( TIME_HEADER, getTimeNow());
}
}
void mousePressed() {
long millis = 1000 - System.currentTimeMillis() % 1000;
try {
Thread.sleep(millis);
}
catch (Exception e){/*Ignore*/}
sendTimeMessage( TIME_HEADER, getTimeNow());
}
void sendTimeMessage(String header, long time) {
String timeStr = String.valueOf(time);
myPort.write(header); // send header and time to arduino
myPort.write(timeStr);
myPort.write('\n');
}
long getTimeNow(){
// java time is in ms, we want secs
Date d = new Date();
Calendar cal = new GregorianCalendar();
long current = d.getTime()/1000;
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
long daylight = cal.get(cal.DST_OFFSET)/1000;
return current + timezone + daylight;
}
Скетч Arduino - NanoRtcLcdSet.ino - центральный модуль :
/*
* Этот скрипт выполняет функции - синхронизация времени с ПК, запись времени в RTC модуль, вывод времени на дисплей max7219,
* сравнение времени ардуины и RTC каждую секунду (коррекция), формирование из времени CAN сообщения и отправка в CAN шину.
* TimeRTCSet.pde
* example code illustrating Time library with Real Time Clock.
*
* RTC clock is set in response to serial port time message
* A Processing example sketch to set the time is included in the download
* On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
*/
#include <mcp_can.h>
#include <SPI.h>
#include <TimeLib.h>
#include <Wire.h>
#include <DS3231RTC.h> // a basic DS1307 library that returns time as a time_t
#include "LedControl.h" // подключается библиотека MAX7219
// следующий абзац - для диспля.. the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int LED=8;
boolean ledON=1;
LedControl lc=LedControl(6,8,9,1); //Tells LedControl where our hardware is connected.
//(6,8,9,1) for nano with busy spi bus
int hourTen; //tens of hours
int hourUnit; //units of hours
int minTen; // you get the idea..
int minUnit;
int secTen;
int secUnit;
int msTen;
int msUnit;
int sec;
int rtcSec;
unsigned long timer =0;
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
#define TIME_HEADER "T" // Header tag for serial time sync message
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
void setup() {
Serial.begin(115200);
pinMode(LED,OUTPUT); //для дисплея
// while (!Serial) ; // Needed for Leonardo only
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
//setup для can шины
while (CAN_OK != CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
{
Serial.println("CAN BUS Shield init fail");
Serial.println(" Init CAN BUS Shield again");
delay(100);
}
Serial.println("CAN BUS Shield init ok!");
// setup для дисплея MAX7219
lc.shutdown(0,false); // Wake up the MAX 72xx controller
lc.setIntensity(0,8); // Set the display brightness
lc.clearDisplay(0); //Clear the display
}
void loop()
{
//ожидаем сообщения синхронизации времени из COM порта
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
RTC.set(t); // set the RTC and the system time to the received value
setTime(t); // устанавливаем время в ардуину
}
}
rtcSec = RTC.get(); // зпаисать в переменную rtcSec, время из RTC модуля - с точностью до секунды
delay(1) ;// ждём 1 мс
displayTime(); // показать время ардуины на светодиодном дисплее - это тоже занимает время
if (rtcSec != RTC.get()) { //сравниваем время записанное в переменную rtcSec с текущим временем RTC, и если оно не равно, выполнить функции
setSyncProvider(RTC.get); // the function to get the time from the RTC - коррекция времени ардуины из RTC
}
canMsg(); // формирование сообщения для CAN шины, из времени ардуины
digitalClockDisplay() ; // время в com порт
}
void canMsg(){
// формирование сообщения для CAN шины, из времени ардуины
unsigned char stmp[3] = {hour(), minute(), second()};//, 3, 4, 5, 6, 7}; // 3-7 - запасные ячейки
// send data: id = 0x00, standrad frame, data len = 3, stmp: data buf
CAN.sendMsgBuf(0x00, 0, 3, stmp);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
printDigits(millis()%100);
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}
// Displays the time on our LEDs - отображаем время на дисплее
void displayTime() {
// timer = millis(); // reset the serial comms timer
hourUnit = (hour()%10);
hourTen = ((hour()/10)%10);
minUnit = (minute()%10);
minTen = ((minute()/10)%10);
secUnit = (second()%10);
secTen = ((second()/10)%10);
msUnit = (millis()%10);
msTen = ((millis()/10)%10);
lc.setDigit (0,7,hourTen,false);
lc.setDigit (0,6,hourUnit,false);
lc.setDigit (0,5,minTen,false);
lc.setDigit (0,4,minUnit,false);
lc.setDigit (0,3,secTen,false);
lc.setDigit (0,2,secUnit,false);
lc.setDigit (0,1,msTen,false);
lc.setDigit (0,0,msUnit,false);
}
Скетч Arduino - DisplayCanReceive.ino клиент-дисплей :
// Этот скрипт принимает сообщение по CAN шине (через MCP2515), и если оно принято,
// отправляет в последовательный порт инфомацию о принятом сообщении,
// и информацию из buf [0,1,2], и отображает инфомацию
// из buf [0,1,2] на led дисплей max7219.
#include <SPI.h> // подключается библиотека SPI
#include "mcp_can.h" // подключается библиотека SPI для MCP
#include "LedControl.h" // подключается библиотека MAX7219
// Далее - установки инициализации переменных для CAN шины и MAX7219
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int LED=8;
boolean ledON=1;
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
float br=(5 - analogRead(A0)) * 2.5 + 5; //делитель установить 12в на 5в, в итоге если вход 12 и выше - яркость 5 ; если 10 то яркость 8
LedControl lc=LedControl(6,8,9,1); //Tells LedControl where our hardware is connected.(6,8,9,1) for nano with busy spi bus
unsigned long timer =0;
void setup()
{
// setup для инициализации MCP2515
Serial.begin(115200);
pinMode(LED,OUTPUT);
while (CAN_OK != CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
{
Serial.println("CAN BUS Shield init fail");
Serial.println(" Init CAN BUS Shield again");
delay(100);
}
Serial.println("CAN BUS Shield init ok!");
// setup для дисплея MAX7219
lc.shutdown(0,false); // Wake up the MAX 72xx controller
lc.setIntensity(0,br); // Set the display brightness
lc.clearDisplay(0); //Clear the display
}
void loop()
{
unsigned char len = 0;
unsigned char buf[8];
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if data coming
{
CAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf
unsigned char canId = CAN.getCanId();
// выводим инфу в последовательный порт
Serial.println("-----------------------------");
Serial.println("get data from ID: ");
Serial.println(canId);
Serial.print(buf[0]);
Serial.print(":");
Serial.print(buf[1]);
Serial.print(":");
Serial.print(buf[2]);
Serial.println();
//и выводим на led дисплей max7219
lc.setDigit (0,7,(buf[0]/10)%10,false);
lc.setDigit (0,6,buf[0]%10,false);
lc.setDigit (0,5,(buf[1]/10)%10,false);
lc.setDigit (0,4,buf[1]%10,false);
lc.setDigit (0,3,(buf[2]/10)%10,false);
lc.setDigit (0,2,buf[2]%10,false);
}
// displayTime(); // показать время на светодиодном дисплее
}
// Displays the time on our LEDs - отображаем время на дисплее
/*********************************************************************************************************
Конец
*********************************************************************************************************/
C программной частью закончили - переходим к практике.
Центральный модуль.
Далее рабочие скетчи:
Скетч Processing - TimeRTCSet.pde :
/**
* SyncArduinoClock.
*
* portIndex must be set to the port connected to the Arduino
*
* The current time is sent in response to request message from Arduino
* or by clicking the display window
*
* The time message is 11 ASCII text characters; a header (the letter 'T')
* followed by the ten digit system time (unix time)
*/
import processing.serial.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public static final short portIndex = 0; // select the com port, 0 is the first port
public static final String TIME_HEADER = "T"; //header for arduino serial time message
public static final char TIME_REQUEST = 7; // ASCII bell character
public static final char LF = 10; // ASCII linefeed
public static final char CR = 13; // ASCII linefeed
Serial myPort; // Create object from Serial class
void setup() {
size(200, 200);
println(Serial.list());
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this,Serial.list()[portIndex], 115200);
println(getTimeNow());
}
void draw()
{
background(200);
textSize(20);
textAlign(CENTER);
fill(0);
int m=minute(),s=second(),h=hour();
float sy=map(millis()%1000,0,1000,0,120);
text(h,50,30);
text(m,100,30);
text(s,150,30);
text("Click to send\nTime Sync\n", 0, 75, 200, 175);
if ( myPort.available() > 0) { // If data is available,
char val = char(myPort.read()); // read it and store it in val
if(val == TIME_REQUEST){
long t = getTimeNow();
sendTimeMessage(TIME_HEADER, t);
}
else
{
if(val == LF)
; //igonore
else if(val == CR)
println();
else
print(val); // echo everying but time request
}
}
if(millis()%1000==0){
sendTimeMessage( TIME_HEADER, getTimeNow());
}
}
void mousePressed() {
long millis = 1000 - System.currentTimeMillis() % 1000;
try {
Thread.sleep(millis);
}
catch (Exception e){/*Ignore*/}
sendTimeMessage( TIME_HEADER, getTimeNow());
}
void sendTimeMessage(String header, long time) {
String timeStr = String.valueOf(time);
myPort.write(header); // send header and time to arduino
myPort.write(timeStr);
myPort.write('\n');
}
long getTimeNow(){
// java time is in ms, we want secs
Date d = new Date();
Calendar cal = new GregorianCalendar();
long current = d.getTime()/1000;
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
long daylight = cal.get(cal.DST_OFFSET)/1000;
return current + timezone + daylight;
}
Скетч Arduino - NanoRtcLcdSet.ino - центральный модуль :
/*
* Этот скрипт выполняет функции - синхронизация времени с ПК, запись времени в RTC модуль, вывод времени на дисплей max7219,
* сравнение времени ардуины и RTC каждую секунду (коррекция), формирование из времени CAN сообщения и отправка в CAN шину.
* TimeRTCSet.pde
* example code illustrating Time library with Real Time Clock.
*
* RTC clock is set in response to serial port time message
* A Processing example sketch to set the time is included in the download
* On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
*/
#include <mcp_can.h>
#include <SPI.h>
#include <TimeLib.h>
#include <Wire.h>
#include <DS3231RTC.h> // a basic DS1307 library that returns time as a time_t
#include "LedControl.h" // подключается библиотека MAX7219
// следующий абзац - для диспля.. the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int LED=8;
boolean ledON=1;
LedControl lc=LedControl(6,8,9,1); //Tells LedControl where our hardware is connected.
//(6,8,9,1) for nano with busy spi bus
int hourTen; //tens of hours
int hourUnit; //units of hours
int minTen; // you get the idea..
int minUnit;
int secTen;
int secUnit;
int msTen;
int msUnit;
int sec;
int rtcSec;
unsigned long timer =0;
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
#define TIME_HEADER "T" // Header tag for serial time sync message
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
void setup() {
Serial.begin(115200);
pinMode(LED,OUTPUT); //для дисплея
// while (!Serial) ; // Needed for Leonardo only
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
//setup для can шины
while (CAN_OK != CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
{
Serial.println("CAN BUS Shield init fail");
Serial.println(" Init CAN BUS Shield again");
delay(100);
}
Serial.println("CAN BUS Shield init ok!");
// setup для дисплея MAX7219
lc.shutdown(0,false); // Wake up the MAX 72xx controller
lc.setIntensity(0,8); // Set the display brightness
lc.clearDisplay(0); //Clear the display
}
void loop()
{
//ожидаем сообщения синхронизации времени из COM порта
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
RTC.set(t); // set the RTC and the system time to the received value
setTime(t); // устанавливаем время в ардуину
}
}
rtcSec = RTC.get(); // зпаисать в переменную rtcSec, время из RTC модуля - с точностью до секунды
delay(1) ;// ждём 1 мс
displayTime(); // показать время ардуины на светодиодном дисплее - это тоже занимает время
if (rtcSec != RTC.get()) { //сравниваем время записанное в переменную rtcSec с текущим временем RTC, и если оно не равно, выполнить функции
setSyncProvider(RTC.get); // the function to get the time from the RTC - коррекция времени ардуины из RTC
}
canMsg(); // формирование сообщения для CAN шины, из времени ардуины
digitalClockDisplay() ; // время в com порт
}
void canMsg(){
// формирование сообщения для CAN шины, из времени ардуины
unsigned char stmp[3] = {hour(), minute(), second()};//, 3, 4, 5, 6, 7}; // 3-7 - запасные ячейки
// send data: id = 0x00, standrad frame, data len = 3, stmp: data buf
CAN.sendMsgBuf(0x00, 0, 3, stmp);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
printDigits(millis()%100);
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}
// Displays the time on our LEDs - отображаем время на дисплее
void displayTime() {
// timer = millis(); // reset the serial comms timer
hourUnit = (hour()%10);
hourTen = ((hour()/10)%10);
minUnit = (minute()%10);
minTen = ((minute()/10)%10);
secUnit = (second()%10);
secTen = ((second()/10)%10);
msUnit = (millis()%10);
msTen = ((millis()/10)%10);
lc.setDigit (0,7,hourTen,false);
lc.setDigit (0,6,hourUnit,false);
lc.setDigit (0,5,minTen,false);
lc.setDigit (0,4,minUnit,false);
lc.setDigit (0,3,secTen,false);
lc.setDigit (0,2,secUnit,false);
lc.setDigit (0,1,msTen,false);
lc.setDigit (0,0,msUnit,false);
}
Скетч Arduino - DisplayCanReceive.ino клиент-дисплей :
// Этот скрипт принимает сообщение по CAN шине (через MCP2515), и если оно принято,
// отправляет в последовательный порт инфомацию о принятом сообщении,
// и информацию из buf [0,1,2], и отображает инфомацию
// из buf [0,1,2] на led дисплей max7219.
#include <SPI.h> // подключается библиотека SPI
#include "mcp_can.h" // подключается библиотека SPI для MCP
#include "LedControl.h" // подключается библиотека MAX7219
// Далее - установки инициализации переменных для CAN шины и MAX7219
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int LED=8;
boolean ledON=1;
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
float br=(5 - analogRead(A0)) * 2.5 + 5; //делитель установить 12в на 5в, в итоге если вход 12 и выше - яркость 5 ; если 10 то яркость 8
LedControl lc=LedControl(6,8,9,1); //Tells LedControl where our hardware is connected.(6,8,9,1) for nano with busy spi bus
unsigned long timer =0;
void setup()
{
// setup для инициализации MCP2515
Serial.begin(115200);
pinMode(LED,OUTPUT);
while (CAN_OK != CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
{
Serial.println("CAN BUS Shield init fail");
Serial.println(" Init CAN BUS Shield again");
delay(100);
}
Serial.println("CAN BUS Shield init ok!");
// setup для дисплея MAX7219
lc.shutdown(0,false); // Wake up the MAX 72xx controller
lc.setIntensity(0,br); // Set the display brightness
lc.clearDisplay(0); //Clear the display
}
void loop()
{
unsigned char len = 0;
unsigned char buf[8];
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if data coming
{
CAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf
unsigned char canId = CAN.getCanId();
// выводим инфу в последовательный порт
Serial.println("-----------------------------");
Serial.println("get data from ID: ");
Serial.println(canId);
Serial.print(buf[0]);
Serial.print(":");
Serial.print(buf[1]);
Serial.print(":");
Serial.print(buf[2]);
Serial.println();
//и выводим на led дисплей max7219
lc.setDigit (0,7,(buf[0]/10)%10,false);
lc.setDigit (0,6,buf[0]%10,false);
lc.setDigit (0,5,(buf[1]/10)%10,false);
lc.setDigit (0,4,buf[1]%10,false);
lc.setDigit (0,3,(buf[2]/10)%10,false);
lc.setDigit (0,2,buf[2]%10,false);
}
// displayTime(); // показать время на светодиодном дисплее
}
// Displays the time on our LEDs - отображаем время на дисплее
/*********************************************************************************************************
Конец
*********************************************************************************************************/
C программной частью закончили - переходим к практике.
Чтобы долго не ломать голову, центральный модуль было решено собрать в корпусе старого дохлого свитча.
В этом корпусе разместились устройства:
Arduino nano
Max7219
LTC модуль ds3132
CAN модуль
Блок питания 19 В / 4 А
Стабилизатор на 5 В
Выглядит в корпусе это так:
Сзади - 2 гнезда rj-45 (они параллельны), выход CAN, USB - для подключения к компьютеру и синхронизации времени.
Сзади - 2 гнезда rj-45 (они параллельны), выход CAN, USB - для подключения к компьютеру и синхронизации времени.
Дисплей - клиент.
Тут дело обстоит несколько сложнее.
Дисплей собран на светодиодной ленте красного цвета, а сегменты образуются из-за маски чёрного цвета.
...Процесс сборки дисплея...
Сначала определяются размеры сегментов светодиодной ленты, затем в чертёжной программе рисуем цифры так, как хотим видеть в итоге.
Получилась основа 17.5 см * 71 см - это с учётом толщины стенок корпуса - рамки.
Первым делом отдаём чертёж в полиграфический центр - для вырезания маски из чёрной плёнки (1:1) Маску вырезать зеркально. Так же понадобится белая самоклейка.
Всё это клеится на стекло размером 17.5см * 71см.
Стекло нужно хорошо очистить чтобы не оставалось разводов.
Сначала размечается маска по распечатанному чертежу (ставятся отметки на углах чертежа и маски). Маска с чертежом совмещается через вырезанные сегменты цифр. Далее на стекло клеится белая самоклейка. Клеить лучше вдвоём и с помощью воды. Так будет проще выгонять пузыри. Воду лучше нанести на стекло с помощью распылителя, а выгонять капли-пузыри пластиковой картой.
Пузырей быть не должно. Отрезаем всё лишнее.
Клеим маску. Есть особенность: нужно клеить стекло к плёнке.
Отрываем клеящий слой от маски, укладываем липким слоем вверх, поливаем водой.
На весу совмещаем намеченные углы маски со стеклом, и аккуратно накладываем стекло белой самоклейкой вниз на маску. Отрезаем лишнее.
Готово.
Изготавливаем корпус для получившейся заготовки из деревянного бруса 20*30 мм. Делаем пропилы на циркулярной пиле. Стекло крепим внутрь рамки без возможности регулировки и замены. Важно чтобы расстояние пропила между стеклом и задней стенкой было 22 мм. Высота перегородок 20 мм + проводка ~2мм.
В результате получим рамку с маской.
Клеим перегородки из белого ПВХ уголка 20*20 мм. Разрезаем его вдоль по углу, получаем полоски. Из них и соорудим "колодцы" для света.
Перегородки клеить плотно и строго по маске. Процесс хоть и кропотливый, но занимает не меньше часа.
Готовим заднюю стенку - основу.
Распечатываем чертёж 1:1, накладываем на лицевую сторону нашей рамки. Я распечатал чертёж из 3-х листов, и мне пришлось делать подгон листов под маску, подсвечивая снизу лампой.
Когда чертёж подогнан, его нужно склеить со стеклом маленьким кусочком двухстороннего скотча. Чертёж должен быть полностью совмещён сегментами на просвет с маской.
Загибаем внешние углы чертежа по рамке.
Отклеиваем чертёж, переворачиваем рамку, накладываем чертёж с задней стороны совмещая углы рамки, и загибаем их в другую сторону.
Заднюю стенку из двп изготавливаем в размер стекла 17,5*71 см. Приклеиваем вертикально по середине к ней полосу двухстороннего скотча. Защитный слой скотча разрезать пополам и прилепить на края этого-же скотча, оставив в середине липкий слой.
Аккуратно и ровно приклеиваем заднюю стенку к нашему чертежу установленному в заготовку - рамку. Загибаем края чертежа вокруг приклеенной дсп, вынимаем дсп с чертежом, переворачиваем и приглаживаем середину.
На этом этапе нужно быть особенно точным, чтобы сегменты совпадали наверняка.
Снимаем защитный слой скотча, и доклеиваем середину. Дальше -проще. лепим полосы 2ст. скотча вдоль доски и приклеиваем чертёж целиком.
Дальше работаем только с задней стенкой.
Лепим светодиодные сегменты на чертёж и запаиваем провода.
Особенность следующая - в моём случае с дисплейным модулем 3641AH - с общим анодом - спаивать сегменты нужно начиная с +, и сегменты при приклеивании нужно лепить + в центр для удобства пайки. А ещё, нужно выпаять резистор из светодиодных сегментов и поставить на его место перемычку! На каждом сегменте.
Дальше придётся попотеть с электроникой.
Max7219 не может работать со светодиодной лентой с напряжением 12 вольт. Дисплей у этой микросхемы не рассчитан на такие задачи.
По этому, нужно изготовить драйвер для светодиодной ленты. На официальном сайте Maxim есть предложение по сборке драйвера. Опираясь на предложенные схемы и оценивая то что есть в местном радиомаге, разработал следующую схему драйвера.
По скольку у меня на плате max7219 был установлен дисплей 3641AH - с общим анодом то анодных цепеи на дисплей 4 а сегментных - 8. Работает схема последовательно по цифрам - цифры загораются по порядку с первого дисплея 1-2-3-4, далее второй дисплей 1-2-3-4. Нам нужно 6 цифр, значит 6 анодных цепей и 7 сегментных. Не буду углубляться в то - как работает схема - заострю внимание что полевик должен быть с логическим входом.
Делаем плату драйвера.
Тут проще - рисуем схему в Sprint Layout - отдаём текстолит и чертёж в полиграфию, и за 100 рублей получаем плату готовую к травлению. Плату нужно делать небольших размеров, чтобы она влезла в корпус. Например 10*5 см.
Из платы с max7219 выпаять дисплеи - вместо них установить шпильки проводники и припаять к готовой и собранной плате микросхемой вверх.
Собираем в корпус и отдыхаем!) Наслаждаемся готовым изделием и готовимся к запуску телестудии...
Первым делом отдаём чертёж в полиграфический центр - для вырезания маски из чёрной плёнки (1:1) Маску вырезать зеркально. Так же понадобится белая самоклейка.
Всё это клеится на стекло размером 17.5см * 71см.
Стекло нужно хорошо очистить чтобы не оставалось разводов.
Сначала размечается маска по распечатанному чертежу (ставятся отметки на углах чертежа и маски). Маска с чертежом совмещается через вырезанные сегменты цифр. Далее на стекло клеится белая самоклейка. Клеить лучше вдвоём и с помощью воды. Так будет проще выгонять пузыри. Воду лучше нанести на стекло с помощью распылителя, а выгонять капли-пузыри пластиковой картой.
Клеим маску. Есть особенность: нужно клеить стекло к плёнке.
Отрываем клеящий слой от маски, укладываем липким слоем вверх, поливаем водой.
На весу совмещаем намеченные углы маски со стеклом, и аккуратно накладываем стекло белой самоклейкой вниз на маску. Отрезаем лишнее.
Готово.
Изготавливаем корпус для получившейся заготовки из деревянного бруса 20*30 мм. Делаем пропилы на циркулярной пиле. Стекло крепим внутрь рамки без возможности регулировки и замены. Важно чтобы расстояние пропила между стеклом и задней стенкой было 22 мм. Высота перегородок 20 мм + проводка ~2мм.
В результате получим рамку с маской.
Клеим перегородки из белого ПВХ уголка 20*20 мм. Разрезаем его вдоль по углу, получаем полоски. Из них и соорудим "колодцы" для света.
Перегородки клеить плотно и строго по маске. Процесс хоть и кропотливый, но занимает не меньше часа.
Готовим заднюю стенку - основу.
Распечатываем чертёж 1:1, накладываем на лицевую сторону нашей рамки. Я распечатал чертёж из 3-х листов, и мне пришлось делать подгон листов под маску, подсвечивая снизу лампой.
Когда чертёж подогнан, его нужно склеить со стеклом маленьким кусочком двухстороннего скотча. Чертёж должен быть полностью совмещён сегментами на просвет с маской.
Загибаем внешние углы чертежа по рамке.
Отклеиваем чертёж, переворачиваем рамку, накладываем чертёж с задней стороны совмещая углы рамки, и загибаем их в другую сторону.
Аккуратно и ровно приклеиваем заднюю стенку к нашему чертежу установленному в заготовку - рамку. Загибаем края чертежа вокруг приклеенной дсп, вынимаем дсп с чертежом, переворачиваем и приглаживаем середину.
На этом этапе нужно быть особенно точным, чтобы сегменты совпадали наверняка.
Снимаем защитный слой скотча, и доклеиваем середину. Дальше -проще. лепим полосы 2ст. скотча вдоль доски и приклеиваем чертёж целиком.
Дальше работаем только с задней стенкой.
Лепим светодиодные сегменты на чертёж и запаиваем провода.
Особенность следующая - в моём случае с дисплейным модулем 3641AH - с общим анодом - спаивать сегменты нужно начиная с +, и сегменты при приклеивании нужно лепить + в центр для удобства пайки. А ещё, нужно выпаять резистор из светодиодных сегментов и поставить на его место перемычку! На каждом сегменте.
Дальше придётся попотеть с электроникой.
Max7219 не может работать со светодиодной лентой с напряжением 12 вольт. Дисплей у этой микросхемы не рассчитан на такие задачи.
По этому, нужно изготовить драйвер для светодиодной ленты. На официальном сайте Maxim есть предложение по сборке драйвера. Опираясь на предложенные схемы и оценивая то что есть в местном радиомаге, разработал следующую схему драйвера.
По скольку у меня на плате max7219 был установлен дисплей 3641AH - с общим анодом то анодных цепеи на дисплей 4 а сегментных - 8. Работает схема последовательно по цифрам - цифры загораются по порядку с первого дисплея 1-2-3-4, далее второй дисплей 1-2-3-4. Нам нужно 6 цифр, значит 6 анодных цепей и 7 сегментных. Не буду углубляться в то - как работает схема - заострю внимание что полевик должен быть с логическим входом.
Делаем плату драйвера.
Тут проще - рисуем схему в Sprint Layout - отдаём текстолит и чертёж в полиграфию, и за 100 рублей получаем плату готовую к травлению. Плату нужно делать небольших размеров, чтобы она влезла в корпус. Например 10*5 см.
Из платы с max7219 выпаять дисплеи - вместо них установить шпильки проводники и припаять к готовой и собранной плате микросхемой вверх.
Дальше - самое лёгкое - закоммутировать модули ардуины, уложить проводку, выпилить лобзиком 2 отверстия под разъём RJ-45 (по которому приходит питание и CAN сообщения). Остаётся исхитриться уместить в оставшееся место радиаторы для стабилизаторов 5 и 12 вольт.