ラベル STM8S の投稿を表示しています。 すべての投稿を表示
ラベル STM8S の投稿を表示しています。 すべての投稿を表示

2020年9月2日水曜日

STM32でDCC/DDCを作る方向で (55) 設計資料 44.1k/48kクロック切り替え

STM32でDCC/DDCを作ろう!            INDEXページへ

情報が間違っていても責任とらないし賠償とかしないです.

XC9536でbitclkを生成しています.

bitclkの源発振は2つのXTALを使い分けます.
  44.1kHz系列は45.1584MHzのXTAL
  48kHz系列は49.152MHzのXTAL

つまり、当deviceのサンプリング周波数を、44.1kHz→48kHzのように切り替えるとき、XTALを切り替えなくちゃいけません.これでちょっとした課題が生じます.

課題1
ロジック回路はむやみにclockを切り替えると誤動作する可能性があります.ヒゲのように細くて周波数が高いclockが入るとロジックは発狂するんですね.そこで、45M/49Mの切り替えには工夫が必要です.

課題2
使ってない方のXTALはオフしたい

課題3
XTALをオンした直後の起動時間がどれだけかかるのか不明(実測で0.3mSecなど)

対策
1)45M/49Mを切り替えたら、その後でロジックをresetする
2)STOP時には45M/49Mを両方ともONにしておく
3)再生開始時に不要なXTALをOFFする


ロジック回路のverilog codeはこのようにしてあります.

1)外部ピン xrstとPC9_resetの両方からresetできるようにする
assign xrst2 = xrst & ~PC9_reset;

always@(posedge clk or negedge xrst2)
  if(!xrst2) bck<=0;
  else if(seq_en) bck<=~bck;

2)XTALのenable信号を出力する   clk49en,clk45en
3)XTALのenableを両方ともONにできるようにする  PA12_clkbothon
assign clk49en = PB2_clksel | PA12_clkbothon;
assign clk45en = ~PB2_clksel | PA12_clkbothon;


STM32はGPIOを操作します.
45M/49Mの切り替え操作    →PB2_clksel 
HAL_GPIO_WritePin(FPGA_CKSEL_PRT, FPGA_CKSEL_BIT, (Fs_ctl&4)>>2 );

FPGAをresetする操作   →PC9_reset
HAL_GPIO_WritePin(FPGA_RESET_PRT, FPGA_RESET_BIT, 1);

XTALを両方ともONする操作(STOP時)  →PA12_clkbothon
HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 1); 

使っていないXTALをOFFする操作(再生開始時)  →PA12_clkbothon
HAL_GPIO_WritePin(FPGA_CKBON_PRT, FPGA_CKBON_BIT, 0); 


かしこ

2020年5月20日水曜日

STM32でDCCを作る方向で (16) CPUをいじっている個人的理由

今回はいつものSTM32 DCCの件とは趣向を変えて「どうしてCPUをいじっているのか?」を書こうと思う.

一言でいえば、取り戻すためだ.

さきほど当ブログ開設当時の人気シリーズである「失業理由INDEX」を作った.失業理由シリーズを連投してたのは、わたしが失業した2011年である.民主党政権と白川日銀のせいで悪夢の円高に日本経済が殺されていた頃だ.当然奴らへの呪いの言葉が何度も登場する.民主党しね.白川しね.

失業理由シリーズの主役は1987~2010まで企業所属の回路設計屋として過ごしたわたしだ.その間のわたしは、CPU周りの仕事を避けて生きてきた.その後遺症で、今でもCPU systemやOSについては詳しくない.その失われたCPU技術を取り戻すのがいまわたしがSTM32やEZUSBをいじっている理由だ.

なんでわたしはCPUを避けてきたのか?

CPU以外に手薄な分野が在ったからである.どの分野かというと「信号処理」だ.わたしは他の技術者から不人気な信号処理の仕事ばかり進んで引き受けてきたのである.

技術者という生き物は、大勢の同僚技術者がいる分野が好きなようだ.CPU systemやソフトや制御には、技術者がわんさか居た.新人技術者はその輪の中に入りたがった.孤独は嫌ですか?

わたしは逆張りしてた.
仲間がたくさんいたら、組織の中で唯一の人材になりにくいじゃん.だから誰もいない分野を歓迎した.孤高な立場に成れてナンボだと思っていた.信号処理屋はたいていは空席だった.
それに、自分が参加する製品を成立させるための多様な技術分野のうちで、手薄な信号処理屋は設計部隊の技術的弱点であると察知してもいたので、誰かがやらなくちゃダメじゃんという判断もあった.
「電磁変換系や信号処理をやらしてくれ」といって上司に止められる事は一度もなかった.

わたしが社会人になって数年間は、DATとVHSの仕事だった.
ヘッド&テープ&録再回路と、VHSではビデオ信号を設計した.
VHSの時は、さほど必要でもないのにトランジスタ回路をたくさん設計した.1980年代終わりの頃の新人電気屋でトランジスタ回路に執着している奴なんか他に誰も居なかった.その後も同年代でトランジスタ好きな奴に会ったことは無い.まして年下においておや.

当ブログの「電撃トランジスタ講座」はその頃に得た知識である.

30歳ごろ、日本HPに勤めていた頃は、HDDの録再回路を設計.

失業するまで勤めたソニーではテープストリーマの録再回路を設計してた.この頃には再生回路を全部digital信号処理に置き換える事に熱を入れてた.

というわけで、キャリアのほぼ全部が磁気記録の録再回路=信号処理だった.

CPUをやったのは社会人1~2年目に少しやっただけだった.(8085アセンブラ)

------
約20年間に、8bit→16bit→32bitになり、CPUはARMになり、開発環境はwinになり、PCBは¥2000で買え、USBを繋げば焼けてしまうようになった.大人のおもちゃとしてかなり楽しめるようになった.

だがわたしは、その20年の進歩をひたすら脇見でスルーしてきたのである.

2011年から失業状態なので、失われた20年間を取り戻してみようかと思い、様々なCPUを触ってみるようになった.

2011年、1chipマイコンを触ってみるかと思っていじくり倒したのがSTM8Sだった.  →こちらのページはメンテしてないけどまだ生きてる

STM32は2012年に触り始めた.

2015年にEZ-USBをいじり倒す.   →こちら

Arduinoは2017年から使い始めている.便利.

------
なんとなく好きなCPUランキング

1位     STM32      リソース豊富なのでなんでもできる.その割に安い.

2位     Arduino      sample codeが豊富なのでDLすりゃなんでもできる.安価.

3位     EZ-USB      480Mbpsにしては安価で良いが、peripheralが貧弱.

