モミの朚、ラむトアップ パヌト2C゜フトりェア、GPIOおよび゜フトりェアPWM

こんにちは、Habr



前回、ナノコンピュヌタヌを゚グれクティブクリスマスツリヌガヌランドなどに簡単に接続し、そのためのC / C ++プログラムを構築するための環境を展開する方法に぀いお曞きたした。 今日は、OpenWRT、したがっおBlack Swift甚のCプログラムを䜜成する方法の2番目の郚分です。



  1. C / C ++でのOpenWRTのガヌランド、Black Swift接続およびビルド環境
  2. C制埡プログラムず盎接か぀高速なGPIO操䜜
  3. Web UIずAndroidアプリ




2぀の点に泚意しおください。 たず、メむン関数ず#includeディレクティブの圹割に぀いおは説明したせん-先ほど蚀ったように、今は原則ずしおプログラミングに粟通しおいるが、コンピュヌタヌのようなものにどちらの偎にアプロヌチするのかわからない人々に基づいお曞いおいたす組み蟌み甚途向け。 もっず正確に蚀うず、今日のテキストはすでに近づいおいる人にずっおより可胜性が高いので、論理入出力ラむンGPIOやマむクロ秒時間の操䜜など、いく぀かの興味深い点に焊点を圓おたす。







第二に、もちろん、Cで曞く必芁はありたせん。 OpenWRTの䞋にはPerl、Python、さらにNode.jsがありたす。これらは必芁ありたせん。゜フトりェアを構築するための環境はなく、@ runをアップロヌドするだけです。



GPIOを䜿甚した迅速で簡単な䜜業





ペアで動䜜する4぀のハヌフブリッゞドラむバヌによっお制埡されるクリスマスツリヌガヌランドを手に持っおいたす。1぀のペアはガヌランドの1぀の枝の極性を蚭定し、極性はグロヌの色を決定したす。 明らかに、これを䜿甚するには、パルス幅倉調が必芁で、十分に具䜓的である必芁がありたす-非察称、぀たり各期間䞭に各極性が異なる時間にオンになりたす最も単玔な堎合-極性を1぀だけ䞎えるず、ガヌランドのグロヌの色が1぀だけになりたす 。



このスキヌムは、額に既補のPWMモゞュヌルを適甚するこずを困難にしたすOpenWRTには1぀ありたす-察称的な信号を生成したす。぀たり、最倧の最倧倀は、色を独立しお制埡するこずなく、すべおの色でガヌランド党䜓の明るさを同時に調敎するこずです。 したがっお、PWMは自分で行う必芁がありたす。



PWMは、基本的なレベルでは、このサむクルでは、最も単玔なケヌスでは、プロセッサのレッグを玠早く動かすだけです。



while (true) { if (value > 0) gpioSet(GPIO_NUMBER, 1); for (int i=1; i<=100; i++) if (value == i) gpioSet(GPIO_NUMBER, 0); }
      
      







意味は明らかだず思いたす。倀「value」で、ガヌランドのLEDの明るさで衚珟された目的のデュヌティサむクルを蚭定したす極性反転ず色管理にはただ觊れおいたせん。



LinuxのgpioSetint gpio、intレベル関数の暙準コンテンツは、暙準sysfsむンタヌフェヌスを介しおGPIOレッグをプルしたす。出力Nを1に蚭定するには、ナニットを疑䌌ファむル/ sys /クラス/ gpio / gpioN /倀に曞き蟌みたす。 その前に、/ sys / class / gpio / exportに数倀Nを蚘述しお、ナヌザヌがgpioNを䜿甚できるようにし、/ sys / class / gpioN / directionに「out」ずいう単語を入力しお、このGPIOのモヌドを「出力」に蚭定したす。



この手順には数千の説明がありたす 。たずえば、 そのうちの1぀です 。



 void gpioSet(int gpio, int value) { sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio); fd = open(buf, O_WRONLY); sprintf(buf, "%d", value); write(fd, buf, 1); }
      
      







この方法は単玔で、普遍的ですファむルに曞き蟌むこずができない蚀語を芋぀けるこずは困難ですが、 非垞に遅いです。 それを䜿甚しお花茪を制埡する堎合、最終的に4本の脚2組のハヌフブリッゞを匕くのに非垞に時間がかかるため、400 MHzプロセッサを搭茉したBlack SwiftのPWM呚波数は玄100 Hzになり、䜎茝床ではLEDがはっきりず点灯したすちら぀くのは䞍快です-䞀般に察策をスキップし始めたす。



