クラス設計をしよう - STM32 Timer編

1. クラス内でする処理を考える

まずは、クラス内でするべき動作を考えます。とりあえず、自分の喫緊の課題になっているものを列挙していきます。

  1. タイマー機能をPWMで初期化する
  2. PWM出力を開始する
  3. PWM出力を設定する

2. 実装方法を決める

続いて実装方法を決めていきます。コンストラクタに乗せるとか、メソッド化するとか、プライベート変数で保持してGetterSetterを作るとかです。

  1. タイマー機能をPWMで初期化する ← TIM_HandleTypeDefだけコンストラクタで受け取って、それ以外はstart関数的なものに実装
  2. PWM出力を開始する ← レジスタに書き込みをするメソッドとして実装
  3. PWM出力を設定する ← レジスタに書き込みをするメソッドとして実装

3. 真面目にメソッドやらをきめる

どんなメソッドを作っていくのか決めます。ヘッダファイルを作るといいような気もします。

class TimerPwm {
/* -----------------------------------------------------
 * public 列挙体、インナークラス
 * ----------------------------------------------------- */
public:
    enum EChannel : int {
        CH1 = 1,
        CH2 = 2,
        CH3 = 3,
        CH4 = 4,
    };


/* -----------------------------------------------------
 * private メンバ変数
 * ----------------------------------------------------- */
private:
    TIM_TypeDef *tim;         // 設定対象タイマーのインスタンス。


/* -----------------------------------------------------
 * public メソッド
 * ----------------------------------------------------- */
public:
    /**
     * @brief 設定対象タイマーのインスタンスを受け取るのみ。初期化動作は行わない。
     * @param TIM_TypeDef _htim [I  ]: 設定対象となるタイマーのインスタンス
     */
    TimerPwm(TIM_TypeDef *uart);

    /**
      * @brief  初期化を行う関数。各レジスタの設定を行いペリフェラルを初期化する。
      * @param  period		[I  ]: PWMの周期幅
      * @param  prescale 	[I  ]: プリスケーラの値。0の場合プリスケールなし。
      * @retval None
      */
    void initialize(uint32_t period, uint32_t prescale);

    /**
      * @brief PWM出力を開始する
      * @param EChannel ch 	[I  ]: 設定するチャンネル
      */
    void start(EChannel ch);

    /**
      * @brief 出力値を設定する。
      * @param EChannel ch		[I  ]: 設定するチャンネル
      * @param uint32_t value	[I  ]: 設定する値
      */
    void out(EChannel ch, uint32_t value);

};

GY-BNO055をUARTで使う

UARTの設定について

項目
ボーレート 115200
データ長 8ビット
パリティビット なし
ストップビット 1ビット

レジスタへの書き込み方法

以下の順でデータを送ります。

Byte 1 Start Byte 0xAA
Byte 2 Write 0x00
Byte 3 レジスタアドレス <..>
Byte 4 データバイト数 <..>
Byte 5 データバイト1 <..>
Byte n+4 データバイトn <..>

センサからの返り値は次のようになっていて、書き込みに成功したかを返します。

Byte 1 Response Header 0xEE
Byte 2 Status 以下の数値


0x01: 書き込み成功
0x03: 書き込み失敗
0x04: REGMAP_INVALID_ADDRESS
0x05: REGMAP_WRITE_DISABLED
0x06: WRONG_START_BYTE
0x07: BUS_OVER_RUN_ERROR
0X08: MAX_LENGTH_ERROR
0x09: MIN_LENGTH_ERROR
0x0A: RECEIVE_CHARACTER_TIMEOUT

レジスタからの読み出し

以下の順でデータを送ります。

Byte 1 Start Byte 0xAA
Byte 2 Read 0x01
Byte 3 レジスタアドレス <..>
Byte 4 データバイト数 <..>

センサからの返り値は2種類で、読み出しに成功したかと読み出した値を返します。

① 成功した場合は以下のパターンで値を返します。

Byte 1 Start Byte 0xBB
Byte 2 データバイト数 <..>
Byte 3 データバイト1 <..>
Byte n+2 データバイトn <..>


