2025年01月02日

超小型PICマイコンPIC10F222/PIC10F322でプログラムの書き初め


1.はじめに
 X(Twitter)のタイムラインでマイコン黎明期の高嶺の花だったスイッチをパチパチしてプログラムを入力するボックス型マイコンの復刻版である Legacy8080 を見かけ、昔自分もパチパチマイコンを作ったことがあったので懐かしく思いました。
 トグルスイッチは高かったので自作のものではアルミ板をコの字型に曲げたパネルに秋葉原で安く売っていたシーソースイッチを付けていました。昔作ったパチパチパネルは今はもう無いので、ワンボード型の DIP SW で入力する小型のものを作ってみようかと思い立ちました。


2.概要設計
 Z80のデータシートで確認すると出力信号のはき出し電流は 250uA 程度のようなので昨今の高輝度LEDならアドレス/データバス表示LEDをCPUに直付けでも行けそうです。
 RAMへの書き込みは以前の通り、DMA で行いますが、今考えてみるとスリーステートバッファ等は全て省略できそうです。クロックは少なくとも高速/低速の2種類供給したいので、74HCU04 での発信回路のコンデンサをスイッチで変更する方法も考えましたが、入力したプログラムのディバッグを考えるとステップ動作ができた方がいいので以前購入したままだった手持ちの PIC10F222 を使うことにしました。なにせ PIC10F222 の SOT-23 パッケージ品は米粒大ですからね^^


3.PICに実装する機能
 PIC10F222 は PROM:512word,RAM:23byte なので価格対性能比で言えば秋月さんで40円で購入できる CH32V003J4M6(PROM:16KB,RAM:2KB)の方が圧倒的に良いのですがリソースが貧弱な方がメモリの少ない黎明期のパソコンのようで面白い面もありそうです。
 開発環境は MPLAB X IDE V5.45 のままで進めます(以前、V6.00に挙げた時の動作に問題があったので古いままにしている)。
 PIC10F222 には
  • Z80へクロック(2MHz)の供給
  • ステップ動作時の WAIT/ 制御
の機能を実装します。  以前はアセンブラやCコンパイラのCC5Xを使っていましたが、今では microchip 社提供のXC8でも特に不便は感じない(しいて言えばFree版ではオプティマイズレベルを高く設定できない)のでXC8で開発します。メモリが足りなくなったらCC5Xに乗り換えるかもしれませんw


4.開発時の注意点
 今回の開発では特に問題は発生しませんでしたが、少し悩んだのが内蔵クロックの校正です。Z80 への供給するクロックは内部オシレータで生成された 8MHz の1/4の 2MHz を供給しているはずが、下記のロジアナ画面に示すように 1.85MHz になってしまっています。

Z80へ供給するクロック(想定より遅い)

 内部発信の校正情報は PROM の最終番地に個別に設定されています。正確に言うと最後のアドレスにWレジスタに校正値を代入するマシン後が格納されていて、リセット後は PROM の最後のアドレスから実行されるので、0000H 番地ではWレジスタに校正値が入った状態になっています。しかしながら、コンパイルされたコードを確認すると何処にも校正値を OSCCAL に設定しているコードがありません。
 仕方がないので main() の先頭にインラインアセンブラで「MOVWF OSCCA」を埋め込みました。その結果、下図のように 2MHz のクロックが出てくるようになりました。

Z80へ供給するクロック(校正後)

 その後、コンパイラ関係の設定を眺めていたらリンカーに校正に関する設定がある(下図の赤丸印)ことに気が付きました。

内部発信校正用コード生成の設定

 上記の校正コード生成のチェック前と後での生成されたコードのキャプチャを張っておきます。

校正用コード生成設定前
校正用コード生成設定後


★追記 2024/03/03 {
WAIT 信号はステップの時と同様に生成(右記)していますがロジアナで確認すると幅が違っています。
WAIT 生成ソース
WAITOUT = 1;
__asm( "NOP" );
WAITOUT = 0;

STEP時のWAIT/信号
AUTO時のWAIT/信号

 コンパイルされたコードを見てみると「NOP」の後の「WAITOUT = 0」の直前でwhile句の直前の「WAITOUT = 0」の部分に飛んでいます。制御シーケンス的には同じですがタイミングが違ってきます。

コンパイルされたコード(最適化レベル:0)

 この時最適化レベルは0だったのでレベルを3に設定した後の生成コードも確認してみました。結果は下図のように同様のコードになっていました。

コンパイルされたコード(最適化レベル:3?)

 ところで前述したように XC8 コンパイラの free 版は最適化レベルの設定に上限がある(確かレベル2まで)だったのでレベル3を設定できたことが不思議でしたが、コンパイラの出力メッセージを確認すると下図のようにレベル2で最適化していました。

最適化レベル3設定時のコンパイラのメッセージ

 参考にそれぞれの最適化レベルでのメモリマップは下図のとおりです。最適化によって Program space は結構小さくなっていますね。

最適化レベル0設定時
最適化レベル3設定時(実際はレベル2)
}

5.PIC10F322 へのコンバージョン ※追記 2025/01/05
 最近、老眼が更に進んだ気がしていましたが手持ちの米粒大のPICをよ~く見てみたらなんと PIC10F322 でしたw
 MPLAB IDE のプロジェクトのプロパティでCPUタイプを変更してみましたがエラーが続出し、一発で変更はできなそうです。PIC10F322 のデータシートを見ながらソースを変更し、PIC10F222 の時と同様な動作をするようになりました。因みに PIC10F222 の米粒大のチップは秋月さんでは販売されていないようです。
 PIC10F322 の内部オシレータは 16MHz(この場合、Z80へは 4MHz の供給が可能)まで対応していますが以前と同様に 8MHz のままにしています。
 今回の簡単なコンバージョンで少し悩んだのが config の LVP ビットの設定です。下図はデータシートからの抜粋ですが LVP ビットが1の場合は MCLRE の設定が無視される(MCLRとして機能しデジタル入力にはできない)と書いてあります。