幞いなこずに、より良い方法がありたす。 幞いなこずに、それほど難しくはありたせん。簡単です。 簡単です。



 void gpioSet(int gpio, int value) { if (value == 0) *(GPIO_ADDR + 4) = (1 << gpio); else *(GPIO_ADDR + 3) = (1 << gpio); }
      
      







秘Theは、プロセッサ内のGPIOがレゞスタを介しお制埡されるこずです。レゞスタを䜿甚したすべおの操䜜はレゞスタを介しお行われたす。 たた、レゞスタは通垞のメモリにマップされたす。 通垞のメモリは、通垞のデバむス/ dev / memから盎接利甚できたす。 したがっお、必芁なのは、mmapを䜿甚しお目的の小さなピヌス/ dev / memをメモリにマップしメモリにメモリ...たあたあ、このピヌスの必芁なビットをプルするこずです。



 if ((m_mfd = open("/dev/mem", O_RDWR) ) < 0) return -1; m_base_addr = (unsigned long*)mmap(NULL, GPIO_BLOCK, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, GPIO_ADDR); close(m_mfd);
      
      







レゞスタのメモリぞのマッピングは、プロセッサのデヌタシヌトに蚘茉されおいたす; AR9331の堎合、アドレスはGPIO_ADDR = 0x18040000 65ペヌゞ で始たりたす 。 たた、ベヌス1の+3および+4のアドレスにも関心がありたす-GPIO番号に察応するビットにナニットを曞き蟌むず、最初のアドレスでGPIOが1に蚭定され、2番目のアドレスで0にリセットされたすもしあれば、+ 2のレゞスタもありたすGPIOをリセットしおむンストヌルできたす-目的のビットに0たたは1を曞き蟌む必芁がありたす。 GPIOの方向は、ベヌスアドレスのビットで蚭定されたす出力の堎合は1、入力の堎合は0。



泚意䞀郚のGPIOは倚機胜であり、これは別のレゞスタに蚭定されたす。たずえば、GPIO 9でUARTをオフにするたで、通垞の入出力のようには機胜したせん。 たた、GPIO 13〜17は入力ずしお䜿甚できたせん。



スピヌド 二重非察称PWMの4぀のGPIOによる痙攣-箄4.5 kHz。 sysfsで䜜業しおいるずきの玄100 Hzに察しお、私は思い出したす。



ナノスリヌプによるPWM呚期調敎





明らかに、このような速床のデむゞヌチェヌン制埡は必芁ありたせん。100Hzを超えるものはすべお、特に䜎茝床でクロックを「倱わない」堎合に最適ですGPIOで盎接動䜜したせん。 遅延を導入する必芁がありたす。 nanosleeptw、NULL関数を䜿甚しお、暙準的に短い遅延が入力されたす。



 struct timespec tw; tw.tv_sec = 0; //  tw.tv_nsec = 10000; //  while (true) { if (value > 0) gpioSet(1); for (int i=0; i<100; ++i) { if (value == i) gpioSet(0); nanosleep(&tw, NULL); } }
      
      







理論的には、ここでは、各PWMクロックサむクルごずに10ÎŒsの遅延、合蚈100クロックサむクル-合蚈1 ms、たたは1 kHzのPWM呚波数足のけいれんのオヌバヌヘッドを考慮しないを取埗する必芁がありたす。 コンパむルし、実行し、玄140〜150 Hzを取埗したす。



問題は、OpenWRTおよびそのようなプロセッサで定期的に維持される最小のナノスリヌプ期間が玄60ÎŒsであるこずです。 ぀たり、関数にtw.tv_nsec = 0を枡しおも、スレッドは60ÎŒs遅くなりたす。



幞いなこずに、非垞に正確ではない原始的なものがありたすが、これに察凊するための有効な方法nanosleepNULL、NULLの呌び出しには玄3ÎŒsかかりたす。



 void nsleep(unsigned long nsecs) { if(nsecs == 0) { nanosleep(NULL,NULL); return; } struct timespec ts; ts.tv_sec=nsecs / 1000000000L; ts.tv_nsec=nsecs % 1000000000L; nanosleep(&ts,NULL); } const int nsleep0_factor=3000; const int nsleep1_factor=70; void _usleep(unsigned long usecs) { if (usecs == 0) return; unsigned long value = (usecs*1000) / nsleep0_factor; if (value > nsleep1_factor) { nsleep((value-nsleep1_factor) * nsleep0_factor); value = value % nsleep1_factor; } for (unsigned long i=0; i < value; ++i) nsleep(0); }
      
      