② 失敗した場合は以下のパターンで値を返します。

Byte 1 Response Header 0xEE
Byte 2 Status 以下の数値

0x02: READ_FAIL
0x04: REGMAP_INVALID_ADDRESS
0x05: REGMAP_WRITE_DISABLED
0x06: WRONG_START_BYTE
0x07: BUS_OVER_RUN_ERROR
0X08: MAX_LENGTH_ERROR
0x09: MIN_LENGTH_ERROR
0x0A: RECEIVE_CHARACTER_TIMEOUT

席替えをしたかった

ちょっと諸事情ありまして、席替えプログラムを作っておりました。

で、今回作ったプログラムを題するなら、「お隣さん絶対かわるやーつ」ってところでしょうか。
前後お隣の人がかならず変わります。というか、変わるまで、プログラムは終わりません。

あと、かなりテキトーなプログラムになるので、まさかりは投げないでください。

以下、プログラムになります。

#include<iostream>
#include<vector>
#include<random>
#include<unistd.h>

#define X_MAX 6
#define Y_MAX 4

#define KINBOU_N 4
#define NINZU 21

struct Tkumi{
	int p1;
	int p2;

    bool operator<( const Tkumi& right ) const {
        return p1 == right.p1 ? p2 < right.p2 : p1 < right.p1;
    }
};

void back_disp(){
	printf("\e[5F");
}
void disp(int seki[Y_MAX+2][X_MAX+2], char const *name[]){
	printf("---------------------------------------------");
	printf("---------------------------------------------------\n");
	for ( int y = 1; y < Y_MAX+1; y++ ){
		for ( int x = 1; x < X_MAX+1; x++ ){
			if ( seki[y][x] > 0 ) {
				printf("%16s", name[seki[y][x]]);
			}else{
				printf("                ");
			}
		}
		printf("\n");
	}

}

