ぷらこのきろく

メモとかテストとか備忘録とか

飴ちゃん分配問題

某所で問題出されたので解いてみた。
効率とか出力形式とかはきにしなくていい、というので、まずは思いついたまま

「M個の飴を余りがないようにN人の参加者に分配する。
このとき、飴を1つも貰わない参加者が居てもいい。
参加者名のリストと M, N が与えられたとき、分配のしかたを全て求めよ」


M=3, N=2
参加者名 ["A", "B"]

出力
A=3, B=0
A=2, B=1
A=1, B=2
A=0, B=3

using System;
using System.Collections.Generic;

namespace candydistribute
{
    class Program
    {
        const int MEMBER_NUM = 3;   //人数
        const int CANDY_NUM = 10;   //飴の数

        static void Main(string[] args)
        {
            //分ける人のリスト作成
            List<string> inputnames = new List<string>();
            for (int i = 1; i<= MEMBER_NUM; i++)
            {
                inputnames.Add("member" + i.ToString());
            }
            destribute(inputnames, CANDY_NUM, "");
        }

        static void destribute(List<string> names, int amount, string for_output)
        {
            if(names == null)
            {
                Console.WriteLine("ぬるぽ");
            }
            else
            {
                int count = names.Count;
                if (count == 0)
                {
                    Console.WriteLine("いない人に配れません");
                }
                else
                {
                    string member = names[0];
                    if (count == 1)
                    {
                        Console.WriteLine(for_output + string.Format("({0}: {1})", member, amount));
                    }
                    else
                    {
                        for (int i = 0; i <= amount; i++)
                        {
                            List<string> tmplist = remove_first(names);
                            destribute(tmplist, amount - i, for_output + string.Format("({0}: {1}), ", member, i));
                        }
                    }
                }
            }
        }
        //一番前の要素を削除して返す。元のリストからそのままremoveしたらたぶん再帰で呼び出したときに元のリスト変わってしまうのでコピーして返す
        static List<string> remove_first(List<string> src)
        {
            int count = src.Count;
            string[] tmparr = new string[count - 1];
            src.CopyTo(1, tmparr, 0, count - 1);
            return new List<string>(tmparr);
        }
    }
}

リストをコピーしてるのでメモリ効率的にとても悪いがとりあえずいいことにする。
直すとしたら、現在何人目の処理してるかを引数とかでとってくるんかな。あとで気が向いたらなおしてみよう。
再帰使って書いたけど、使わないやり方でよさげな方法が思い浮かばん。

先輩風を吹かせてみる

先輩風を検知すると風車が回るようなおもちゃがあるとか。
ちょっとそのおもちゃを回してみる。

僕が仕事してて、初めて後輩と言える後輩が入った。
彼らへのメッセージ。

知識は仕入れ続けろ

たとえば今日一日筋トレをしたとする。明日になって何かが変わるわけではない。
でも、一年続けたとすると、しなかったときとの差は恐らく大きい。
知識が全て役に立つかというと、それはない。でも1%でも役に立てば、それは充分効果がある。
知識がない状態でも君らは自分でそこまで考えられるんだ。知識があったらその分時間が短縮できるし、短縮した分をさらなる発展につなげられる。
自分の興味がある分野があればそれについてなんか適当にやっておけばいいし、もし興味がある分野がないなら、テレビやネットなどで見つけた単語をググるだけでもいい。
何でもいい。知識を仕入れろ。

自信をもて

依頼した仕事の報告で「プログラムができてしまったんですけど・・」
見たら、こちらの要求した仕様を不足なく満たしてできている。
もちろん、コードが洗練されていないとかもっといい実装方法があるとかはあるかもしれない。
でも僕が求めたのはそこじゃない。これこれを満たすプログラムを作ってくれという要求だけ。
そして君は自分の力でそれを作った。
できて「しまった」じゃないよ。君の力で完璧にこなしたんだよ。
「俺がお前のためにお前の求めるものを作ってやったぞ」心の中ではそれくらいの気分でいよう。

わからなければ聞け

「聞くは一時の恥、聞かぬは一生の恥」とかいうけどそれは間違い。
聞くのは恥でもなんでもないよ。
もちろん、これはこの分野の基礎知識だから知っといてと思うことはあるけど、それはそう言うし知らなかったら知ればいいだけなんだからこんな簡単な話はない。
知らないことで僕は君らの評価を落としたりはしない。僕は君らの知識の有無を、「この人はこの知識があるから導入端折れるな」「この人は知識がないから導入しっかりしような」と、話題の振り方に利用するだけ。
怖がらずに自分は知らないと言ってほしい。