4位     STM8S       Arduinoの便利さには負けている.


CPUのお勉強は趣味であって、就職のための勉強じゃないんです.

かしこ


2015年12月25日金曜日

Ether ShieldのW5100をSTM8Sから動かしてみる(7)TCP似非webサーバ

メリークリスマス.(メリーって何だろう?)

諸般の事情で家族で夕飯を食べれなかったため、家から歩いて2分ほどの「ノムネン」で夕食がてら奥さんと呑んだのですが、いつも混雑しているノムネンが今日はガラ空きでした.クリスマスイブのカップルはノムネンには来ないのか? 二次会でノムネンに来るカップルが居るかもと23時まで滞在しましたが、全てのカップルはホテルへチェックインしてしまったようで誰二人来店しませんでした.マスターの井上さんは早々と帰宅してしまうしw... クリスマスイブのカップルは恵比寿界隈で心と体を温め合っているのでしょうねぇ.
去年のクリスマスイブは「青の洞窟」効果によってノムネンの辺りにまで人が流れてきてたそうですが、今年は青の洞窟やらないのでホント静かなもんですわ.

------------
Ethernetの末端に置いたW5100を制御するために、webでW5100に接続してコマンドを与えたい.それがこの連載の目的です.そのためにW5100をweb serverとして立てたい.なのでW5100のTCPを実装し、かつserverモードで動かすのが最終型に近いです.その先は、TCP serverにHTTPのやりとりを実装すればより最終型に近づきます.

というわけで今回は、TCP serverでHTTPを送受信できるようにし、webブラウザでW5100に接続するところまでを動かします.

ところで、serverモードとclientモードはどう違うのか?

イメージを浮かべやすいのはserverモードだと思います.(下図の左側)
あらかじめW5100を起動し受信体勢(LISTEN)で待ち受けさせときます.clientはwebブラウザです.webブラウザのURLに例えば、
http://192.168.1.100:12340/
のように入力します.ここで192.168.1.100はW5100のIPアドレス、12340はW5100の待ちうけポート番号で、サンプルプログラムでそのように決め打ちしてあります.webブラウザがW5100に接続要求してW5100が接続許可するとデータ通信できます.通信が終わったら切断.あたりまえですがserverは先に起動していなくちゃいけません.

次にclientモードでは、下図右側のように、先んじてPCが起動(server)しているところへ、W5100が接続要求(client)します.以後同様.

以下は、serverモードで似非webサーバーを実装してみた結果ですが、webサーバですからTCPで通信する中身はHTTPのデータになります.

待ち受けしているW5100にwebブラウザが接続し、HTTPのやり取りをしているときのW5100の内部状態をUART経由でモニタしたのが以下です.なにやらいろいろとやり取りしているようで面白いです.
なお、今回のwebサーバはバカピーなのでいつもHello World!という応答しかしません.

STM8Sをリセット
   ↓
   中略
   ↓
(TCP 14 LISTEN)         W5100はTCP serverモードで待ち受け状態
(TCP 16 SYNRECV)       webブラウザから接続要求が来た
(TCP 17 ESTABLISHED)    W5100とwebブラウザが接続確立した
[TCP server recv] reg BYTE=356(0164)   ブラウザからW5100へ要求あり
GET / HTTP/1.1         ここから
Host: 192.168.1.100:12340
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ja,en-US;q=0.8,en;q=0.6     ここまで
[TCP server response]       W5100からブラウザへのレスポンス
HTTP/1.0 200 OK           ここから
Content-Type: text/html
Content-Length: 21
<h1>Hello World!</h1><HR>    ここまで   「Hello World!」が返答
     ↓
ブラウザにHello World!と表示される
     ↓