CONFIG の MCLRE ビット ※PIC10F322 データシートからの抜粋

 MPLAB IDE の config 設定ソース生成ツールで下図のような意味の説明文を読むと LVP が OFF の時は高電圧をかけるのに MCLR の使用が必須とあるのでデジタル入力に使用できないように勘違いしそうですがこの文言は上記のデータシートと同じですね^^;
※2025/01/05文言を一部変更

MPLAB IDEのconfig 設定ソース生成ツール

 実物で試してみた結果、LVP=OFF の場合だけ MCLRE=OFF で MCLR をデジタル入力にできました


6.今回作成したプログラム
 今回書き初めした PIC10F222 のプログラムを参考に貼っておきます。

PIC10F222 : generate Z80 clock & wait/
/* * PIC10F222 : generate Z80 clock & wait/ * Author: skyriver * Date : 2025/01/02 Ver 0.02 */ // PIC10F222 Configuration Bit Settings // CONFIG #pragma config IOSCFS = 8MHZ // Internal Oscillator Frequency Select bit (4 MHz) #pragma config MCPU = ON // Master Clear Pull-up Enable bit (Pull-up enabled) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config CP = OFF // Code protection bit (Code protection off) #pragma config MCLRE = OFF // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD) // #pragma config statements should precede project file includes. #include <xc.h> #include <stdint.h> #define _XTAL_FREQ 8000000 // 76543210 #define OPTSLOW 0b10000111 // enable fosc4 output, prescaler 1/256 //#define TIMSLOW 244 // //#define TIMCNT 8 // 0.499s = (1/2000000)*256*244*8*2 // GPIO definition #define AUTOSTP GP0 // auto step #define WAITOUT GP1 // WAIT #define FOSCOUT GP2 // cann't change #define STEPSW GP3 // step switch input void init( void ) { // OPTION reg // bit 7 :GPIO change interrupt 0:enable, 1:disable // bit 6 :GPIO weak pull up 0:enable, 1:disable // bit 5 :timer0 clock source 0:internal, 1:T0CK1 // bit 4 :timer source edge 0:positive edge 1:negative edge // bit 3 :prescaler 0:timer0, 1:WDT // bit 2-0:prescaler rate timer:2..256, WDT:1..128 OPTION = OPTSLOW; ANS1=0; //digital port set ANS0=0; //digital port set // 76543210 TRISGPIO = 0b11111001; FOSC4 = 1; // out FOSC4 to GP2 } void main(void) { // __asm( "MOVWF OSCCAL" ); // set calbration parameter at last memory to OSCCAL init(); WAITOUT = 0; for( ; ; ) { while( AUTOSTP == 0 ) { __delay_ms( 1000 ); WAITOUT = 1; __asm( "NOP" ); WAITOUT = 0; } while( STEPSW != 0 ) {} __delay_ms( 10 ); WAITOUT = 1; __asm( "NOP" ); WAITOUT = 0; while( STEPSW == 0 ) {} __delay_ms( 10 ); } }
※2024/01/03 update

 PIC10F322 へコンバージョン後のソースが下記になります。

コンバージョンした PIC10F322 のソース
/* * PIC10F322 : generate Z80 clock & wait/ * Author: skyriver * Date : 2025/01/05 Ver 0.01 */ // PIC10F322 Configuration Bit Settings // CONFIG #pragma config FOSC = INTOSC // Oscillator Selection bits (INTOSC oscillator: CLKIN function disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD) #pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) #pragma config LPBOR = ON // Brown-out Reset Selection bits (BOR enabled) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include <xc.h> #include <stdint.h> #define _XTAL_FREQ 8000000 // 76543210 #define OPTSLOW 0b01100000 // INTOSC:8MHz //#define TIMSLOW 244 // //#define TIMCNT 8 // 0.499s = (1/2000000)*256*244*8*2 // GPIO definition #define AUTOSTP RA0 // auto step #define WAITOUT RA1 // WAIT #define FOSCOUT RA2 // cann't change #define STEPSW RA3 // step switch input void init( void ) { // OSCCON reg // bit 7 :not use // bit 6-4 : select INTOSC 110:8MHz(default) // bit 3 :HFIOFR(ready bit) // bit 2 :not use // bit 1 :LFIOFR(ready bit) // bit 0 :HFIOFS(stable bit) OSCCON = OPTSLOW; ANSELA = 0; // all RAx is digital // 76543210 TRISA = 0b11111001; CLKROE = 1; // out FOSC4 to RA2 } void main(void) { // __asm( "MOVWF OSCCAL" ); // set calbration parameter at last memory to OSCCAL init(); WAITOUT = 0; for( ; ; ) { while( AUTOSTP == 0 ) { __delay_ms( 500 ); WAITOUT = 1; __asm( "NOP" ); WAITOUT = 0; __asm( "NOP" ); // for compiler's optimize problem } while( STEPSW != 0 ) {} __delay_ms( 10 ); WAITOUT = 1; __asm( "NOP" ); WAITOUT = 0; while( STEPSW == 0 ) {} __delay_ms( 10 ); } }


★追記 2025/01/07
 X(Twitter)に投稿したメッセージに添付した動画を張っておきます。PIC から供給するクロックで Z80 は 2MHz で動作していて、PIC はステップスイッチからの信号を受けて Z80 を制御しています。LED は Z80 のアドレス信号で点灯しています。

posted by skyriver at 17:52| Comment(0) | PIC | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください