記事ディレクトリ
エピソード 6 (パート 2) 放課後演習の答え: SOS 遭難照明プログラミング
遭難信号の原理
出典: Asking Knowledge: SOS をライトでどのように表現しますか?
短い 3 つ、長い 3 つ、短い 3 つ
SOS は世界共通の遭難信号です。光信号で表すと、3 つの短いライトは S の文字を表し、3 つの長いライトは文字を表します。ライトは文字 O を表し、その後に続く 3 つの短いライトは S を表します。
ライトの長い点灯時間は短い点灯時間の3倍、短い点灯時間はLEDの2つの点灯時間の間隔と同じであり、また、文字間の短い点灯時間の3倍の消灯間隔があります。そして次の手紙。
Chongge コードと分析
モジュール設計:
math.h:
#ifndef __MATH_H //if no define
#define __MATH_H
#include "stc.h" //调用头文件,具体引用路径根据options选择的调用路径而定
#include "usb.h"
#define MAIN_Fosc 24000000UL //定义一个IRC系统时钟
int Add( int parm1,int parm2 ); //加法
int Sub( int parm1,int parm2 ); //减法
int Mul( int parm1,int parm2 ); //乘法
void SOS_Led(void);
void delay_ms(u16 ms); //unsigned int
#endif
math.c:
関数定義を追加します。
#include "math.h"
int Add( int parm1,int parm2 )
{
return parm1 + parm2;
}
int Sub( int parm1,int parm2 )
{
return parm1 - parm2;
}
int Mul( int parm1,int parm2 )
{
return parm1 * parm2;
}
void SOS_Led(void)
{
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
delay_ms(3000);
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
メインプログラムのdemo.c: #include "math.h" で、メイン関数本体のmain()関数でSOS_Led()を直接呼び出して、SOS関数を実装します。
まず、ヘッダー ファイル math.h に void SOS_Led(void) と void late_ms(u16 ms) の関数宣言を配置します。
次に、 void SOS_Led(void) と void late_ms(u16 ms) の関数定義を math.c に移動します。
MAIN_Fosc は遅延関数で使用されるため、増やす必要があります。main.c 内:
#define MAIN_Fosc 24000000UL //#define 名称 需要定义的内容
一方、メインプログラム部分
while(1) //死循环
{
if( DeviceState != DEVSTATE_CONFIGURED ) //
continue;
if( bUsbOutReady )
{
usb_OUT_done();
printf("计算Add( 1,2 )结果为:%d\r\n",Add( 1,2 ));
printf("计算Sub( 5,2 )结果为:%d\r\n",Sub( 5,2 ));
printf("计算Mul( 5,3 )结果为:%d\r\n",Mul( 5,3 ));
}
P40 = 0; //三极管引脚输出低电平
SOS_Led();
}
通常通りにコンパイルし、フラッシュを開始します。
ボタンの照明(下)
1.ボタンの原理
ボタンは奇妙に見えますが、本質的には 2 つのピン間の接続です。
ボタンを押した後に2つのピンが接続される場合と、
ボタンを押した後に2つのピンが切断される場合があります。
回路図:
ボタンが押されていない場合、プルアップ抵抗があります。これは完全な切断信号です。
マイクロコントローラーの 3.2 ピンは VCC に接続されています。VCC は R10 および R82 を介して P3.2 INT0 の IO ポートに流れます。ボタンを押すと、抵抗の後部が GND に接続され、
配線のすべての位置が 0V になります。R10 は IO ポートを保護するための電流制限抵抗です。分析後、押されていない場合は高レベル、押されている場合は低レベルになります。
上記の方法を使用して、ボタンが押されたかどうかを判断できます。
ヒント: キーストロークの排除
ボタン SW17 が放されると P3.2 はハイレベルになり、ボタンが押されると P3.2 はローレベルになるとします。
ボタンはメカニカルです. メカニカルスイッチを押して飛び出すと振動します. 振動時間は下図の通りです.
ローレベルが10ms後に再び検出されます. まだローレベルの場合は, 10ms は処理が必要なデバウンス時間です。
押したり放したりする過程で、電圧は上下に変動します。
2. キーコード実装プロセス
サンプルコード 1:
if( KEY == 0 )
{
Delay_ms(10);
if( KEY == 0 )
{
执行功能
}
}
サンプルコード 2:
if( KEY == 0 )
{
Delay_ms(10);
if( KEY == 0 )
{
while(KEY == 0);
执行功能
}
}
プロジェクト例
テンプレート プロジェクト フォルダー 1.C-Printf をコピーし、名前を変更します。 3. ボタン コントロール LED。
概略図に従って、2 つの KEY を定義します。
#define KEY1 P32 //定义一个按键引脚KEY1
#define KEY2 P33 //定义一个按键引脚KEY2
Delay_ms 関数をコピーして宣言します。
制御コードを追加します。
if( KEY1 == 0 )
{
Delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下!\r\n");
}
}
if( KEY2 == 0 )
{
Delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下!\r\n");
}
}
シリアルポートの印刷出力
3. ボタンの応用
- 3.1 ボタンを押すと LED が点灯し、離すと消灯します
コード P22 = 0 と P23 = 0 を追加すると LED が点灯しますが、ボタンを放しても消灯しません。
解除と消灯を実現するための判定も追加する必要があり、基板上の元のインジケーターライトは同じ状態でオン/オフになります。
完全なコード:
if( KEY1 == 0 )
{
Delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下!\r\n");
P22 = 0;
}
}
else //如果没有按下
{
P22 = 1;
}
if( KEY2 == 0 )
{
Delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下!\r\n");
P23 = 0;
}
}
else //如果没有按下
{
P23 = 1;
}
- 3.2 ボタンを押すと LED が消灯し、放すと点灯します
P22 と P23 ポートのレベルを反転し (invert~ 操作を使用するだけです)、ボタンを放すと、元の点灯とは逆に点灯します。ボードインジケータライトの状態。
実装コード:
if( KEY1 == 0 )
{
Delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下!\r\n");
P22 = 0; //实现按下点亮,抬起熄灭
//P22 = !0; //取反操作实现按下熄灭,抬起点亮
}
}
else //如果没有按下
{
P22 = 1;
//P22 = !1; //取反操作实现按下熄灭,抬起点亮
}
if( KEY2 == 0 )
{
Delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下!\r\n");
P23 = 0; //实现按下点亮,抬起熄灭
//P23 = !0; //取反操作实现按下熄灭,抬起点亮
}
}
else //如果没有按下
{
P22 = 1;
//P23 = !1; //取反操作实现按下熄灭,抬起点亮
}
- 3.3 ボタンを 1 回押すと、LED の状態が 1 回変化します
最初の考え方は、ボタンを押すと IO 状態が反転し (P23 = !P23)、
押した後の待機状態は間違っています。遅延を増やす必要があります: Delay_ms(200); //検出が速すぎるのを防ぎますが、
ボタンを長押し (押し続ける) と LED が点滅します。1 回だけ実行する制限コードを追加する必要があります。
printf("按键P33已经按下!\r\n");
P23 = !P23; //LED取反1次
while( KEY2 == 0 ) //如果按键一直是按下,一直空循环,实现只执行1次
{
}
リリースしてから実行するように変更できますか? マニュアルの側面を長押しするのと同様に、コードを見てください。
while( KEY2 == 0 ) //如果按键松开,则结束空循环,则执行以下程序
{
}
printf("按键P33已经松开!\r\n");
P23 = !P23; //LED取反1次
- 3.4 ボタンを 1 回押すと、LED が左に 1 ステップ移動します (流れる光の効果)。
ライトのグループを操作するには、例として P2 を取り上げます
: P2 = 0xFE; //1111 1110 初期状態を設定します。
初期状態では、一番右のライトが先に点灯し、1111 1101が
左に1桁ずらして2番目のライトが点灯し、右が最後の桁になります0を加えると1番目と2番目のライトが両方点灯します, 1111 1100 であり、変更は
ライトのみを点灯したい場合は +1 操作が必要で、次のようになります: 1111 1101 2 番目のライトのみがオンです。
ここでは、別の変数を使用して計算し、P2 ポートを使用します。特定のキーを押した後のステータスが取得され、値が割り当てられます。
u8 LED_Data = 0XFD; //8个2进制位的变量
P2 = LED_Data; //1111 1110 设定初始状态
制御コード:
if( KEY2 == 0 ) //判断按键有没有按下
{
Delay_ms(10);
if( KEY2 == 0 ) //按键确实按下了
{
printf("按键P33已经按下,led左移一个\r\n");
LED_Data = ( (LED_Data<<1) +1 ); //本来是直接输出P2,先计算,后输出
if( LED_Data == 0xFF )
LED_Data = 0xFD;
P2 = LED_Data;
while(KEY2 == 0); //如果按键一直是按下的,一直执行while
//while函数体如果无实际执行需要,即花括号内为空,可以直接跟;结束语句,
// {
//
// }
}
}
注: while 関数本体が判定のみに使用され、実際のコードを実行する必要がない場合、つまり中かっこが空の場合は、ステートメントを直接「;」で終了できます。
STC-ISP シリアル ポート ツールの設定
シリアル ポート出力データを簡素化するために、表示プログラムに文字を送信するためのシリアル ポート ツールの簡略化された設定を実行できます。
宿題: P32 (KEY1) を 1 回押すと、ライトが 1 つ右に移動します。
- 4.
配列の使用 配列の使用は 2 つのステップに分かれています
。 1.
型名 [長さ] = {値} を定義します。
たとえば、流水ランプを実装するには、いくつかの状態は次のとおりです。
1111 1110 0XFE
1111 1101 0XFD
1111 1011 0XFB
1111 0111 0XF7
1110 1111 0XEF 1101 1111 0XDF 1011 1111
0XBF 0111 1
111
0 X7F
がランニング ライトを実装したい場合は、上記ステータスを直接出力します。
たとえば、メインの while ループの最後の行にテスト コードを追加します。
P2 = 0XFB; Delay_ms(500);
P2 = 0XF7; Delay_ms(500);
P2 = 0XEF; Delay_ms(500);
P2 = 0XDF; Delay_ms(500);
P2 = 0XBF; Delay_ms(500);
P2 = 0X7F; Delay_ms(500);
効果:流水ランプが右から左に500msずつ点灯し、順次点灯します。
2.
割り当てを使用します: name [index] = value
. 前述の流水ランプを例にとると、データは次のとおりです: 0XFB、符号なし 8 ビット、インデックス (総データ量) は 8、定義データは次のとおりです。
u8 LED_DataTab[8] = {0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F};
縦列選択文字:選択したい文字の左側をカーソルで選択し、alt+shiftを押しながらドラッグして選択したい行または列を選択し、反転してコピーできます。
変数 num を使用して流水ランプを実装します。
P2 = LED_DataTab[num+1]; Delay_ms(500);//1111 0111 0XF7
num++;
上記のコードは、流水ランプを一度実装すると、すべてオンになります。
問題のトラブルシューティングとその理由: num++ には範囲があり、制限する必要があります: if (num > 7) num = 1; //num は 7 までしか指定できません。7 に達すると、1 から開始して右端に戻ります。 。
照明制御は、LED_DataTab 配列の値を変更することで実現できます。たとえば、値が割り当てられている場合: LED_DataTab[3] = 0XFD;、最初のライトは 3 回目でも点灯します。
データを自由に変更して、派手な照明を実現できます。
要約する
1. キーの使用方法を学ぶ
2. 配列をマスターする
授業後の演習: ボタンを押して、LED 出力をさまざまな効果に切り替えます。
4 つのライトと 4 つのライトが点灯する、または 3 つのライトと 3 つのライトが点灯する、2 つと 2 つのライトが左に移動する、というようになります。主要な機能を実装するためのコーディングに熟練している。