(TCP 1c CLOSE_WAIT)     ブラウザから切断要求が来た
(TCP 00 CLOSED)        W5100切断
(TCP 14 LISTEN)         再びW5100待ち受け状態へ
(TCP 16 SYNRECV)       webブラウザから接続要求が来た
(TCP 17 ESTABLISHED)    W5100とwebブラウザが接続確立した
[TCP server recv] reg BYTE=334(014e)   ブラウザからW5100へ届いた内容
GET /favicon.ico HTTP/1.1  ここから    faviconを送れという要求
Host: 192.168.1.100:12340
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept: */*
Referer: http://192.168.1.100:12340/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ja,en-US;q=0.8,en;q=0.6     ここまで
[TCP server response]      W5100からブラウザへのレスポンス
HTTP/1.0 200 OK          ここから
Content-Type: text/html     
Content-Length: 21
<h1>Hello World!</h1><HR>  ここまで  バカピーなのでいつも「Hello WorlD!」を返す
(TCP 1c CLOSE_WAIT)      ブラウザから切断要求が来た
(TCP 00 CLOSED)         W5100切断
(TCP 14 LISTEN)          再びW5100待ち受け状態へ
    ↓
ブラウザがあきらめちゃって、以後は要求が来なくなる

上記の内部状態を見ると、
待ち受け→接続→HTTP要求→HTTPレスポンス→切断→待ち受け
という流れをグルグル廻る様がよくわかります.「HTTP要求→HTTP返答」の部分を賢くしてゆけば、賢いwebサーバーになるわけですね.(現状はバカピーですが)

このように実際に動かしてみると、OSI参照モデルにおける、Ethenet→ARP→IP→TCP→HTTPというスタック構造がよく見えて面白いもんです.
-----
今回のサンプルコードはこちらですが、このコードで受けたあなたの損害には関知しませんので、ハマったら素直に死んでください.開発環境とかハードウエアの解説は過去記事を参照してください.

解凍したフォルダの中にあるmain.cの中にある、この部分がTCP serverとICMPのルーチンを繰り返し呼び出しているところです.
  while (1)  // main loop
    {
      main_menu();
      TCP_server();
      ICMP();
    }
TCPはSOCKET1で、ICMPはSOCKET3で動かしています.ICMPが動いているのでpingが通ります.

TCP serverモードのキモのルーチンであるTCP_server()は、ip.cの中に記述があります.いつものようにベターっと記述してあるので、見苦しいですが判りやすいかと思いますが、やってることはW5100のデータシートに書かれている通りです.

6へ   8へ

かしこ


=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...




人気ブログランキングへ

2015年12月21日月曜日

Ether ShieldのW5100をSTM8Sから動かしてみる (6)TCP死亡中

UDPとpingが通ったので、つぎはTCPを通そうと思うわけですが、、、謎のトラブルが発生しているところです.

読者の皆さんはすでにご存知かもですが、わたしはW5100を弄り始めて初めて知ったのですが、ネットとかLANとかEthernetというとTCP/IPなどという用語が連想され、ゆえにLANのプロトコル=TCPという認識しかありませんでした.
しかし、実際にはUDPやICMPといったプロトコルもあり、それらの方が通信が簡単なので勉強がてらUDPやICMPを実装してこれまで遊んできたわけでした.もっとも、ネットを飛び交っているプロトコルはTCPが90%を占めるらしいですが.

UDPやICMPが簡単である理由は、それらはpacketを送りっぱなしだからで、接続やエラー再送なんかしないので実装が簡単だというわけです.
ところがTCPは、先んじてマシンA-マシンBでネゴシエーションして接続を確立してからデータを送受信する、エラーがあったら再送する、などがあるので実装がめんどくさい.

それで実験です.
W5100をTCPモードで待ちうけ(サーバ 192.168.1.100)して、こちらのページを参考にしてクライアント(192.168.1.2)からチョイとTCP接続してチョイと"HELLO"を送信するようにして、W5100がTCPで動いた確認としたい.

 クライアントのrubyプログラム1
 #!/usr/bin/ruby
 require "socket"
 sock = TCPSocket.open("192.168.1.100", 12340)
 sock.write("HELLO")
 sock.close

ところが、W5100が"HELLO"を受信できません.これは困った.

packet monitor でサーバ(192.168.1.100)とクライアント(192.168.1.2)の間を飛び交うpacketを観測したのがこちら.
tshark -i 1 -f 'port 12340'
[NG]
  1 0.000000  192.168.1.2 -> 192.168.1.100 TCP [SYN] Len=0
  2 0.000183 192.168.1.100 -> 192.168.1.2  TCP [SYN, ACK] Len=0
  3 0.000229  192.168.1.2 -> 192.168.1.100 TCP [ACK] Len=0
  4 0.000333  192.168.1.2 -> 192.168.1.100 TCP [PSH, ACK] Len=5
  5 0.000380  192.168.1.2 -> 192.168.1.100 TCP [FIN, ACK] Len=0
  6 0.000801 192.168.1.100 -> 192.168.1.2  TCP [ACK] Len=0

SYNやACKなどのキーワードが飛び交っていて、TCPのネゴシエーションは正常っぽく見えます.4行目でLen=5となっているのは"HELLO"が5文字だからであって、データ伝送も正常っぽい.

けれど、W5100の内部の受信バッファには"HELLO"が入ってこない.つまり受信不能です.

W5100の内部ステータスをチェックすると、TCP接続確立の直後にSOCKET CLOSEになってしまっている.どうしてだろう???

-----
クライアントのrubyプログラムにdelayを挿入してみたら、W5100が"HELLO"を受信できるようになりました.socketをcloseする前にあるfor~end文がdelayです.

 クライアントのrubyプログラム2
 #!/usr/bin/ruby
 require "socket"
 sock = TCPSocket.open("192.168.1.100", 12340)
 sock.write("HELLO")
 for i in 0..10000 do
 end
 sock.close

クライアントがこれ↑ならW5100が正常動作するわけですから、飛び交うpacketの時刻がどれだけ変わったかに着眼してみます.tshark表示の2列目がpacketが飛んだ時刻(秒)です.

5行目のFINと書かれたpacketがSOCKET CLOSEの意味を持っています.そのpacketが飛んだ時刻は+2.397mSec後になっています.一方で動作不良なケースでは、5行目の時刻は+0.380mSec後と遥かにせわしなく動いています.
[OK]
  1 0.000000  192.168.1.2 -> 192.168.1.100 TCP [SYN] Len=0
  2 0.000183 192.168.1.100 -> 192.168.1.2  TCP [SYN, ACK] Len=0
  3 0.000227  192.168.1.2 -> 192.168.1.100 TCP [ACK] Len=0
  4 0.000333  192.168.1.2 -> 192.168.1.100 TCP [PSH, ACK] Len=5
  5 0.002397  192.168.1.2 -> 192.168.1.100 TCP [FIN, ACK] Len=0
  6 0.002558 192.168.1.100 -> 192.168.1.2  TCP [ACK] Len=0

というわけで、本日のトラブルはクライアントの速度にW5100が追いついてないと見える状況ですが、これってなにげにfuckな感じがしちゃっているところです.

追記1:
治ったわけではありませんが、内部状態をモニタするために各所に挿入してあったUARTへの送信命令を削除すると、不具合が軽減されました.やはりW5100の動作を妨げないのが吉であるらしいです.

追記2:
治ったわけではありませんが、rubyプログラム末尾のsock.closeを削除すると、正常に動作します.sock.closeを削除すると、close要求packetの送信時刻がわずかに遅延するのでW5100をcloseに叩き込む作用が遅延し、その結果正常動作に持ち込めていると推測します.

追記3:
治ったわけではありませんが、rubyプログラムをあきらめて、代わりにブラウザのchromeからW5100にTCP接続してみます.chromeのURLにこれを与えます.
http://192.168.1.100:12340
すると、chromeがW5100とのTCPネゴシエーションに成功し、chromeが送信したHTTPのメッセージをW5100が受信できました.rubyだと死ぬがchromeなら動くという微妙な情勢です.
 [TCP recv] reg BYTE=356(0164)  bufadrs=6800
 GET / HTTP/1.1
 Host: 192.168.1.100:12340
 Connection: keep-alive
 Accept: text/html,application/xht
rubyから受信できない不具合を治せる気がしないのと、実をいうとTCPの目的はHTTPサーバーを実装することなので、chromeとの通信が正常なら許そうと思ふ.

5へ   7へ

エイメン

=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...

2015年12月20日日曜日

Ether ShieldのW5100をSTM8Sから動かしてみる (5)ping

上司が言うんですよ、「pingが通らなかったらnetwork機器とは云えないぞ」.確かにそうだと合点して、pingに応答するようにしました.

ところがW5100でpingに応答するにはちょっとかったるいんです.w5100のUDPやTCPはautomaticに動く感が強いのですが、ping応答はそうではなく自力でごにょごにょせにゃいかんみたい.

なぜかというと、UDPやTCPと同列に語られる(と思う)ICMPというのがあって、pingはICMPを活用して行われる.ゆえにW5100でICMPを動くようにする必要がある.さしずめTCP(ICMP)を土台としてHTTP(ping)が動くような比喩をできるのではないかと思う.
しかし、W5100にはICMPモードというのは存在せず、RAWIPモードを使ってICMPを実装する必要がある.RAWIPモードでは、送受信データの受け渡しのみをW5100が請け負いますが、それだけしかやりませんよというモードです.

pingを使ったことのある人はご存知でしょうが、①マシンAからマシンBにpingを打つと、②マシンBがマシンAに受領の旨返信する、という2段階のpacketの送受でpingが通ったなどと称します.
ゆえに、①のpacketを少し加工して②のpacketとして返信する必要があるのですが、W5100はICMPをautomaticで返信してくれず、①→②の加工をユーザーが自分でやる必要があるぞと、そんな状況だったわけだった.

------------
W5100のRAWIPモードはどこまで面倒を見てくれるんだろう?

↓Ethernet Cableへ降りてゆくにつれて色々なヘッダが追加されてゆき、ICMPフレームは最終的にこんなになっているそうです.W5100のRAWIPモードはデータの送受だけやりますと上で書きましたが、それはどの部分なのかというと、下図の青で囲った部分だけです.他の部分はバックグラウンドで捨てられて見えません.
↓①W5100の受信バッファに積まれるデータの具体例(=青囲い)はこんなです.
送信元IP+データ数で6BYTEが先頭に付加されていて、これはW5100がサービスで付けてくれたデータです.+6以降の40H BYTEが上図の青囲い部分です.タイプ+コード+チェックサムがあります.その後続はユーザーデータで、ping commandが好きに挿入したデータですからdon't careでOK.タイプ=08だとping要求の意味があります.
↓詳細の説明は後でしますが、②ping返信データはこうなります.受信データから変えたところは、送信元IPとデータ数を捨てて、タイプとチェックサムを変更しています.タイプ=0だとping返信の意味があります.これをW5100の送信バッファに積んで「送信命令」をW5100に与えるとping返信が成立します.

問題はチェックサムのところでした.IPのチェックサム計算をするのは未体験ゾーンなため.
1の補数のチェックサムの手順は以下のようになります.IPに登場する1の補数のチェックサムは皆同じやり方みたいです.
 1)チェックサム部分をとりあえず0000にしておく
 2)データを、16bitごとに区切る.この例では32ワードのデータに区切れる
3)32ワードを全部足します.ここで、桁あふれを保存するために32bit加算する
000505fa
4)上位16bitと下位6bitに分けて32bit加算する
000005FF
5)4と同じことをもう一度やる.ここでは結果は同じになる.2度やる理由はよくわからないが、IPだと高々1500BYTEなので2度で済むということなのかもしれない.

6)それの下位16bitを残す
05FF
7)bit反転すると、FA00が出てきました.おしまい
FA00

というわけで、言葉で書くとping受信→ping返信の手続きはこれだけで動くようです.

---------
コピってチェックサムを入れ替えるって簡単そうですが、W5100の制御コードはずいぶんと長いものになりました.

sizeRX = getSn_RX_RSR();  ping受信データBYTE数
if(sizeRX==0) goto EX;   ping受信データ=0ならスルー

sizeRX = sizeRX & bufMUSK;  ping受信データBYTE数
topRX = bufRXtop + (getSn_RX_RD()&bufMUSK); 受信バッファ先頭アドレス

ip[0] = w51RD(bufRXarign(topRX  ));  ping発信元マシンのIPアドレス
ip[1] = w51RD(bufRXarign(topRX+1));
ip[2] = w51RD(bufRXarign(topRX+2));
ip[3] = w51RD(bufRXarign(topRX+3));

     中略)

以下はping返信の手続き

r16 = getSn_TX_FSR();   送信バッファ空きBYTE
r8 = getSn_SR();    動作モード
if(tim3_reset_seq!=0 || r16<sizeRX || r8!=SOCK_IPRAW) goto UPDATERXBUF;      送信バッファ空きとか諸々をチェック

topTX = bufTXtop + (getSn_TX_WR() & bufMUSK); 送信バッファ先頭アドレス

ptrRX = 6;  受信pingデータを送信バッファにコピーする
ptrTX = 0;
do
  {
    adrsRX = bufRXarign(topRX+ptrRX);
    adrsTX = bufTXarign(topTX+ptrTX);
    if(ptrTX==0) w51WR(adrsTX,0);     タイプ=0
    else if(ptrTX==2) w51WR(adrsTX,0);  チェックサム=0
    else if(ptrTX==3) w51WR(adrsTX,0);  チェックサム=0
    else w51WR(adrsTX,w51RD(adrsRX));  他はそのままコピー
    ptrRX++;
    ptrTX++;
  }while(ptrRX<sizeRX);

chksum32=0;    送信pingのチェックサムを計算する
sizeTX=sizeRX-6; 送信データは受信データよりも6バイト少ない
ptrTX=0;
do{
  uint8 H,L;
  H=w51RD(bufTXarign(topTX+ptrTX  ));
  L=w51RD(bufTXarign(topTX+ptrTX+1));
  chksum32 += (uint16)(H<<8) + L;    32bit加算
  ptrTX+=2;
}while(ptrTX<sizeTX);
chksum32 = (chksum32>>16) + (chksum32 & 0xFFFF); 上位16+下位16
chksum32 = (chksum32>>16) + (chksum32 & 0xFFFF); 上位16+下位16
chksum32 = ~chksum32;            bit反転
chksum16 = chksum32 & 0xFFFF;   下位16bitのみでチェックサムを得る

送信バッファにチェックサムを記入する
w51WR( bufTXarign(topTX+2) , (uint8)(chksum16>>8) );
w51WR( bufTXarign(topTX+3) , (uint8)(chksum16&0xFF) );

setSn_DIPR(ip);  返信相手のIPをW5100にセットする

setSn_TX_WR( getSn_TX_WR() + sizeTX );  送信バッファポインタ更新
setSn_CR( Sn_CR_SEND );    送信コマンド
while( getSn_CR() != 0 ){}

UPDATERXBUF:
setSn_RX_RD( getSn_RX_RD() + sizeRX );  受信バッファポインタを更新
setSn_CR( Sn_CR_RECV );    受信コマンド
while( getSn_CR() != 0 ){}

EX:

------------
サンプルプログラムでは自機IPアドレス=192.168.1.100にしてあるので、
ping 192.168.1.100
Linuxあるいはwindowsマシンからpingを打つと、本機からの応答を観測できるはず.

例によってサンプルプログラムであなたが受けた損害がなんであれわたしは知りませんぞ.

さて、次はTCPの実装だ.

4へ   6へ

かしこ

=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...

2015年12月17日木曜日

Ether ShieldのW5100をSTM8Sから動かしてみる (4)UDP全二重

ソケットって、全二重(full duplex)で動くんでしょうか?という素朴な疑問を抱いています.

Linuxやwindowsのsocket()recv()send()を利用するぶんには、APIから見たら全二重っぽく見えるのはもちろんそうでしょうが、回路としてのソケットは半二重なんだけどソフトで全二重に見せかけているのかなぁなどという疑問.
 (A)単一ソケットで送受信できるならソケットは全二重
 (B)ソケット0を送信専用、ソケット1を受信専用に設定せないかんならソケットは非全二重
ちなみにEthernet cableは全二重構造になっているらしい.10M/100Mの信号はcableを全二重で利用しているらしい.1000Mでもそうなのかは知らない.

それでW5100のソケットを弄ってみたら、
ソケットというものは、回路として全二重で動くみたいだ
というのが結論です.なかなか便利に作ってあるもんですね.

----
以下、ソースコードについて.(エラー処理とか一切やってません)

W5100はTCP,UDPなどのプロトコル処理をお任せでやってくれるので、ユーザー(CPU)がやる仕事は次の3つでだいたい事足ります.
 1)レジスタを初期設定する
 2)受信処理をする (受信バッファから読む)
 3)送信処理をする (送信バッファへ積む)
下記を見るとわかるように、送受信バッファのアクセス以外には、W5100の取り扱いに関与する操作としてはポインタ計算+αぐらいしかやっていませんので楽チン.

1)レジスタに初期設定をする
上半分が自機のIPやMACの設定.(割り込みは使ってない)
ここでは、自機IPを 192.168.1.100 ポート12345 に仮決めしてあります.
下半分がソケット0の設定で、相手のIPやPORTを設定.(ソケット1,2,3は放置プレイ)
ここでは、パケットの送り先を 192.168.1.5 ポート12345 に仮決めしてあります.
(上半分)
  // common registers
  // gateway address
  ip[0] = 192; ip[1] = 168; ip[2] = 1; ip[3] = 1;
  setGAR( ip );   // (1-4)
  // subnet address
  ip[0] = 255;ip[1] = 255;ip[2] = 255;ip[3] = 0;
  setSUBR( ip );  // (5-8)
  // MAC address   自機のMAC
  ip[0]=0x00; ip[1]=0x08; ip[2]=0xDC; ip[3]=0x00; ip[4]=0x00;
  ip[5]=0x4F;
  setSHAR( ip );  // (9-e)
  // IP address  自機のIPアドレス
  ip[0] = 192; ip[1] = 168; ip[2] = 1; ip[3] = 100;
  setSIPR( ip );  // (f-12)
  setIR(0);       // (15) no interrupt
  setIMR(0);      // (16) no interrupt
  setRMSR(0x55);  // (1A) read buf 2kB each
  setTMSR(0x55);  // (1B) send buf 2kB each
(下半分)
  // set socket 0 registers for UDP   UDPモード
  setSn_MR(s,Sn_MR_UDP);  // (400) UDP mode
  setSn_IR(s,0);          // (402) no interrupt
  // source PORT number 12345-->0x3039  自機PORT番号
  ip[0]=0x30; ip[1]=0x39;
  setSn_PORT(s,ip);       // (404-405)
  // destination IP  相手のIPアドレス
  ip[0] = 192; ip[1] = 168; ip[2] = 1; ip[3] = 5;
  setSn_DIPR(s,ip);
  // destination PORT number 12345-->0x3039  相手のPORT番号
  ip[0]=0x30; ip[1]=0x39;
  setSn_DPORT(s,ip);
  // open socket 0   ソケットOPEN
  setSn_CR(s,Sn_CR_OPEN); // (401) socket open command
  while(getSn_SR(s)!=SOCK_UDP){}    // wait UDP
  sprintf(UARTstrTX,"=====W5100 Socket#%d UDP start=====\r\n", s);

2)ソケット0の受信処理をする (受信バッファから読む)
void UDP_recv(void)
{
  r=getSn_RX_RSR(s);  UDP受信データバイト数を示すレジスタを読む

  if(r!=0) UDP受信データ在りなら以下を実行
    {
      // 受信バッファポインタを計算する
      size = getSn_RX_RSR(s) & bMUSK; // RX data size
      offset = getSn_RX_RD(s) & bMUSK; // buffer pointer
      adrs = bRX(s)+offset;

        中略      
      for(i=8;i<size;i++)  受信バッファを読んでUARTに出力する
        {
          adrs = bRXA(s,bRX(s)+offset+i);
          r = w51RD(adrs);
          sprintf(UARTstrTX,"%c", r<32?32:r);
          SerialPutString(UARTstrTX);
        }

      adrs = getSn_RX_RD(s)+size;  受信バッファポインタを進める
      setSn_RX_RD(s,adrs);

      setSn_CR(s,Sn_CR_RECV);  W5100の受信動作をkickoff
    }
}

3)送信処理をする (送信バッファへ積む)
STM8Sのタイマ3割り込みで1秒毎に送信するようにkickされるようになっていまして、SOCKET_SEND_DATA_EXIST=1にセットされます.(タイマ3割り込みルーチン内で送信するのはSPIバスが不具合になるのでやめました)
void UDP_send(void){
  if(SOCKET_SEND_DATA_EXIST==1)  送信データがあれば以下を実行
    {
      freeb = getSn_TX_FSR(s); 送信バッファの空き
      sr = getSn_SR(s);  UDPモードかどうかをチェック
      if(freeb>=100 && sr==SOCK_UDP) 送信バッファの空きチェック
    {
        // 送信バッファポインタを計算する
        offset = getSn_TX_WR(s) & bMUSK; // TX buffer pointer

        // なんでもよいが送信データを用意する
        sprintf(UARTstrTX,"TIM3 int %04d   free%04x SR%02x 
          offset%04x\r\n",
                tim3cnt,
                freeb,
                sr,
                offset);
        size = strlen(UARTstrTX);

        for(i=0;i<size;i++){ 送信バッファへコピる
          adrs = bTXA(s,bTX(s)+offset+i);
          w51WR(adrs,UARTstrTX[i]);
        }

        adrs = getSn_TX_WR(s)+size; 送信バッファポインタを進める
        setSn_TX_WR(s,adrs);

        setSn_CR(s,Sn_CR_SEND);  送信コマンド
        while(getSn_CR(s)!=0){} // wait send ending
      }
      SOCKET_SEND_DATA_EXIST=0;
    }
}


ハードウエア
連載3回目ではこのようにSPIを接続しましたが、
今回からはSTM8SがW5100をリセットできるようにRESET線を一本追加しました.
STM8S 24pin PE6 → W5100 59pin RESET

使い方
(2)UDPパケット受信
本機を家庭内LANに接続し、外部から本機192.168.1.100 ポート12345に向けてUDPモードで送信すると、本機が受信した内容をUARTに出力します.packet発生にはipsendwinでも使ってください.
(3)UDPパケット送信
本機は1秒ごとにUDPpacketを送信します.宛先は、192.168.1.5 ポート12345です.192.168.1.5マシンでpacket monitor toolで観測すりゃUDP packetが毎秒来てるのがわかります.
←この写真はEther未接続だorz

Linuxのsdccでコンパイルするソースフォルダはここです.エラー処理とか一切してません.
コンパイル方法などは連載3を参照のこと.
このソースによりリーダーやメンバーが捕えられ、或いは殺されても当局は一切関知しないのでそのつもりで.

3へ   5へ

かしこ

=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...

2015年12月13日日曜日

Ether ShieldのW5100をSTM8Sから動かしてみる (3)UDP受信

W5100を動かしてみるトライはそれなりに苦心しまして、ようやくUDPが動きました.判ってしまえばW5100を動かすのは意外に簡単でした.

当初はSocketが全然判らないので、PCやLinuxのSocketプログラムについての書籍はチラチラと読みました.Socketのあらましについて理解できたところで、W5100をUDPで動かすぶんにはsocketの知識はあまり必要ではありませんでした.W5100のdatasheetに解説されている手続きどおりにダラーッとcodingすりゃUDPぐらいなら動ごかせそうです.というかそれで動いた.

以下は、windowsが送信したUDP packetを、W5100が受信する動作を実現するものです.
ソースコードの入ったLinuxのフォルダをzipにしてこちらに置きました.
http://yahoo.jp/box/uamBA5
この情報であなたが損害を受けてもわたしは責任を負いません.

【ハードウエア】
・W5100は、arduinoのEternet shield V5を使用.Ethernet shieldの詳細はこちらを参照.
・CPUは、STM8Sを使用.UARTでPCに接続する.
・STM8S---W5100はSPIで接続する.SPI信号はこの4本です.
【開発ツール】
・開発マシンはLinux.
・STM8Sのクロスコンパイラとして、Linux上で動くsdccを使う.sdccインストールはこちらを参照.
・STM8S FLASH焼きは、Linux上で動くstm8flashを使う.

【デバッグ環境】
・家庭内LANを使う.(もちろん有線LAN)
・windows7のipsendwinからUDP packetを送信する.
・W5100で同packetを受信する.
・W5100のUDP受信データを、UART経由でwindowsマシンへ送る.
・windowsはtera termでUARTデータを受信し表示する.
・想定どおりに表示されたらよかよか

-----
【W5100の感想】
W5100はハードウエアだけでTCP,UDP,PPPOEなどのプロトコルを処理してくれます.
なので、CPUを搭載してません.
なので、SPI越しのSTM8Sから見えるW5100は、純粋にレジスタとして見えます.
CPUの処理負担が少ないので8bit CPUでも大丈夫です.
W5100にはSocketが0-3の4つ内蔵されているので、同時に4つの送受信ができます.

W5100のレジスタは、
・W5100のIPやMACアドレスを収蔵するための、0~30H番地までのレジスタ群と、
・Socket0-3の相手IPなどを収蔵するための、レジスタ群があります.
    400H~  Socket0
    500H~  Socket1
    600H~  Socket2
    700H~  Socket3

W5100の送受信バッファは、
    送信バッファ 4000~5FFFH  (8kBYTE)
    受信バッファ 6000~7FFFH  (8kBYTE)
と在りますが.これらを4つのSocketで分けあって使うのでアドレッシングは要注意です.

W5100の送受信バッファはSocket0~順に割り振られますが、各Socketに割り当てるサイズを1k, 2k, 4k, 8kBYTEからセレクトできます.なので、この割り当てサイズによって、各Socketのバッファの先頭アドレスが変動します.地道に計算するしかありませんが、わたしはめんどくさいので、全部等しく2kBYTEに割り振って、次のように決め打ちしました.
    4000H Socket 0 TX buffer (2kBYTE)
    4800H Socket 1 TX buffer (2kBYTE)
    5000H Socket 2 TX buffer (2kBYTE)
    5800H Socket 3 TX buffer (2kBYTE)
    6000H Socket 0 RX buffer (2kBYTE)
    6800H Socket 1 RX buffer (2kBYTE)
    7000H Socket 2 RX buffer (2kBYTE)
    7800H Socket 3 RX buffer (2kBYTE)

W5100の送受信バッファのアドレッシングにはもう一つ要注意があります.
USBでは、どんなに小さいサイズのpacketでも、1つのpacketを受信したらFIFOがそれで満杯になってしまい、あまりにも矢継ぎ早にpacket送信するとFIFOが便秘になります.
一方で、W5100の送受信バッファの特徴はこうです(W5100のというよりもEthernetコントローラ全般に同様と思います)
    1) リングバッファである
    2) ひたすらpacketをバッファにブタ積みしつづける
    3) ゆえにリングバッファのポインタがある
バッファ領域を踏み外すと隣のSocketを踏み荒らしてしまうので、リングバッファと成すためにアドレッシングにケアが必要です.

【ソースコード】
ソースコードのzipを解凍するといろいろなファイルが出てきます.
・stm8s_xxxx.x というファイルは、ST社が提供するSTM8S用のライブラリ
・usrlib-spi-w5100.x というファイルは、SPIでW5100のレジスタを読み書きするサブルーチン
・usrlib-uart.x というファイルは、STM8SがPCとUARTで通信するサブルーチン

以上はW5100とはあまり関係の無いファイルです.

W5100とモロに関係のあるのは、
  w5100.h
  w5100.c
  main.c
です.

(w5100.h)
・w5100.hの上の方では、W5100のレジスタ関係の定義をやっています.
・最後の方にはレジスタアクセスサブルーチンを宣言しています.setXXX()とgetXXX()はそれぞれがXXXというレジスタに書くか(set)、読むか(get)です.SOCKET sにはソケット番号0~3を代入します.main.cでs=0に決めています.

(w5100.c)
・ひたすら、setXXXとgetXXXを機械的に書いています.一部しか動作確認してないのでバグってるかもしれない.

(main.c)
ここは重要箇所です.

まず、レジスタの初期設定ですが、たったのこれだけと云ってよいでしょう.ここでsetXX()はレジスタXXに値を書くだけのルーチン.getXX()はレジスタから値を読むルーチン.ゆえに大したことはやっていない.
雰囲気的には、こんなかんじ.
 1) レジスタにIPやMACをセットし、UDPモードにセットし、GO!するのみ
 2) データ到着はポーリングまたは割り込みで気付く
 3) データはリングバッファで出し入れする
  // common registers
  setMR(0x80);    // (0) w5100 reset
  ip[0] = 192; ip[1] = 168; ip[2] = 1; ip[3] = 1;
  setGAR( ip );   // (1-4) gateway address
  ip[0] = 255;ip[1] = 255;ip[2] = 255;ip[3] = 0;
  setSUBR( ip );  // (5-8) subnet address
  ip[0]=0x00; ip[1]=0x08; ip[2]=0xDC; ip[3]=0x00; ip[4]=0x00;
  ip[5]=0x4F;
  setSHAR( ip );  // (9-e)  MAC address
  ip[0] = 192; ip[1] = 168; ip[2] = 1; ip[3] = 100;
  setSIPR( ip );  // (f-12)  IP address
  setIR(0);       // (15) no interrupt
  setIMR(0);      // (16) no interrupt
  setRMSR(0x55);  // (1A) read buf 2kB each
  setTMSR(0x55);  // (1B) send buf 2kB each
  
  // set socket registers for UDP
  setSn_MR(s,Sn_MR_UDP);  // (400)  UDP mode
  setSn_IR(s,0);          // (402) no interrupt
  ip[0]=0x30; ip[1]=0x39; //  PORT number 12345-->0x3039
  setSn_PORT(s,ip);       // (404-405)
  setSn_CR(s,Sn_CR_OPEN); // (401)  socket open command
  r=getSn_SR(s);          // (403) status
  while(r!=SOCK_UDP){}    // wait to be UDP

初期設定のキモの部分は①~⑤くらいかと思います.
①IPを192.168.1.100に設定していますが、これがW5100の自機IP(任意)です.
②MACを00:08:DC:00:00:4Fに設定していますが、これがW5100の自機MACアドレス(任意)です.MACアドレスは好き勝手に名乗れないはずですから、このように好き勝手に決めてしまって良いはずがありませんが、好き勝手に設定しちゃいます.LAN内に同じMACの機器が在る確率は少ないだろう.
③W5100のSocket0はport番号0x3039=12345であんぐりと口を開けて待ちます.
④UDPモードに設定
⑤socket0をオープン

次に、STM8Sのmain loopで、UDP受信データをポーリングする部分です.今回は割り込みは使いませんでした.UDP受信データを受信バッファから取り出してUARTへ表示しています.
  if(getSn_RX_RSR(s)!=0)  //  w5100 UDP data polling
 {
   // RX buffer addressing
   size = getSn_RX_RSR(s) & bMUSK; //  RX data size
   offset = getSn_RX_RD(s) & bMUSK; //  buffer pointer
   adrs = bRX(s)+offset; 

   // display UDP data
     中略
   for(i=8;i<size;i++)
    {
      adrs = bRXA(s,bRX(s)+offset+i); 
      r = w51RD(adrs); 
      sprintf(UARTstrTX,"%c", r<32?32:r);
      SerialPutString(UARTstrTX);
    }

   adrs = getSn_RX_RD(s)+size; //  update RX buffer pointer
   setSn_RX_RD(s,adrs); 

   setSn_CR(s,Sn_CR_RECV);   //  UDP recieving mode
 }

⑥受信データがあるとこのレジスタが非ゼロになります
⑦受信データサイズを得て、上位桁無視のため2kB(0x7FF)でANDしておきます
⑧受信バッファのポインタです.これも上位桁を無視します
⑨bRX(s)は受信バッファ先頭アドレスなので、ポインタ値を加算してリアルアドレスを得ます
⑩リアルアドレスを++したら、リングバッファを意識したアドレス補正計算をしないと他のSocketバッファにはみ出してしまいます.はみ出し防止計算がbRXA()です
⑪w51RD()はW5100から1バイト読みます
⑫次のpacket受信準備のため、受信バッファポインタを進めます
⑬進めた受信バッファポインタを書き込みます  (ここはなぜか手動なんですね)
⑭次のpacket受信準備のため、RECIEVEコマンドをW5100に与えます

以上、UDPの受信動作を実現するためのW5100の操作でした.TCPだと再送処理とかで2倍ぐらい複雑になりますが、UDPは送りっぱなしなんで意外と簡単です.

【動かし方】
sdcc及びstm8flashがインストールされたLinuxマシンで、
 make
すればコンパイルが完了します.
LinuxマシンのUSBに、ST-LINX/V2を接続し、SWIMでSTM8Sを接続し、
 make flash
すればFLASHを焼けます.
このへんの詳細はこちらです.

STM8SのUARTをwindowsマシンに接続し、teratermでシリアル接続します.

W5100のEthernetに家庭内LANを接続します.

windowsマシンでipsendwinを起動.
ipsendwinを以下のように設定.
ipsendwinの送信ボタンを押す.
←192.168.1.100
←ターゲットポート12345
←お好みでどうぞ
すると、teraterm画面に、W5100が受信したUDPデータがこのように表示されます.
[w5100 recv reg] BYTE=102 0066 offset=1e bufadrs=601e
[UDP recv headder] IP 192.168.1.3 PORT 12345 data size 94 BYTEs
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
next offset 4884
3行目の文字列は、ipsendwinでお好みで設定したPAYLOADデータです.

以上で、UDP packetの送出→W5100で受信→STM8S→teraterm端末 まで通りました.

#192.168.1.100は家庭内LANのルータに明示的に登録しないとダメなのかと思っておりましたが、やってみたところそのような手順は不要でした.いきなり繋いだだけでLANに192.168.1.100が存在しています.

2へ  4へ

かしこ


=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...

2015年12月6日日曜日

【STM8S】 フリーコンパイラsdccをSTライブラリと組み合わせて使う件

前回、STM8SのコンパイラがRaisonanceとCosmicを行ったり来たりするので移植の手間がかったるい、と書きました.

その記事を書いているときに、sdccというgccみたいなコンパイラがあって、それならRaisonanceだのCosmicだのに煩わされることなく使えそうだと知りました.

ST社が提供しているPeripheral Libraryとsdccを組み合わせて割り込みによるLEDチカチカを動かせましたので、以下それをreportします.

【開発環境】
Kona Linux 3.0をinstallした非力なマシン(atom 64bit)で、専らCUIとmakeで開発する.GUIのsdccがあるのかどうかは知らない.windows環境についても関知しない.

STM8SのFlash焼きもLinuxで行う.ST-LINK/V2を使う.写真のプリント基板は自作物なのですが、ST-LINKでSWIMコネクタに接続しているのが要点です.ゆえにSTM8S-discoveryを使っているのと事情は同じです.
【sdccの入手など】
kona-linuxの場合はこれでsdccのインストール完了です.makeとかはせずに済みました.どこかの誰かさんありがとう.
apt-get install sdcc

次に、LinuxからSTM8Sを焼くtoolをインストールします.
apt-get install libusb-1.0     ←usblibを使いますので
apt-get install git       ←gitをインストール
git clone https://github.com/vdudouyt/stm8flash.git    ←gitからコピる
cd stm8flash    ←以下はビルドと/usr/binあたりへのコピー
make
make install

root権限でやってます.
FLASH焼きtoolはstm8flashという名前です.使い方は後で.

追記: 後の方でstm8flashを-p stm8s105 コマンドオプションで使うのですが、DLした状態だと、FLASH=16kBの設定になっているので、わたしは32KBに改変してmakeしました.さもないと巨大なプログラムを焼けなくて難儀します.STM8S-discoveryに載っているSTM8S105C6は32kBです.
stm8.cの入手直後
        .name = "stm8s105",
        .ram_start = 0x0000,
        .ram_size = 2*1024,
        .eeprom_start = 0x4000,
        .eeprom_size = 1024,
        .flash_start = 0x8000,
        .flash_size = 16*1024,
        .flash_block_size = 128,
        REGS_STM8S
同改変後
        .name = "stm8s105",
        .ram_start = 0x0000,
        .ram_size = 2*1024,
        .eeprom_start = 0x4000,
        .eeprom_size = 1024,
        .flash_start = 0x8000,
        .flash_size = 32*1024,
        .flash_block_size = 128,
        REGS_STM8S


【ST提供のPeripheral Libraryの入手】
STのサイトが更新されるとリンク切れになりますけども、現時点でここに置いてあります.
http://www.st-japan.co.jp/web/jp/catalog/tools/FM147/CL1794/SC1807/SS1754/PF258009
解凍すると、ソースファイルが入っていますのでそれを作業フォルダにコピーします.
必要なファイルは以下です.
stsw-stm8069.zip\STM8S_StdPeriph_Lib\Project\STM8S_StdPeriph_Template\stm8s_conf.h
stsw-stm8069.zip\STM8S_StdPeriph_Lib\Libraries\STM8S_StdPeriph_Driver\inc\*.h
stsw-stm8069.zip\STM8S_StdPeriph_Lib\Libraries\STM8S_StdPeriph_Driver\src\*.c
作業フォルダに全部ベタコピーしちゃいました.鬼畜ですが実験用と云う事でお許しを.この時点で、Makefileとmain.c以外はSTライブラリファイルです.
【ST提供のPeripheral Libraryの改変】
ライセンス的にどうなのとお叱りを受けるかもだが、stm8s.hというファイルを一部変更するとsdccを通せるようになる.インラインアセンブラ表記をsdcc用に変える.本日現在の入手物で8箇所あるので全て変更.
入手直後  _asm
書き換え後 __asm__
それともう一つ、stm8s.hを書き換える必要がある.
#define STM8S105         /*!< STM8S Medium density devices */
ファイルの最初方にあるこのコメントアウトを外す.これは他の品種のSTM8を使うならば別の行を活かす必要があるので適宜やってください.こちらは通常の手続きです.