他人の力を使え

仮に何かのミスで手戻りが発生したとする。一人でやろうとせず僕らを使え。理由は三つ。
一つ目は、僕らはチームだ。お客さんからすれば誰がどうやろうと期限内にちゃんと物ができていればいい。君ひとりが4時間でする作業を僕と二人で2時間で済むならその方が早く帰れるだろ。もひとり先輩巻き込めば1時間20分だ。もちろん単純計算はできないが、複数で分担した方が早いだろ。
二つ目は、僕は君らの手伝いをするのが好きだ。自分に余裕がない場合はあまり手伝いできないかもだけど「ねーねー何やってるの?もしよければ僕にも一枚かませて」仕事の邪魔にならない程度にやりたい。だから、そう大して負担じゃないよ。
三つ目は、僕も君らの力を借りてるよ。だからお互い様。
僕が自分のめんどくさい苦手なことを、君らに手伝ってもらってるの知ってる?
だから僕が君らの手伝いをするのは当然の恩返しなんだよ。

「すみません」と「ありがとう」は違う

誰かがなにかやってくれたときは、「すみません」ではない。やってくれたことに「ありがとう」だ。
もし僕らに手間やお金をかけさせて申し訳ないと思うなら、それを今後増えるであろう君らの後輩にやってほしい。
少なくとも僕は、僕が君らに何かしたときに謝られるより感謝の言葉を言われる方がうれしい。


今の僕は君らよりほんのちょっとだけ知識と技術はあるけど、差はほんのちょっとだし、君らは僕を一瞬で抜き去ることができるよ。そして僕はそれが楽しみで仕方ない。
もちろん、簡単には抜かせないように僕もがんばるよ。
だから、君らは僕を一蹴してほしい。

いじょ。

C言語ポインタの初級者用課題

あっちの方はちょっと休憩。

某所で、プログラムとC言語を勉強し始めた方がおられて、すごくがんばっておられる。
僕も負けてはいられないんだけど、その方向けにちょっとした課題のプレゼント。

その1:値を入れ替える関数を作ってみる

こんなプログラム。

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c;
	/* まず初期値を表示する */
	printf("a=%d, b=%d\n", a, b);

	/* ここから値を入れ替えてみる */
	c = a;
	a = b;
	b = c;
	/* 値がいれかわったかな? */
	printf("a=%d, b=%d\n", a, b);
	return 0;
}

即席で作ったからタイプミスとかあったらごめんなさい。適当に直して。
aとbの値が入れ替わる処理はこんな風に書けます。直接一気に交換する方法…もないことはないらしいんですが、
普通は、退避用の変数(この場合はcですね)をとって、入れ替えを行います。
ということで、この入れ替える処理を関数(名前はとりあえず"swap")にしてみましょう。
まず失敗例。

#include <stdio.h>
void swap(int a, int b)
{
	int c;
	c = a;
	a = b;
	b = c;
	return;
}
int main()
{
	int a = 10;
	int b = 20;
	int c;
	/* まず初期値を表示する */
	printf("a=%d, b=%d\n", a, b);

	/* ここから値を入れ替えてみる */
	swap(a, b);
	/* 値がいれかわったかな? */
	printf("a=%d, b=%d\n", a, b);
	return 0;
}

実行してみたらわかるけど、これは値が入れ替わらないのですね。
なんでかというと、main()関数のaとswap()関数のaは違うものなんです。bももちろん同じ。
そして、main()関数のaの「値」(この場合は10ですね)がswap()関数のaにコピーされて動くので、コピーされたものを入れ替えるだけではコピー元のmain()のaやbには影響はないのです。
そこでどうするか、ポインタなんですね。*1
ポインタで、main()関数のaやbという変数のアドレスの値を渡してあげて、そのアドレスの指す値を入れ替える、そういう関数を作ってください。

*1:マクロの話はとりあえずおいとく

ASOOVU USBで遊んでみる(2-1)

その2-1:割り込みを使おうとして失敗する

タイマ機能というものがあるらしい。時間をカウントして一定時間経ったら教えてくれたり時間を計ったりできるらしい。
その機能で、時間がたったら割り込みがかかって教えてくれるものがあるっぽい。
というわけで、割り込みを使おうと思いたった。