int main(int argc, char const *argv[])
{
	int kinbou_x[KINBOU_N] = {  1,  0, -1,  0 };
	int kinbou_y[KINBOU_N] = {  0,  1,  0, -1 };
	int set[Y_MAX+2][X_MAX+2] = { // 場所固定の場合はここに値を入れる
		{-1,-1,-1,-1,-1,-1,-1,-1},
		{-1, 0, 0, 0, 0, 0,19,-1},
		{-1, 0, 0, 0, 0, 0, 0,-1},
		{-1, 0, 0, 0, 0, 0, 0,-1},
		{-1, 0,-1, 0,-1, 0,-1,-1},
		{-1,-1,-1,-1,-1,-1,-1,-1}
	};
	int next[Y_MAX+2][X_MAX+2] = {
		{-1,-1,-1,-1,-1,-1,-1,-1},
		{-1, 0, 0, 0, 0, 0, 0,-1},
		{-1, 0, 0, 0, 0, 0, 0,-1},
		{-1, 0, 0, 0, 0, 0, 0,-1},
		{-1, 0,-1, 0,-1, 0,-1,-1},
		{-1,-1,-1,-1,-1,-1,-1,-1}
	};
	int before[Y_MAX+2][X_MAX+2] = {
		{-1,-1,-1,-1,-1,-1,-1,-1},
		{-1, 1, 5, 8,12,15,19,-1},
		{-1, 2, 6, 9,13,16,20,-1},
		{-1, 3, 7,10,14,17,21,-1},
		{-1, 4,-1,11,-1,18,-1,-1},
		{-1,-1,-1,-1,-1,-1,-1,-1}
	};
	int now[Y_MAX+2][X_MAX+2] = {
		{-1,-1,-1,-1,-1,-1,-1,-1},
		{-1, 1, 2, 3, 4, 5, 6,-1},
		{-1, 7, 8, 9,10,11,12,-1},
		{-1,13,14,15,16,17,18,-1},
		{-1,19,-1,20,-1,21,-1,-1},
		{-1,-1,-1,-1,-1,-1,-1,-1}
	};
	char const *name[NINZU+1] = {
		"nonono",
		"name01",
		"name02",
		"name03",
		"name04",
		"name05",
		"name06",
		"name07",
		"name08",
		"name09",
		"name10",
		"name11",
		"name12",
		"name13",
		"name14",
		"name15",
		"name16",
		"name17",
		"name18",
		"name19",
		"name20",
		"name21"
	};

	std::vector<Tkumi> kinshi;
	Tkumi dmy = { 0, 0 };

	// 禁止リストを作成
	for ( int y = 1; y < Y_MAX+1; y++ ){
		for ( int x = 1; x < X_MAX+1; x++ ){
			if ( now[y][x] >= 0 ) {
				// -1じゃなければ、4近傍を検索
				for ( int k = 0; k < KINBOU_N; k++ ) {
					int hikaku = now[y+kinbou_y[k]][x+kinbou_x[k]];
					if ( hikaku > 0 ){
						// -1じゃなければ追加
						dmy.p1 = now[y][x];
						dmy.p2 = hikaku;
						if ( dmy.p1 > dmy.p2 ){
							int swp = dmy.p1;
							dmy.p1 = dmy.p2;
							dmy.p2 = swp;
						}
						kinshi.push_back(dmy);
					}
				}
			}
		}
	}

	for ( ;; ) {
		// printf("---- restart ----\n");
		// disp(next, name);

		int not_decide = 0;
		int ok = 0;
		// 固定値を設定
		for ( int y = 1; y < Y_MAX+1; y++ ){
			for ( int x = 1; x < X_MAX+1; x++ ){
				if ( set[y][x] >= 0 ) {
					if ( set[y][x] == 0 ) not_decide++;
					next[y][x] = set[y][x];
				}
			}
		}


		// 確定フェーズへ
		// 乱数生成
		std::random_device rnd;     // 非決定的な乱数生成器を生成
		std::mt19937 mt(rnd());     //  メルセンヌ・ツイスタの32ビット版、引数は初期シード値
		std::uniform_int_distribution<> rand(1, NINZU);        // [0, 99] 範囲の一様乱数
		ok = 1;
		for ( int y = 1; y < Y_MAX+1; y++ ){
			for ( int x = 1; x < X_MAX+1; x++ ){
				if ( next[y][x] == 0 ) { // 席が未決かを確認
					// 1席を決定
					int maybe;
					for ( ;; ) {
						maybe = rand(mt);
						int maybe_cnt = 0;
						// 表内を探索して、同一番号があれば、乱数を振り直す
						for ( int y = 1; y < Y_MAX+1; y++ ){ 
							for ( int x = 1; x < X_MAX+1; x++ ){
								if ( next[y][x] == maybe ) {
									maybe_cnt++;
								}
							}
						}
						if ( maybe_cnt == 0 ) { // かぶり無しなので、この座席で仮決定
							next[y][x] = maybe;
							break;
						}
					}
	back_disp();
	disp(next, name);
	usleep(50000);

					// 禁止組み合わせを確認
					// 4近傍を検索
					for ( int k = 0; k < KINBOU_N; k++ ) {
						int hikaku = next[y+kinbou_y[k]][x+kinbou_x[k]];
						if ( hikaku > 0 ){
							// 近傍の席が配置済み
							for ( auto v = kinshi.begin(); v != kinshi.end(); v++ ) {
								if ( v->p1 == hikaku && v->p2 == maybe ) { ok = 0; break; }
								if ( v->p1 == maybe && v->p2 == hikaku ) { ok = 0; break; }
							}
						}
					}
				}
				if ( ok == 0 ) break;
			}
			if ( ok == 0 ) break;
		}


		if ( ok == 1 ){
			break;
		}
	}

	printf("-----------------finale-------------\n");
	printf("-----------------finale-------------\n");
	printf("-----------------finale-------------\n");
	disp(next, name);
	return 0;
}

EagleでPCB基板にロゴを入れる

書きかけです。