追記1) stm8s.hの中にある、NEARとFARを消す必要あり.
入手直後
 #define PointerAttr NEAR
 #define PointerAttr FAR
書き換え後
 #define PointerAttr

 #define PointerAttr

追記2) stm8s_flash.cの中にある、(NEAR uint8_t*)などの”NEAR”を消す必要あり.たぶん8箇所ある.

追記3) stm8s_itc.cの中にある、_asmを__asm__に変える必要あり.たぶん2箇所ある.

【実験用のmain()】
#include "stm8s.h"

void main(void)
{
        CLK_ClockSwitchConfig ( CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE );
        GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST);
        GPIO_Init(GPIOE, GPIO_PIN_1, GPIO_MODE_OUT_OD_HIZ_FAST);
        TIM1_DeInit();
        TIM1_TimeBaseInit( 1599, TIM1_COUNTERMODE_UP, 5000, 0 );
        TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
        TIM1_Cmd(ENABLE);
     enableInterrupts();
     while(1) {}
}

void TIM1_interrupt(void) __interrupt (11) // interrupt interval 0.5 Sec
{
  TIM1_ClearFlag(TIM1_FLAG_UPDATE);
        GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
        GPIO_WriteReverse(GPIOE, GPIO_PIN_1);
        return;
}

#ifdef USE_FULL_ASSERT
void assert_failed(u8* file, u32 line) { while (1) {} }
#endif

TIM1割り込みでLEDをチカチカさせるだけのプログラムです.
main()でSTライブラリを多くcallしています.
sdccの割り込み表記は、__interrupt (11) のようにします.ここはsdccのポイントですね.

【コンパイル】
sdcc -c -lstm8 -mstm8 -D__CSMC__ main.c
sdcc -c -lstm8 -mstm8 -D__CSMC__ stm8s_gpio.c
sdcc -c -lstm8 -mstm8 -D__CSMC__ stm8s_clk.c
sdcc -c -lstm8 -mstm8 -D__CSMC__ stm8s_tim1.c
sdcc -lstm8 -mstm8 -D__CSMC__ --out-fmt-ihx main.rel stm8s_gpio.rel stm8s_clk.rel stm8s_tim1.rel

これで、出来上がるmain.ihxというファイルが、Intel hex formatになっているみたいです.

ちなみにここでコンパイルオプションで、__CSMC__をdefineしているのがミソでして、これはSTライブラリにCosmicコンパイラであるという嘘を吹き込んでいます.

【FLASH焼き】
stm8flash -c stlinkv2 -p stm8s105 -w main.ihx
Determine FLASH area
Writing Intel hex file 9536 bytes at 0x8000... OK
Bytes written: 9536

一行目のがコマンドラインです.2行目以降はメッセージです.冒頭の写真のように、ST-LINK/V2でSTM8SにSWIM接続しておいてから、コマンドを与えると10秒ぐらいで焼きあがります.

makefileを作っておけばmake一発でbuild~焼きまでできます.

なんだか意外に簡単にSTライブラリを使い、sdccも使え、Flash焼きもできました.
メールで.licファイルをもらったりしなくて済むしライセンス期限やサイズ制限を気にせずに使えるので今後はsdccを使おうと思います.

かしこ


=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...



人気ブログランキングへ