SBC6809ルーズキットの製作(リセット不良対策編)

2018/05/08

 SBC6809を楽しんでいると、電源オンしてもROMから立ち上がらない場合が時々あることがありました。気になったので調べてみると、電源スイッチを切っても電源電圧が下がり切っていないことがわかりました。さらに調べると、USB-Serial BridgeのTXDから電源にHD63B50Pの入力寄生ダイオード経由で18mA程度の電流が回り込みしていました。


今回、全面的にCMOS化したため、わずかな消費電流がゆえに電源電圧が下がり切っていないようです。このため、CPUのリセット回路コンデンサーの電圧が下がりきれず、本来の電源が投入されてもCPUリセットがかからない状態が発生したと考えられます。


CMOSならではの問題です。NMOSのMC6850Pを使用した場合は、入力寄生ダイオードがないため、発生しないはずです。また、CMOSのHD6350Pを使用した場合でも、CPUがMC6809やTTLロジックを使っていれば、消費電流が大きいため、問題が起きにくいと考えられますが、電流が増えるため、USB-Serial Bridge側へのストレスが若干心配になります。銅箔カットが必要ですが、HD6350PのRXD入力に1K程度の直列抵抗を入れる対策も一案です。


SBC6809の電源を入れたのち、USB-Serial Bridgeに接続すれば良いのですが、ROM立ち上げメッセージを表示したい場合は、問題です。このため、対策としてダイオードD1を電圧検出器に交換することで、解決できました。良くある問題ですが、またレトロっぽくなくなりました。電圧検出器は、たまたま手持ちのSEIKO製S80830CNY(3.0V検出)を使用しました。


下記のような回路に改造することになりますが、またピンバイスとドリル刃の登場です。D1の電源側の隣に穴をあけ、S80830CNYのVSSをこの穴を通して、GNDランドにはんだ付けすれば、D1の置き換えができます。調子が良くなりました。


(再クロール更新:2022/12/22)

SBC6809ルーズキットの製作(シリアル通信速度自動設定編)

2018/05/07

 SBC6809ルーズキットには、データパックが提供されています。この中に通信クロック用のosc1536.hexが添付され、9600bps用のクロックを生成することができます。9600bpsは、レトロな雰囲気を出すぴったりの速度ですが、今後プログラムのダウンロードを考えた場合、心配が残ります。そこで、通信クロック周波数の自動検出ができるPIC12F1822プログラムを作成してみました。


下記のプログラムは、PIC12F1822の3ピンにRXDを接続し、2秒間以上の無通信区間後に、CRコードを打つと、そのマークビットの時間計測を行い、9600、19200、38400、57600、115200に対応したクロックを生成できるようにしてあります。電源投入後、30秒以内に行うことを想定しています。


ここで、115200の場合、クロック周波数偏差の問題が残ります。これは、OSCTUNEによる内部クロックの調整範囲には限界があるようで、実測では、115200の場合1940kHzで、5%強の偏差がありますが、今のところ、特に問題なく使用できています。


シリアルクロックの許容偏差は、通常の使用ではスタートビットの中央から8ビットデータの後、1ストップビットが正しく認識できるためには、9ビット分の時間経過後、0.5ビット以内の誤差が必要要件となりますので、最大許容偏差は±5.6%になります。

PIC12F1822のクロック偏差は、実測で+5.25%でしたので、あまりマージンがありません。USB-Serial Bridgeのクロック精度によっては、フレーミングエラーが発生する可能性があります。そのコントローラがPL-2303HXDの場合、クロックの最大偏差は、±2%ですので、クロック周波数がプラス動向に触れている場合は、良い方向になりますが、逆に振れるとエラーになる可能性が高くなります。したがって、五分五分ですので、実使用で安定性があるか確認し、使用できれば使用することができるレベルです。


/**************************************************************************/
/* Files to Include */
/**************************************************************************/

#if defined(__XC)
#include /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include /* HiTech General Include File */
#endif

#include /* For uint8_t definition */

/**************************************************************************/
/* Configuration bits */
/**************************************************************************/

/* CONFIGURATION WORD 1 setting value */
#pragma config FOSC = INTOSC // INTOSC oscillator
#pragma config WDTE = OFF // WDT disabled
#pragma config MCLRE = ON // MCLR/VPP pin function is MCLR

/* CONFIGURATION WORD 2 setting value */
#pragma config PLLEN = ON // PLL Enable->4x PLL enabled

/**************************************************************************/
/* User Global Variable Declaration */
/**************************************************************************/

volatile uint16_t timer_tick;
uint16_t min_count;
uint16_t max_count;

/**************************************************************************/
/* Main Program (Auto Baud Rate Detection Program for MC6850) */
/**************************************************************************/

/* TIMER0 interrupt tick value */
#define IDLE_TIME 244 // idle tick of time -> 2s
#define TIMEOUT 3662 // timeout of auto detection -> 30s

/* I/O port bit assign */
#define RXD RA4 // PIN3 RA4 RXD sensing Digital Input

/* set PWM parameters */
void set_PWM(uint8_t period, uint8_t tune) {
OSCTUNE = tune; // Internal Oscillator Frequency Adjustment
PR2 = period; // set PWM Period
CCPR1L = (uint8_t)(period >> 1); // set PWM width
TMR2ON = 1; // Timer2 is on
}

