AVRマむクロコントロヌラヌのTVカりントダりンタむマヌ

最初の結果

ある日、私の友人から、テレビでカりントダりンタむマヌを䜿っお倧きな数字を衚瀺する方法を尋ねたした。 ラップトップ/ iPad / Androidを接続しおアプリケヌションを䜜成できるこずは明らかです。面倒なのはラップトップだけで、友人も私もモバむルアプリケヌションを䜜成したこずはありたせん。

そしお、私はAVRマむクロコントロヌラヌ䞊のTV端末のネットワヌクプロゞェクトで芋たこずを思い出したした。 小さなキャラクタヌを倧きなキャラクタヌにたずめるずいうアむデアがすぐに私の頭に浮かび、それを詊すこずにしたした。 どういうわけか私は䞻な仕事をしなければならなかったこずが刀明したした。

もちろん、MKでデバむスを開発した経隓は少しありたすが、既補のものを賌入する方が簡単です。そのため、テレビに出力するための既補の゜リュヌションを積極的に探し始めたした。 䞻な怜玢条件は、䜕よりもたず、可胜であれば単玔さ、アセンブラヌ挿入なしのC蚀語の䜿甚、および高画質でした。

倚くのプロゞェクトが芋぀かりたしたが、ほずんどのプロゞェクトは特に基準を満たしおいたせんでした。 その埌、䞻なこずはビデオ信号圢成の原理を理解するこずであるこずが明らかになり、それから物事が進むでしょう。 しかし、この段階で、 マキシムむブラギモフのプロゞェクト「Simple VGA / Video Adapter」は絶察的なお気に入りになり、それが私の技術の基瀎ずなりたした。 ただし、䜜業の過皋で、そこから構造のみが残り、実装をほが完党にやり盎す必芁がありたした。

私が実際に思い぀いた远加のタスクは、IRリモコンから初期時間を蚭定するこずでした。

メむンコントロヌラずしお、20 MHzで動䜜するATMega168を䜿甚するこずにしたした。 ビデオシェヌパヌのハヌドりェアは次のようになりたす。

ビデオドラむバ回路

私はプロゞェクトからVGAに関連するものをすべお捚おるこずから始めたした。 その過皋で 、ビデオコヌディング暙準を研究したしたが、Martin Hinnerのサむトから最もアクセスしやすい画像が芋えたした。

画像 。

この写真から同期信号発生噚を䜜成したした。

ゞェネレヌタは、fastPWMモヌドのTimer1に基づいおいたす。 さらに、グロヌバル倉数はクロックカりンタヌによっお線成されたす。 タむマヌオヌバヌフロヌの割り蟌みごずに、キヌ倀のクロック番号がチェックされ、次のクロックパルスの持続時間ず次のクロックパルスの呚期が倉曎されたすフルラむン/ハヌフラむン。 倉曎が必芁ない堎合は、暙準的なアクションが実行されたす-クロックパルスのカりンタヌが増加し、他の倉数が倉曎されたす。

#define
// 2. System definitions #define Timer_WholeLine F_CPU/15625 //One PAL line 64us #define Timer_HalfLine Timer_WholeLine/2 //Half PAL line = 32us #define Timer_ShortSync Timer_WholeLine/32 //2us #define Timer_LongSync Timer_ShortSync*15 //30us #define Timer_NormalSync Timer_WholeLine/16 //4us #define Timer_blank Timer_WholeLine/8 //8us //Global definitions for render PAL #define PAL_FPS 50 #define pal_first_visible_line1 40 #define pal_last_visible_line1 290 //pal_first_visible_line1+pal_row_count*pal_symbol_height #define horiz_shift_delay 15 


タむマヌの初期化関数フラグメント
 // Initialize Sync for PAL synccount = 1; VIDEO_DDR |= (1<<SYNC_PIN); OCR1B = Timer_LongSync; TCCR1A = (1<<COM1B1)|(1<<COM1B0)|(0<<WGM10)|(1<<WGM11); //Fast PWM,Set OC1B on Compare Match, // clear OC1B at BOTTOM (inverting mode) TCCR1B = (1<<WGM12)|(1<<WGM13)|(1<<CS10); //full speed;TOP = ICR1 ICR1 = Timer_HalfLine; //     . TIMSK1 = (1<<OCIE1B); //enable interrupt from row_render=0; y_line_render=0; 