XC8のマニュアルを読むと、

void interrupt hoge(void)

と書くと、hoge()は割り込み関数として登録されるらしい。
ASOOVU USBで使われるPIC18F2550は、割り込み関数が二つ(優先度高と優先度低)使えて、それぞれ

void interrupt low_priority hoge(void)
void interrupt high_priority heke(void)

で、優先度低と高が使えるらしい。と、PIC18F2550のデータシートとXC8のマニュアルに書いてあった。

というわけで、何もしない(単にreturnを返す)だけの割り込み関数だけ作って、動かそうとしてみた。
・・・動かない。今まで動いていたmain()の中身が実行されない。
いろいろ試してみた。3日ぐらいかけて。関数の中身を変えてみたり、割り込み許可をしないようにしてみたり、コンフィギュレーションビットを確認してみたり。
で、確認できたのは、割り込み関数を定義すると動かなくなって、定義をコメントアウトすると動くようになる。

なんでだろう・・・と、ASOOVU USBの販売元のサイトからサンプルプログラムをダウンロードして確認してみた。bootload.hに

#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x1008
...
#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS
void Remapped_High_ISR (void)

とかかいてある。書いてあることは半分くらいしかよくわからないが、優先度高の割り込み関数が置いてあるアドレスが0x1008に再設定された、ということなのではないか。

mapファイル(プロジェクトのhexファイルが置いてあるディレクトリと同じ場所に、~.mapと書いてあるファイルがある)を確認する。
ちょっとしか意味はわからないが、このファイルには関数やグローバル変数などがどのアドレスに割り付けてあるかが書いてある。

サンプルプログラムのmapファイルの優先度高割り込み関数(だと思われるもの)は、確かに0x1008に割り付けられてる。
そして、自分が書いたプログラムのmapファイルを見ると、0x0008に割り付けられている。

ここで、データシートを見る。figure5-1にメモリのマップがある。やっぱり0x0008だよなぁ。。
でも、やってみるしかない。割り込み関数の割り付けを変えてみよう。。

MPLAB X IDEのプロジェクトを右クリックして、Propertiesを選択。
XC8 LinkerのカテゴリからAdditional optionsを選択。Extra Linker Optionsに

-L-preset_vec=1000h,intcode=1008h,intcodelo=1018h,powerup,init

と記述。
f:id:prizeout:20171225223504p:plain
あ、なんかmain()が動きだした。割り込み関数を使ってみよう、と、適当にタイマ0で割り込みを起こしてみたら、
ちゃんと割り込み関数を通った。
しかしなんで割り付けをデフォルトから変えないといけないのだろう。どこにその説明が書いてあるのだろう。
まあともかく、これで次はやっとタイマをまともに使える。
続く。

ASOOVU USBで遊んでみる(4)

その(4) :コード書いてコンパイル・書き込み・実行

コード確認

コードはこれだけ
f:id:prizeout:20171217162443p:plain
とりあえずメモ

9行目

ヘッダファイルインクルード。こないだの日記でコンフィグレーションビットの設定を出力したコピーペースト。中でxc.hをインクルードしている。

14~15行目

PORTBとLATBを0クリア。
前の日記の初期化例で調べたのをそのまま。

16行目

TRISBを0に設定。つまり、RB0~RB7を全部出力に設定。

18行目

while(1)で回す。組込み関係ではプログラムは終了しないのが常道らしい。つまりmain()関数のreturnはしない。

19行目

500ms待つ。これはXC(コンパイラ)が提供するマクロみたい。
xc.hから読み込まれるpic.hに*1

// NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

って書いてある。
で、10行目で_XTAL_FREQを設定。設定する数は、マイコンの周波数。ASOOVU USBのマニュアルに製品仕様が書いてあって
内部動作周波数:48MHzと書いてある。ということで48000000を設定。
・・・あ、#defineの値を括弧でくくるの忘れてた。

20~25行目

LATB4をON/OFFする。僕はif~elseで書くけど多分別に~のビット反転使ってもいいと思う。たぶん。
LATBbits構造体や、先ほどでてきたPORTBやLATB、TRISBのレジスタなどはpic18f2550.hで定義されてる。

コンパイル