その結果、_usleepを70マむクロ秒未満呌び出すず、nanosleepを通垞呌び出すこずはありたせんが、nanosleepNULL、NULLを䜕床もスクロヌルするだけで、各呌び出しには3マむクロ秒かかりたす。 それは倧たかなですが、私たちの目的のためにそれをより正確に必芁ずしたせん高品質のPWMが必芁な堎合、リアルタむムモヌドを保蚌するシステムですべお同じハヌドりェアたたは゜フトりェアを行う必芁がありたす-たずえば、UARTを介しお通垞のATTinyをBlack Swiftに接続する。



さお、䞀般に、基本的なブリックは準備ができおいたす-呚波数が100ヘルツたたは2ヘルツの完党に安定した゜フトりェアPWMを䜜成できたす。



たた、このPWMで2組のハヌフブリッゞを制埡するこずも思い出せたすが、これはすでに䞀般的です



 for (int i = 0; i < 100; i++) { for (int j = 0; j < 2; j++) { if (i == floor((float)gpioValuePos/brightness)) { gpioSet(gpio[2*j], GPIO_OFF); gpioSet(gpio[2*j+1], GPIO_OFF); } if (i == (100 - floor(((float)gpioValueNeg/brightness)))) { gpioSet(gpio[2*j], GPIO_OFF); gpioSet(gpio[2*j+1], GPIO_ON); } } _usleep(PWM_PERIOD); }
      
      







gpioValuePosずgpioValueNegは2぀の極性の倀ですもちろん、それらの合蚈が100を超えおはならないずいう条件で、明るさ-これは、ガヌランド党䜓の明るさをすぐに調敎できるようにするために事前に定めたものです。 同じレベルの2぀のGPIOにむンストヌルするこずは、ガヌランドをオフにするこずず同じです。



ベルずホむッスル





ガヌランドを制埡するアプリケヌションには、他に䜕が必芁ですか



第䞀に、明るさは時間の経過ずずもに䜕らかの圢で倉化するはずであり、いく぀かのプログラムがある方が良いです。 あなたはあなた自身のスクリプト蚀語を䜜るこずができたす、私はそれを簡単にしたした-私はコヌドに3次元配列を挿入したした、その䞭に6぀のプログラムがそれぞれ4぀の段階で、それぞれの2぀のペアで保存されたす-1぀の極性の初期茝床倀ずその倉化の速床。 はい、ガヌランドの䞡方のブランチは同期しお動䜜したす。



 int prg[PROGRAMS][4][4] = { { {0, 1, 0, 0}, {99, -1, 0, 0}, {0, 0, 0, 1}, {0, 0, 99, -1} }, { {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 1, 0, 0}, {0, 0, 0, 1} }, { {99, -1, 0, 1}, {0, 1, 99, -1}, {99, -1, 0, 1}, {0, 1, 99, -1} }, { {99, 0, 0, 0}, {99, 0, 0, 0}, {99, 0, 0, 0}, {99, 0, 0, 0} }, { {0, 0, 99, 0}, {0, 0, 99, 0}, {0, 0, 99, 0}, {0, 0, 99, 0} }, { {49, 0, 50, 0}, {49, 0, 50, 0}, {49, 0, 50, 0}, {49, 0, 50, 0} } };
      
      







぀たり、たずえば、最初のプログラム-「+」チャンネルの茝床が埐々に増加し、その埌埐々に䜎䞋し、次に「-」チャンネルの茝床も増枛したす。 簡単に蚀えば、1぀の色がフェヌドむンおよびフェヌドアりトし、別の色が同様にスムヌズにフェヌドむンおよびフェヌドアりトしたす。 埌者では、すべおの色のすべおのLEDが垞に点灯しおいたす。



次に 、ここにはBlack Swift党䜓がありたすので、Wi-Fi制埡を行いたしょう。 そしお、IPアドレスのないクリスマスツリヌガヌランドがあっおも、最終的にスマヌトフォンアプリケヌションはどのようなオタクですか 䞀般に、通垞のWebぞのむンタヌフェヌスを䜜成する必芁がありたす。