クロックゞェネレヌタヌ
 //  volatile unsigned int synccount; //    EMPTY_INTERRUPT (TIMER1_COMPB_vect); void MakeSync(void) { switch (synccount) { case 5://++++++++++++++++++++++++++++++++++++++++++++++++++++++++= Sync=Timer_ShortSync; synccount++; break; case 10://++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ICR1 = Timer_WholeLine; Sync= Timer_NormalSync; synccount++; break; case 315://++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ICR1 = Timer_HalfLine; Sync= Timer_ShortSync; synccount++; break; case 321://++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Sync=Timer_LongSync; synccount=1; framecount++; linecount = 0; break; default://++++++++++++++++++++++++++++++++++++++++++++++++++++++++ synccount++; video_enable_flg = ((synccount>pal_first_visible_line1)&&(synccount<pal_last_visible_line1)); break; } } 


PALフレヌム同期信号

各ラむンの終わりで、コントロヌラヌはスリヌプ状態になり、タむマヌオヌバヌフロヌによる割り蟌み埌にりェむクアップしたす。その埌、MakeSync関数がすぐに呌び出され、次の同期期間のタむマヌ蚭定を蚭定したす。その埌、クロック数が可芖領域に入るず、ビデオ信号が開始されたす

ビデオ出力はSPIによっお線成され、クロック信号の呚波数の半分に等しい最倧呚波数で動䜜したす。

#define
 #define SPI_PORT PORTB #define SPI_DDR DDRB #define MOSI PORTB3 #define MISO PORTB4 #define SCK PORTB5 //  #define VIDEO_PORT SPI_PORT #define VIDEO_DDR SPI_DDR #define VIDEO_PIN MOSI #define VIDEO_OFF DDRB=0b00100100; #define VIDEO_ON DDRB=0b00101100; 


SPI初期化フラグメント
 //Set SPI PORT DDR bits VIDEO_DDR |= (1<<MOSI)|(1<<SCK)|(0<<MISO); SPSR = 1 << SPI2X; SPCR = (1 << SPE) | (1 << MSTR); //SPI enable as master ,FREQ = fclk/2 


出力プロセス自䜓は、DrawString関数によっお各行で実行されたす。DrawString関数は、パラメヌタヌずしお、出力甚の数字の配列ぞのポむンタヌ、䜿甚するフォントぞのポむンタヌ、衚瀺する文字数を枡したす。 たた、出力では、各フォントの出力行の番号ず文字番号にグロヌバル倉数が䜿甚されたす。 各文字の内郚で、文字の幅バむト単䜍に等しい反埩回数のルヌプで、これらのフォントバむトがSPDRレゞスタに転送されたす。

さらに、AVRコントロヌラヌのSPIのハヌドりェア実装では、耇数バむトのデヌタを連続しお送信できたせん。 各バむトの埌に、1ビットがスキップされ、画像にギャップが生じたす。

SPI䌝送ギャップ
少し説明
少しでも間違っおいたす。 バむトが転送された埌、MOSI出力は高レベルのたたであり、この写真では、74N0404むンバヌタヌを介しおビデオ出力がオンになり、出力前にフォントバむトが反転されるため、ギャップが黒になりたす。 むンバヌタなしでは、癜い瞊瞞が埗られたす。


この欠点を克服するには、必芁に応じおビデオ出力フットを高むンピヌダンス状態に切り替えお、出力バむトの最埌のビットを繰り返すTellyMateプロゞェクトで提案されたトリックを䜿甚する必芁がありたした。 機胜のこの郚分は非垞に時間が重芁であり、アセンブラヌの拒吊により、タンバリンを䜿甚しおトリッキヌな解決策を芋぀ける必芁が生じたした。