で、コンパイル
MPLAB X IDEのプロジェクト名を右クリックして、Buildを選択。
コンパイル結果はOutput欄に出力。
f:id:prizeout:20171217170238p:plain
BUILD SUCCESSFULLでビルド成功を確認。
次に、hexファイル(デバイスに書き込むためのファイル)の場所を確認。

書き込み

HIDBootLoader.exeを起動。「Hexファイル読み込み」で、先ほどできたhexファイルを読み込む。
ASOOVU USBのマニュアルに従って、デバイスのジャンパーピンをBOOT側に設定し、USBケーブルでPCにつなぐ。うまくいくとLEDが赤青にちかちかする。
そして「Hexファイル書き込み」。うまくいけば書き込み成功のメッセージが出る。

実行

バイスをPCから外し、ジャンパーピンをBOOT側からもう一方へ設定する。もう一度USBケーブルに接続する。
実行結果の動画(youtube)
めでたしめでたし

*1:XCのヘッダファイル群はインストールされたディレクトリ、デフォルトでは"C:\Program Files (x86)\Microchip\xc8\v1.44\include"にある

ASOOVU USBで遊んでみる(3)

その(3):データシートを見る

とりあえず今回は、LEDを光らせたり消したりすることを考える。

ポートの確認

ASOOVU USBのマニュアルの裏側に回路図が書いてある。(サイトからダウンロードもできる)
で、LEDの部分を見てみる。
f:id:prizeout:20171217121741p:plain
LED(青)は25番ポートとつながっているらしい。

データシートの確認

公式サイトからデータシートをダウンロードしてくる。
http://ww1.microchip.com/downloads/en/DeviceDoc/39632e.pdf
英語とか、426ページとか、中学以降英語は落ちこぼれで文章読んだりするのも苦手な僕は結構うんざりなんだけど*1
がんばって手抜き読みをしてみる。きっと図表が書いてあるにちがいない。。
可能な限り図表だけで読んでみよう。。

ピン配置図

pdfの4ページにピン配置図があった。
f:id:prizeout:20171217130346p:plain
25番ピンはRB4というらしい。

ブロック図

12ページにブロック図があった。
f:id:prizeout:20171217130942p:plain
RB4はPORTBにつながっているらしい。

ポート回路図

というわけで10章。IO Portの章。回路図を見る。
f:id:prizeout:20171217150310p:plain
三角形は3ステートバッファというものだと思う。論理回路:3ステートバッファ(1) | 東芝 半導体&ストレージ製品
制御信号に○がついてるからきっとNOTで、TRISラッチが0のときにDATAラッチからIOピンに出力があるらしい。
(ラッチはなんか値を保存しておくものだった気がする。なんか値を書き込んだらそのまま保持されるんだろう。)
ということでTABLE 10-3を見ると
f:id:prizeout:20171217152722p:plain
RB4はLATB4から読み込めるらしい。

初期化例

PORTBの初期化例が書いてあった。アセンブラなんか読めないが、この程度ならなんとなく理解。
f:id:prizeout:20171217153053p:plain
とりあえず理解に必要なのは上下の赤枠の中。真ん中のはコンフィグレーションビットの設定でしてあるからいらない。と判断。
上の部分はPORTBとLATBの0クリア。下の部分はTRISBに0xCFを代入。0xCFは2進法で書くと11001111だから、一番右を0番目、一番左を7番目として、4,5番目が0となっている。
コメントに、「RB4とRB5を出力に」とあるので、TRISBの該当ビットを0にすると出力なんだろう。

というわけで、やっとコードが書ける。
続く。

*1:だったら日本語訳見ろっていうんだけど、それはそれで敗北感が・・・

ASOOVU USBで遊んでみる(2)

その(2) :Configuration bitの設定

よくわからないけど、機械の設定っぽいことをするらしい。
本当はデータシートとか見ないといけないけど、そのうち詳しくなってみたい。

とりあえず今回は、マニュアルのサイト、あるいは書き込みソフトをダウンロードしたサイトから
適当なスケルトンソフトを落とし、中のヘッダファイルを見て真似してみた。

Production→Set Configration Bitsを選ぶ。
いろいろとあるので、とりあえず真似して設定。
f:id:prizeout:20171214010833p:plain
Generate Source Code to Outputボタンを押すと、アウトプットのコードができる。
これをコピーペーストでソースコードに貼りつけるみたい。

main.cにぺたぺた貼りつけるのもあるんだろうけど、僕はヘッダファイルを作ってそこに貼りつけた。

続く。