技術的に最も簡単な方法は、UNIX゜ケットを介しお、少なくずもPHPから、少なくずもコマンドラむンからsocatナヌティリティを䜿甚しおプッシュできるコマンドを擬䌌ファむルで制埡するこずです。



 mode_t mask = umask(S_IXUSR | S_IXGRP | S_IXOTH); int s, s2, len; unsigned int t; struct sockaddr_un local, remote; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { printf("Socket error\n"); return -1; } int flags = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, flags | O_NONBLOCK); local.sun_family = AF_UNIX; strcpy(local.sun_path, "/tmp/treelights.sock"); unlink(local.sun_path); len = strlen(local.sun_path) + sizeof(local.sun_family); if (bind(s, (struct sockaddr *)&local, len) == -1) { printf("Socket bind failed\n"); return -1; } if (listen(s, 5) == -1) { printf("Socket listen failed\n"); return -1; } umask(mask);
      
      







ここにあるものはすべお事実䞊テンプレヌトです。Webサヌバヌずアプリケヌションが異なるナヌザヌで動䜜するシステムでは、最初ず最埌にumaskを䜿甚した操䜜が必芁であるこずのみを説明したす。 OpenWRTでは、デフォルトではそうではなく、すべおがルヌトの䞋にありたす。 ゜ケットの非ブロッキング操䜜も基本です。そうしないず、最初の呌び出しで党員が立ち䞊がり、䜕かが゜ケットに萜ちるたで埅機したす。



PWM内に、゜ケットに該圓するものの凊理コヌドを挿入したす。



 s2 = accept(s, (struct sockaddr *)&remote, &t); if (s2 > 0) { int i = recv(s2, sockinput, 25, 0); sockinput[i] = 0; // null-terminated string close(s2); cmd[0] = strtok(sockinput, "- \n"); cmd[1] = strtok(NULL, "- \n"); if (strcmp (cmd[0], "brightness") == 0) // set brightness { brightness = atoi(cmd[1]); // 1 is maximum, 2 is half-brightness, etc. } }
      
      







その考えは明確だず思いたす。 「brightness 2」゜ケットを挿入したす-花茪の明るさを半分にしたす。 他のコマンドの凊理も同様に远加されたす。



他に䜕を远加したすか デバッグ䞭の手動起動の䟿宜のために、プログラムがCtrl-Cおよびその他の移動芁求に正しく応答するようにしたす。



 do_exit = 0; static struct sigaction act; sigemptyset (&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction (SIGHUP, &act, NULL); act.sa_handler = cleanup; sigaction (SIGINT, &act, 0); act.sa_handler = cleanup; sigaction (SIGTERM, &act, 0); act.sa_handler = cleanup; sigaction (SIGKILL, &act, 0); while (!do_exit) { //  -    }
      
      







そしお、関数を远加したす



 void cleanup(int sig) { do_exit = 1; }
      
      







これで、OSから割り蟌み芁求で到着した信号がdo_exit倉数を1に切り替え、PWMサむクルが終了したすもちろん、do_exitはグロヌバル倉数ずしお宣蚀するずよいでしょう。



䞀般に、それですべおです。 茝床制埡、耇数の䜜業プログラム、Webベヌスの制埡を備えた非察称PWMを構築するための既補のレンガがありたす。



結果はここにありたす https : //github.com/olegart/treelights OpenWRTおよびプロゞェクト自䜓のMakefileを含むこれは、Linuxで゜フトりェアをビルドするための通垞のMakefileず同じです。 読者のリク゚ストに応じたビデオは、第3郚にありたすただし、花茪は芋おいたせんが、䜕ですか。



もちろん、䞊蚘のすべおはすべおのOpenWRTルヌタヌずナノコンピュヌタヌに適甚されたすが、1぀の泚意点がありたす。BlackSwiftの堎合、通垞の100 Hzに察しお1000 Hzのタむマヌでプリ゚ンプティブマルチタスクを有効にしおコアを構築したす。 それがなければ-暙準コアでは-PWMはシステムの負荷がかかっおも遅くなるか、安定性が䜎䞋したす。



ただし、OpenWRT甚のファヌムりェアのアセンブリに぀いおは、別の機䌚です。



All Articles