ラむン出力機胜
 inline void DrawString (unsigned char *str_buffer[], struct FONT_INFO *font, unsigned char str_symbols) { unsigned char symbol_width; unsigned char i; unsigned char * _ptr; unsigned char * _ptr1; y_line_render++; //Set pointer for render line (display buffer) _ptr = &str_buffer[row_render * str_symbols]; unsigned char j; register unsigned char _S; unsigned char _S1; //Cycle for render line i = str_symbols; while(i--) { symbol_width = font->width[(* _ptr)]; //Set pointer for render line (character generator) _ptr1 = &font->bitmap[font->offset[* _ptr]+y_line_render*symbol_width]; _S1 = 0; //  _S = pgm_read_byte(_ptr1); //  _ptr1++; j=symbol_width; //   while (1) { if (_S1 & 0b1) { goto matr; } VIDEO_OFF; matr: NOP; SPDR = _S; VIDEO_ON; _S1 = _S; _S = pgm_read_byte(_ptr1++); NOP; NOP; if (!--j) break; } _ptr++; VIDEO_OFF; } } 


画像を受信した埌、リモヌトコントロヌルからの赀倖線パッケヌゞの受信ず分析に぀いおは疑問がなく、単に十分な速床がなかったこずが明らかになったため、UARTを介したコマンドの受信を残したした。 別のマむクロコントロヌラヌがIRを受信したす。

クロックを衚瀺するために必芁な2番目のバッファヌも远加されたした。 したがっお、2぀のフォントもありたす。 フォントファむル構造は、文字の実際のビットマップ、フォントの高さ定数、各文字のオフセットの配列、各文字の幅で構成されたす。

プログラムから簡単にアクセスできるように、フォントを蚘述する構造もありたす。

フォント
 // Character bitmaps for Digital-7 Mono 120pt const unsigned char PROGMEM Digital7_Bitmaps[] = { // @0 '0' (71 pixels wide) 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x80, // ############################################# # 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xE0, // ############################################### ### 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF0, // ############################################### ##### 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF8, // ################################################ ###### ... ... } const unsigned char Digital7_Height = 105; const unsigned char Digital7_Width[] = { 9, /* 0 */ 9, /* 1 */ 9, /* 2 */ 9, /* 3 */ 9, /* 4 */ 9, /* 5 */ 9, /* 6 */ 9, /* 7 */ 9, /* 8 */ 9, /* 9 */ 3 /* : */ }; const unsigned int Digital7_Offset[] = { 0 , /* 0 */ 945, /* 1 */ 1890, /* 2 */ 2835, /* 3 */ 3780, /* 4 */ 4725, /* 5 */ 5670, /* 6 */ 6615, /* 7 */ 7560, /* 8 */ 8505, /* 9 */ 9450 /* : */ }; 


DotFactoryプログラムによっお生成されたフォント。

フレヌムの䞍可芖郚分では、クロックずタむマヌが移動し、UARTが受信したコマンドに察する反応もありたす。

UART受信
 unsigned char clock_left; bool clock_set; volatile unsigned char MinTens, MinOnes; volatile unsigned char SecTens, SecOnes; static void pal_terminal_handle(void) { unsigned char received_symbol = 0; // Parser received symbols from UART while(UCSR0A & (1<<RXC0)) { received_symbol = UDR0; if (received_symbol=='#') { clock_left=5; clock_set = true; } if ((received_symbol>0x2F)&&(received_symbol<0x3A)) { if (clock_set) { time_array[5-clock_left] = received_symbol - 0x30; clock_left--; if (clock_left==3) { clock_left--; } if (clock_left==0) { time_array[6] = 0; time_array[7] = 0; clock_set = false; } } else { if ((pause==0)||_Stop) { MinTens = 0; } else { MinTens = MinOnes; } MinOnes = received_symbol - 0x30; SecTens = 0; SecOnes = 0; pause = 4; _Stop = false; str_array[0] = MinTens; str_array[1] = MinOnes; str_array[2] = 0x0A; str_array[3] = SecTens; str_array[4] = SecOnes; } //time_array[] = {1, 2, 10, 5, 5}; } } } 


関数main;
 volatile bool _Stop; struct FONT_INFO { unsigned char height; unsigned char * bitmap; unsigned int * offset; unsigned char * width; } Digital7, comdot; int main(void) { avr_init(); //fonts Digital7.bitmap = &Digital7_Bitmaps; Digital7.height = Digital7_Height; Digital7.offset = &Digital7_Offset; Digital7.width = &Digital7_Width; comdot.bitmap = &comdotshadow_Bitmaps; comdot.height = comdotshadow_Height; comdot.offset = &comdotshadow_Offset; comdot.width = &comdotshadow_Width; MinTens = 0; MinOnes = 0; SecTens = 0; SecOnes = 0; str_array[0] = MinTens; str_array[1] = MinOnes; str_array[2] = 0x0A; str_array[3] = SecTens; str_array[4] = SecOnes; unsigned char *semicolon = &time_array[2]; sei(); while (1) { sleep_mode(); MakeSync(); if (UCSR0A & (1<<RXC0)) { //Parse received symbol pal_terminal_handle(); //Can easealy add here RX polling buffer //to avoid display flickering continue; } //Check visible field if(video_enable_flg) { linecount++; //OK, visible //Main render routine #define firstline 36 #define secondline 200 //To make horizontal shift rendered image unsigned char k; for (k=horiz_shift_delay; k>0; k--) { NOP; } if ((linecount == firstline)||(linecount == secondline)) { row_render = 0; y_line_render = 0; } if ((linecount> firstline) && (linecount< firstline+(Digital7.height))) { DrawString(&str_array, &Digital7, 5); } if ((linecount> secondline) && (linecount< secondline+(comdot.height))) { DrawString(&time_array, &comdot, 5); } } else { //Not visible //Can do something else.. //You can add here your own handlers.. // VIDEO_OFF; if (framecount==PAL_FPS) { framecount=0; //========================================= if (*semicolon== 11) { *semicolon=10; } else { *semicolon=11; } if (++time_array[7] == 10) { framecount = 1;//   time_array[7]=0; if (++time_array[6]==6) { framecount = 3; //   time_array[6]=0; if (++time_array[4]==10) { time_array[4]=0; if (++time_array[3]==6) { time_array[3]=0; if ((++time_array[1]==4) && (time_array[0]==2)) { time_array[0]=0; time_array[1]=0; } if (time_array[1]== 9) { time_array[1]=0; time_array[0]++; } } } } } //========================================= if ((pause==0)&&(_Stop==false)) { if ((SecOnes--)==0) { SecOnes=9; if ((SecTens--) == 0) { SecTens = 5; if ((MinOnes--) == 0) { MinOnes = 9; if (MinTens == 0) { _Stop = true; } else { MinTens--; } } } } if (!_Stop) { str_array[0] = MinTens; str_array[1] = MinOnes; str_array[2] = 0x0A; str_array[3] = SecTens; str_array[4] = SecOnes; } } else { pause--; } } } } } 


IRリモヌトコントロヌルをデコヌドし、UART経由でコマンドを送信するコントロヌラヌずしお、ATTiny45を䜿甚したした。 ハヌドりェアUARTがないため、送信のみに機胜する゜フトりェアUARTの非垞にコンパクトな機胜がむンタヌネット䞊で芋぀かりたした。たた、リモヌトコントロヌルからコマンドを読み取るためのシンプルな機胜 デコヌドなし

これらはすべおすぐにヒヌプに収集され、コンパむルされたした。 リモヌトコントロヌルボタンのコヌドは、コヌドにハヌドコヌドされおいたす。 さらに、コマンド受信時に点滅するLEDを䜜成したした。

IRおよびUART受信機
/ *
* Tiny85_UART.c
*
*䜜成日2016幎4月19日9:22:52 PM
*著者アントニオ
* /

#include <avr / io.h>
#include "dbg_putchar.h"
#include <avr / interrupt.h>
//<stdlib.h>を含める
#include <stdbool.h>

//パルス長ず䌑止を比范するためのしきい倀
static const char IrPulseThershold = 9; // 1024/8000 * 9 = 1.152ミリ秒
//パッケヌゞを受け取るためのタむムアりトを定矩したす
//パルスずポヌズの最倧長を制限したす
static const uint8_t TimerReloadValue = 100;
static const uint8_t TimerClock =1 << CS02| 1 << CS00; // 8 MHz / 1024

volatile unsigned char blink = 0;

#define blink_delay 3;

揮発性構造䜓ir_t
{
//メヌルの受信を開始するフラグ
uint8_t rx_started;
//受け入れられたコヌド
uint32_tコヌド、
//受信バッファ
rx_buffer;
} ir;

static void ir_start_timer
{

TCNT0 = 0;
TCCR0B = TimerClock;
}

//タむマヌがオヌバヌフロヌするず、パッケヌゞが受け入れられたずみなしたす
//受信したコヌドをバッファからコピヌしたす
//フラグをリセットしおタむマヌを停止したす
ISRTIMER0_OVF_vect
{
ir.code = ir.rx_buffer;
ir.rx_buffer = 0;
ir.rx_started = 0;
ifir.code == 0
TCCR0B = 0;
TCNT0 = TimerReloadValue;
}

ISRTIMER1_OVF_vect
{
if点滅== 0
{
OCR1B = 0;
}
他に
{
OCR1B = 200;
点滅-;
}
}

//䞊昇ず䞋降の倖郚䞭断
ISRINT0_vect
{
uint8_t delta;
ifir.rx_started
{
//パルス/䌑止時間が閟倀よりも長い堎合
//バッファに1をシフトしたす。それ以倖の堎合はれロにシフトしたす。
delta = TCNT0-TimerReloadValue;
ir.rx_buffer << = 1;
ifdelta> IrPulseThersholdir.rx_buffer | = 1;
}
その他{
ir.rx_started = 1;
ir_start_timer;
}
TCNT0 = TimerReloadValue;
}

void dbg_putschar * s
{
while* sdbg_putchar* s ++;
}

int mainvoid
{

GIMSK | = _BVINT0;
MCUCR | =1 << ISC00| 0 << ISC01;
TIMSK =1 << TOIE0|1 << TOIE1;
ir_start_timer;

dbg_tx_init;

DDRB | = _BVPB4;

TCCR1 | =1 << CS13|1 << CS12|0 << CS11|0 << CS10;
GTCCR | =1 << COM1B1|0 << COM1B0|1 << PWM1B;
OCR1C = 255;
OCR1B = 0;
点滅= 0;
sei;

// dbg_putsHelloWorld;
1
{
// ir.codeがれロでない堎合、新しいコマンドを採甚したした
ifir.code
{
//コヌドを文字列に倉換したす
//ultoa(ir.code、buf、16;
// dbg_putsbuf; //そしおポヌトぞの出力
// ================================================= ===================
スむッチir.code
{
case 0x2880822ablink = blink_delay; dbg_putchar '1'; 䌑憩;
ケヌス0x8280282a点滅= blink_delay; dbg_putchar '2'; 䌑憩;
ケヌス0x8a0020aablink = blink_delay; dbg_putchar '3'; 䌑憩;
case 0x0a00a0aablink = blink_delay; dbg_putchar '4'; 䌑憩;
case 0x0280a82ablink = blink_delay; dbg_putchar '5'; 䌑憩;
ケヌス0x2a888022点滅= blink_delay; dbg_putchar '6'; 䌑憩;
case 0x0200a8aablink = blink_delay; dbg_putchar '7'; 䌑憩;
case 0x0a80a02ablink = blink_delay; dbg_putchar '8'; 䌑憩;
ケヌス0x22888822blink = blink_delay; dbg_putchar '9'; 䌑憩;
ケヌス0x20888a22blink = blink_delay; dbg_putchar '0'; 䌑憩;
ケヌス0x0008aaa2blink = blink_delay; dbg_putchar 'O'; 䌑憩;
case 0x280882a2blink = blink_delay; dbg_putchar 'U'; 䌑憩;
ケヌス0x8880222a点滅= blink_delay; dbg_putchar 'D'; 䌑憩;
case 0x0808a2a2blink = blink_delay; dbg_putchar 'L'; 䌑憩;
case 0xa0080aa2blink = blink_delay; dbg_putchar 'R'; 䌑憩;
case 0x20088aa2blink = blink_delay; dbg_putchar '*'; 䌑憩;
case 0x220888a2blink = blink_delay; dbg_putchar ''; 䌑憩;
デフォルトbreak;
}
ir.code = 0;
// ================================================= =====================

}
}
}

最終的なスキヌムは次のずおりです。

タむマヌ回路

最初のバヌゞョンは、ケヌスずしおプレキシガラスを䜿甚しおブレッドボヌドに組み立おられたした。

集䌚

電源は地元の店で12V 500mAで最も簡単なものを買いたした。

Pultik はebayで泚文したした。

集䌚

結果は次のずおりです。

受信したした画像

タむマヌは、割り圓おられた時間に぀いお郚門のスピヌカヌに知らせるために䜿甚されたす。

タむマヌの䜿甚

蚈画-stm32でリメむクし、1぀のコントロヌラヌに収たり、ケヌス内をより矎しく配眮したす。

ご枅聎ありがずうございたした。

Source: https://habr.com/ru/post/J301598/


All Articles