/* get TIMER1 count of mark bit */
void get_mark_bit(uint8_t bits) {
uint16_t count;
while(!RXD); // wait space bit
TMR1ON = 0; // stop TIMER1 count
TMR1H = 0; TMR1L = 0; // clear TMR1H, TMR1L count
while(RXD); // wait mark bit
TMR1ON = 1; // start TIMER1 count
while(!RXD); // wait space bit
TMR1ON = 0; // stop TIMER1 count
count = ((uint16_t)TMR1H << 8) | TMR1L;
count >>= bits; // normalize count
if(count < min_count) min_count = count;
if(count > max_count) max_count = count;
}

void interrupt timer_isr(void) {
if(TMR0IF) { // timer0 interrupt
timer_tick++; // increment time tick in each 1/122 senconds
TMR0IF = 0; // clear timer0 interrupt
}
}

/* PIC12F1822 SBC6809 Baud Rate Generator with Auto Speed Detection */

#define PR2_9600 51 // PR2 value for 9600bps
#define PR2_19200 25 // PR2 value for 19200bps
#define PR2_38400 12 // PR2 value for 38400bps
#define PR2_57600 8 // PR2 value for 57600bps
#define PR2_115200 3 // PR2 value for 115200bps

#define MIN_9600 792 // min. TIMER1 count for -5% of 9600bps
#define MIN_19200 396 // min. TIMER1 count for -5% of 19200bps
#define MIN_38400 198 // min. TIMER1 count for -5% of 9600bps
#define MIN_57600 132 // min. TIMER1 count for -5% of 19200bps
#define MIN_115200 66 // min. TIMER1 count for -5% of 9600bps
#define MAX_9600 875 // max. TIMER1 count for +5% of 9600bps
#define MAX_19200 438 // max. TIMER1 count for +5% of 19200bps
#define MAX_38400 219 // max. TIMER1 count for +5% of 38400bps
#define MAX_57600 148 // max. TIMER1 count for +5% of 57600bps
#define MAX_115200 73 // max. TIMER1 count for +5% of 115200bps

/* Auto Speed Detection Sequence
* no mark bit in 2s and CR in 30s after powerup
*/

void main(void)
{
/* initialize oscillator */
OSCCON = 0b11110000; // PLL Is enabled by CONFIGURATION WORD 1
// 8 MHz or 32 MHz HF
// Clock determined by CONFIGURATION WORD 1

/* initialize PIN assignment */
CCP1SEL = 0; // PIN5 CCP1 on RA2
ANSELA = 0; // Digital I/O. Pin
TRISA = 0b11111011; // PIN3 RA4 Digital Input
// PIN5 CCP1 on RA2 Digital Output
WPUA4 = 1; // PIN3 RA4 Pull-up enabled

/* initialize timer */
OPTION_REG = 0b00000111; // Weak pull-ups are enabled
// Interrupt on falling edge of RB0/INT pin
// Internal instruction cycle clock (FOSC/4)
// Increment on low-to-high transition on T0CKI pin
// Prescaler is assigned to the Timer0 module
// Prescaler Rate Select bits (1 : 256)
T1GCONbits.TMR1GE = 0; // always counting
T1CON = 0b00000000; // Timer1 clock source is FOSC/4
// 1:1 Prescale value
// LP OSC disabled
// clock synchronization disabled
// Stops Timer1
TMR1H = 0; TMR1L = 0; // clear TMR1H, TMR1L count
timer_tick = 0; // reset timer

/* initialize interrupt setting */
PIR1 = 0; // clear peripheral interrupt flag
PIR2 = 0; // clear peripheral interrupt flag
INTCON = 0b11100000; // global interrupt enabled
// periferal interrupt enabled
// timer0 interrupt enabled
// external interrupt disabled
// interrupt-on-change disabled
// clear timer1 interrupt flag
// clear external interrupt flag
// clear interrupt-on-change flag

/* initialize PWM */
T2CONbits.T2CKPS = 0b00; // Prescaler is 1
CCP1CON = 0b00001100; // Single output; P1A modulated
// the two LSbs of the PWM duty cycle = 00
// PWM mode: P1A active-high
set_PWM(PR2_9600, 0); // start baud rate generator for 9600bps

/* sensing loop */
while(1) {
timer_tick = 0;
while(timer_tick < IDLE_TIME) { // wait for no mark bit in 10s
if(!RXD) { // if mark bit then reset timer
timer_tick = 0; // reset timer
}
}
timer_tick = 0; // reset timer
min_count = 32767; // reset min_count
max_count = 0; // reset max_count
/* CR($0D) 1-0-1011-0000-1
* 1 1 4bit */
get_mark_bit(0); // 1 mark bit
get_mark_bit(0); // 1 mark bit
get_mark_bit(2); // 4 mark bit
if(min_count > MIN_9600 && max_count < MAX_9600) {
set_PWM(PR2_9600, 0); break;
} else if(min_count > MIN_19200 && max_count < MAX_19200) {
set_PWM(PR2_19200, 0); break;
} else if(min_count > MIN_38400 && max_count < MAX_38400) {
set_PWM(PR2_38400, 0); break;
} else if(min_count > MIN_57600 && max_count < MAX_57600) {
set_PWM(PR2_57600, 31); break;
} else if(min_count > MIN_115200 && max_count < MAX_115200) {
set_PWM(PR2_115200, 32); break;
}
if(timer_tick > TIMEOUT) { // check timeout
break;
}
}
while(1); // done.
}

(再クロール更新:2022/12/22)