手順は
1.GIMP2値化(「色→Threshold...」もしくはモード変換のときにディザリング)
2.GIMPの「画像→モード→インデックス」で1bitモードに変換。(ビューは変わらないことがある)
3.GIMPbmpデータで保存
4.EagleのULP「import-bmp」を実行
5.EagleのULP「import-bmp」で色選択
6.EagleのULP「import-bmp」で大きさとレイヤーを指定(DPIがやりやすい、レイヤーは21を選択)

ロゴをパーツとして登録しておくと使いやすい。

Elecrowで注文してみた

最近Twitterで広告を見なくなったElecrowですが、気が向いたので注文してみました。いろいろな関係で写真や基板データは見せられないのですが…

1. 所感

思ったより早く到着しました。ちょうど2週間なので早い方では?ただ、基板のパッドがヘアライン加工されているみたいな跡がついているのはなぜだろう?べつに完成したあとの基板の性能を左右するもんでもないので、問題ないのですが。

2.基板について

基板の詳細はこちら。特に何の変哲もない70×40[mm]の基板です。
2layers PCB 1.6mm 70mm x 40mm 5pcs Red
PCB Qty:5pcs
Layer:2layers
PCB Thickness:1.6mm
Dimensions:70mm * 40mm
Castellated Hole:No
PCB Color:Red
Surface Finish:HASL
Copper Weight:1oz
Text Color:White
Same Design:1

3. お値段

送料が一番安いのでもこれだったので、やむなく有料のにしました。たしか、Registered Airmailだったかな?
小計 $4.90
配送料と手数料 $5.34
割引価格 (196 points used) -$1.96
合計 $8.28

4. 日付

2018年5月15日 02:30:30 JST 注文と支払い完了
2018年5月15日 16:30:24 JST 注文受注
2018年5月15日 11:42:00 JST 出荷メール受領
2018年5月28日 11時頃 書留にて到着

5. 最後に

設計ミスした…

シングルボードコンピュータのまとめメモ

完全に自分用ですが…

Windowsを搭載できるシングルボードコンピュータをつらつらと並べていきます。
追記で情報を足していくかも??

1 . UP Core

  • 価格:99$
  • 長所
    • 56.5×66mm、40gと小型で軽量
  • 短所
    • イーサーネット非対応
    • 入手が難しそう?(ラズパイコンパチブルはAmazonにあり)

2 . Latte Panda

  • 価格:260$
  • 長所
    • Arduinoが載っている
    • Amazonにめっちゃコロがってる
  • 短所

3 . Latte Panda Alpha

Latte Pandaの後継機に当たる

  • 価格:$289〜
  • 長所
    • MacBook(無印)と同じプロセッサを積んでいる(サイズに対して速い)
    • RAMが最大8GB
    • ギガビットイーサネット搭載
  • 短所
    • 2018年5月に発売(現在まだ売っていない)

4 . UDOO X86 Basic

  • 価格:99$
  • 長所
  • 短所
    • 入手が難しそう?(Kickstarterのプロジェクト)

5 . UDOO X86 Ultra

  • 価格:99$
  • 長所
  • 短所
    • 入手が難しそう?(Kickstarterのプロジェクト)

MATLABの関数

関数について

 MATLAB関数型言語に分類される。なので、関数の中で関数が宣言できたりもする。関数と関数ファイルは関係性が深い。それらの関係性から、関数は大きく分けて「基本関数」「サブ関数」に分類される。

  • 基本関数 -> 関数ファイルと同じ名前で、ファイルの一番最初に記述される関数。
  • サブ関数 -> 関数ファイル内で、基本関数以外の関数。



1. 関数の呼び出し方

関数には入力引数と出力引数が存在して(C言語での引数と返り値に相当)、それぞれ複数指定可能である。基本文法は次のような感じ。

[y1, y2, …] = func(x1, x2, …)

出力引数が1つの場合は[]が省略できる。また、引数がない場合は、()が省略できる。下のは呼び出し方の例。

A = [1 3 5]
B = [10 6 4]
max(A)
max(A, B)
[maxA, location] = max(A)



2. 関数の定義の仕方

続きを読む