目次

周波数カウンタ

 デジタルICカウンタの74HC4040は、70MHzまで対応できます。
 マイコンの内蔵カウンタと合わせて周波数カウンタを作成しました。

 74HC4040を外部カウンタで利用し、8ビットの値を保持させます。
 8ビットがオーバーフローしたとき、マイコンの内蔵カウンタを
 インクリメントすると、マイコンの動作周波数は2MHz程度でよく
 なります。

 ブロック図で機能を切り分けると、以下。




 全体で32ビットのカウンタを構成。74HC4040を外部カウンタで
 利用し、内部に24ビットのカウンタを用意。
 74HC4040の8ビットのオーバーフローで、16ビットカウンタを
 インクリメント。さらに16ビットがオーバーフローしたならば
 8ビットのカウンタをインクリメントします。

 周波数は、1秒間の信号の変化回数と解釈します。

 1秒間のマイコン内蔵24ビットカウンタの値と外部の74HC4040の
 8ビット値を合体し、32ビットにします。

 ピン数が少ないマイコンでも対応できるように、74HC4040の8ビット
 を、パラレルシリアル変換ICの74HC164で転送。

 マイコンを含めた回路図は、以下。




 マイコン内蔵のタイマーで1秒間を生成し、その間に74HC4040が
 周波数をカウントします。1秒経過したならば、計算して結果を
 7セグメントLEDに転送します。

 マイコンのファームウエアは、以下。

/* redefine data type */
typedef unsigned char  UBYTE ;
typedef unsigned int   UWORD ;
typedef unsigned long  ULONG ;

#define OFF 0
#define ON  OFF+1

/* global variables */
volatile UBYTE flag ;
volatile UBYTE state ;
volatile UBYTE lcnt ;
volatile UBYTE timv ;
volatile UBYTE timx ;
volatile UBYTE msg[10] ;
volatile UBYTE lsx[8] ;
volatile ULONG fcnt ;
volatile ULONG fcntx ;
volatile UBYTE te ;
volatile UBYTE th ;
volatile UBYTE tm ;
volatile UBYTE tl ;
volatile UBYTE xtmp ;
volatile UBYTE xcnt[5] ;

/* function prototype */
void init_usr(void);
void loadecounter(void);
void separatedigit(void);
void generatedigit(void);
void perform_led_control(void);

/* interrupt handler */
void  interrupt(void)
{
  /* timer1 overflow interrupt */
  if ( PIR1.TMR1IF ) {
    /* clear flag */
    PIR1.TMR1IF = OFF ;
    /* increment */
    te = te + 1 ;
  }
  /* timer0 overflow interrupt */
  if ( INTCON.T0IF ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = 6 ;
    /* set flag (1250Hz/4 = 312.5Hz) */
    if ( (timx & 3) == 0 ) { flag.F0 = 1 ; }
    /* increment */
    timx = timx + 1 ;
    timv = timv - 1 ;
    /* judge 10Hz (100ms) F counter */
    if ( timv == 0 ) {
      /* clear */
      timv = 124 ;
      /* set flag */
      flag.F1 = 1 ;
    }
  }
}

void  main(void)
{
  /* user initialize */
  init_usr();
  /* endless loop */
  while ( ON ) {
    /* frequency counter handling */
    if ( flag.F1 == ON ) {
      /* clear flag */
      flag.F1 = OFF ;
      /* start count */
      if ( state == 0 ) {
        /* clear counter */
        te = 0 ;
        TMR1H = 0 ;
        TMR1L = 0 ;
        /* enable timer1 */
        T1CON.TMR1ON = ON ;
        /* enable peripheral interrupt */
        INTCON.PEIE = ON ;
        /* enable 74HC4040 */
        PORTB.F0 = ON ;
        /* check */
        flag.F2 = OFF ;
        if ( PORTA.F0 == OFF ) { flag.F2 = ON ; }
      }
      /* stop count */
      if ( state == 10 ) {
        /* disable timer1 */
        T1CON.TMR1ON = OFF ;
        /* disable peripheral interrupt */
        INTCON.PEIE = OFF ;
        /* disable 74HC4040 */
        PORTB.F0 = OFF ;
      }
      /* load counter */
      if ( state == 11 ) {
        th = TMR1H ;
        tm = TMR1L ;
        loadecounter();
      }
      /* get counter */
      if ( state == 12 ) {
        /* top byte */
        fcnt = te ; fcnt <<= 8 ;
        /* next byte */
        fcnt |= th ; fcnt <<= 8 ;
        /* semi final byte */
        fcnt |= tm ; fcnt <<= 8 ;
        /* final byte */
        fcnt |= tl ;
        /* adjust */
        fcnt <<= 1 ;
        /* copy */
        fcntx = fcnt ;
      }
      /* ? -455kHz */
      if ( state == 13 ) {
        /* check */
        if ( flag.F2 == ON ) {
          if ( fcntx >= 455000 ) { 
            fcntx = fcnt - 455000 ; 
          }
        }
        /* separate digits */
        separatedigit();
      }
      /* generate digits */
      if ( state == 14 ) { generatedigit(); }
      /* zero surpress */
      if ( state == 15 ) {
        if ( *(msg+0) == 0 ) {
          *(msg+0) = 15 ;
          if ( *(msg+1) == 0 ) {
            *(msg+1) = 15 ;
            if ( *(msg+2) == 0 ) {
              *(msg+2) = 15 ;
              if ( *(msg+3) == 0 ) { 
                *(msg+3) = 15 ; 
                if ( *(msg+4) == 0 ) {
                  *(msg+4) = 15 ;
                  if ( *(msg+5) == 0 ) { *(msg+5) = 15 ; }
                }
              }
            }
          }
        }
        /* add Decimal Point */
        msg[3].F7 = 1 ;
        msg[6].F7 = 1 ;
      }
      /* transfer */
      if ( state == 16 ) {
        *(lsx+0) = *(msg+2) ; *(lsx+1) = *(msg+3) ;
        *(lsx+2) = *(msg+4) ; *(lsx+3) = *(msg+5) ;
        *(lsx+4) = *(msg+6) ; *(lsx+5) = *(msg+7) ;
        *(lsx+6) = *(msg+8) ; *(lsx+7) = *(msg+9) ;
      }
      /* increment */
      state = state + 1 ;
      /* update */
      if ( state == 17 ) { state = 0 ; }
    }
    /* LED handling */
    if ( flag.F0 == ON ) {
      /* clear flag */
      flag.F0 = OFF ;
      /* add digit location */
      xtmp = swap(lcnt) ;
      /* get digit from Array */
      xtmp |= lsx[lcnt] ;
      /* disable 74HC138 */
      PORTA.F2 = ON ;
      /* show */
      perform_led_control();
      /* enable 74HC138 */
      PORTA.F2 = OFF ;
      /* increment */
      lcnt = lcnt + 1 ;
      /* update */
      lcnt &= 7 ;
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* disable compare module */
  CMCON = 0x07 ;
  /* I/O state */
  PORTA = 0x04 ;
  PORTB = 0x00 ;
  /* I/O directions */
  TRISA = 0x03 ; /* RA0,RA1 inputs , others outputs */
  TRISB = 0x50 ; /* RB6,RB4 inputs , others outputs */
  /* initialize Timer 0 */
  {
    /*
       20MHz/4 = 5MHz -> 5MHz/16 = 312.5kHz prescaler = 1:16
    */
    OPTION_REG = 0x03 ;
    /* 256 - 250 = 6 , 312.5kHz/250 = 1250Hz */
    TMR0 = 6 ;
    /* enable timer 0 overflow interrupt */
    INTCON.T0IE = ON ;
  }
  /* initialize Timer 1 */
  {
    /* select external clock input , synchronous and stop */
    T1CON = 0x02 ;
    /* enable timer 1 overflow interrupt */
    PIE1.TMR1IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flags */
  flag = OFF ;
  /* initialize variables */
  state = 0 ;
  lcnt = 0 ;
  timx = 0 ;
  timv = 124 ;
}

void loadecounter(void)
{
  UBYTE i ;
  /* Latch external counter 74HC4040 -> 74HC165 */
  PORTB.F7 = ON ; i = 0 ; PORTB.F7 = OFF ;
  /* clear */
  tl = 0 ;
  /* loop */
  for ( i = 0 ; i < 8 ; i = i + 1 ) {
    /* shift */
    tl <<= 1 ;
    /* get LSB */
    if ( PORTB.F4 == ON ) { tl = tl + 1 ; }
    /* CLOCK : H */
    PORTB.F5 = ON ;
    /* CLOCK : L */
    PORTB.F5 = OFF ;
  }
  /* clear external counter */
  PORTB.F1 = ON ; i = 0 ; PORTB.F1 = OFF ;
}

void separatedigit(void)
{
  UBYTE i;
  for ( i = 0 ; i < 5 ; i = i + 1 ) {
    *(xcnt+4-i) = fcntx % 100 ;
    if ( i < 4 ) { fcntx /= 100 ; }
  }
}

void generatedigit(void)
{
  UBYTE i;
  UBYTE j;
  UBYTE k;
  UBYTE l;
  for ( i = 0 ; i < 5 ; i = i + 1 ) {
    j = i + i ;
    k = *(xcnt+i) ;
    l = k % 10 ;
    k = k / 10 ;
    *(msg+j)   = k  ;
    *(msg+j+1) = l ;
  }
}

void perform_led_control(void)
{
  UBYTE i ;
  UBYTE tmp ;
  /* copy */
  tmp = xtmp ;
  /* send */
  for ( i = 0 ; i < 8 ; i = i + 1 ) {
    /* impress */
    PORTB.F2 = tmp.F7 ;
    /* 74HC164_CLK : H */
    PORTB.F3 = ON ;
    /* shift */
    tmp <<= 1 ;
    /* 74HC164_CLK : L */
    PORTB.F3 = OFF ;
  }
}

 455kHzの減を入れられるようにしました。

 アマチュア無線のトランシーバーで中間周波数に変換したい
 場合の操作が単純になるようにと入れてます。

 周波数カウント、シーケンサを利用。
 シーケンス動作は、以下。

 上のシーケンサは、10Hz=100msごとに発生する
 タイマー割込みをトリガーにしました。

 周波数の表示は、約2msの周期で発生するタイマー割込みを
 利用して、1桁ごとに数字を送り出します。

 マイコン基板は、1枚にまとめてます。



 7セグメントLEDは、別基板に。



 GCBASICでも動かせるかを、次のコードでテスト。

#chip 16F628A,20

#config MCLRE = ON

#define CNT_ENA PORTB.0
#define CNT_CLR PORTB.1
#define SDIN    PORTB.2
#define SCLK    PORTB.3

#define SDOUT   PORTB.4
#define SSCK    PORTB.5
#define SFTLOAD PORTB.7

#define YMON    PORTA.4
#define XMON    PORTA.3
#define XENA    PORTA.2
#define XMODE   PORTA.0

Dim state As Byte
Dim lcnt As Byte
Dim te As Byte
Dim th As Byte
Dim tm As Byte
Dim tl As Byte
Dim timv As Byte
Dim timu As Byte
Dim tflag As Byte
Dim lflag As Byte
Dim msg(10) As Byte
Dim lsx(8) As Byte
Dim i As Byte
Dim j As Byte
Dim k As Byte
Dim l As Byte
Dim xtmp As Byte
Dim fcntx As Long
Dim fcnt  As Long
Dim xcnt(5) As Byte

' timer2 compare match
On Interrupt Timer2Match Call Xtim
' timer1 overflow
On Interrupt Timer1Overflow Call Ytim
' timer0 overflow
On Interrupt Timer0Overflow Call Ztim

' initialize
InitUsr

' endless loop
Main:
  ' 7 segment LED handling (about 1ms)
  If lflag.0 = 1 Then
    ' clear flag
    lflag.0 = 0
    ' set digit location
    xtmp = swap4(lcnt)
    ' get digit from Array
    xtmp = xtmp + lsx(lcnt)
    ' LED handling
    PLC
    ' increment
    lcnt = lcnt + 1
    ' update (0-7)
    If lcnt.3 = 1 Then
      lcnt = 0
    End If
  End If
  ' frequency counter handling (1000ms) and convert
  If tflag.0 = 1 Then
    ' clear flag
    tflag.0 = 0
    ' start count (0ms)
    If state = 0 Then
      Btim
    End If
    ' stop count (1000ms)
    If state = 1 Then
      Ftim
    End If
    ' calculate (2000ms)
    If state = 2 Then
      ExecuteCalc
    End If
    ' increment
    state = state + 1
    ' update
    If state = 3 Then
      state = 0
    End If
  End If
GOTO Main

Sub InitUsr
  ' I/O directions
  TRISA = 0x03
  ' RA0,RA1 inputs , others outputs
  TRISB = 0x50
  ' start Timer0
  TMR0 = 6
  OPTION_REG = 0x03
  ' select external clock input and asynchronous
  T1CON = 0x06
  ' start Timer2
  PR2 = 249
  T2CON = 0x56
  ' clear flags
  tflag = 0
  lflag = 0
  ' initialize variables
  state = 0
  lcnt  = 0
  timv  = 0
  timu  = 0
  For i = 0 TO 9
    msg(i) = 0
  Next
End Sub

' generate 1Hz
Sub Xtim
  ' monitor
  XMON = timv.0
  ' increment
  timv = timv + 1
  ' judge 1Hz (1000ms) F counter
  If timv = 125 Then
    ' clear
    timv = 0
    ' set flag
    tflag.0 = 1
  End If
End Sub

' overflow counter
Sub Ytim
  ' increment
  te = te + 1
End Sub

' generate 1250Hz
Sub Ztim
  ' initialize
  TMR0 = 6
  ' increment
  timu = timu + 1
  ' set flag
  lflag.0 = 1
  ' monitor
  YMON = timu.0
End Sub

Sub Btim
  ' clear overflow counter
  te = 0
  ' enable timer1
  T1CON.TMR1ON = 1
  ' enable 74HC4040
  CNT_ENA = 1
End Sub

Sub Ftim
  ' disable timer1
  T1CON.TMR1ON = 0
  ' disable 74HC4040
  CNT_ENA = 0
End Sub

Sub LoadECounter
  ' Latch external counter 74HC4040 -> 74HC165
  SFTLOAD = 1
  SFTLOAD = 0
  ' clear
  tl = 0
  ' loop
  For i = 0 To 7
    ' shift
    tl = tl + tl
    ' get LSB
    If SDOUT = 1 Then
      tl.0 = 1
    End If
    ' CLOCK : H
    SSCK = 1
    ' CLOCK : L
    SSCK = 0
  Next
  ' clear external counter
  CNT_CLR = 1
  CNT_CLR = 0
End Sub

Sub LoadTimer1
  ' get timer1 counter
  th = TMR1H
  tm = TMR1L
  ' clear timer1
  TMR1H = 0
  TMR1L = 0
End Sub

Sub GetCounter
  ' upper 16bits
  fcnt = te
  fcnt = FnLSL(fcnt,8)
  fcnt = fcnt or th
  fcnt = FnLSL(fcnt,8)
  ' lower 16bits
  fcnt = fcnt or tm
  fcnt = FnLSL(fcnt,8)
  fcnt = fcnt or tl
  ' adjust
  fcnt = FnLSL(fcnt,1)
  ' copy
  fcntx = fcnt
End Sub

Sub AjustFreq
  ' adjust
  If XMODE = 0 Then
    If fcntx >= 455000 Then
      fcnt = fcnt - 455000
    End If
  End If
End Sub

Sub SeparateDigit
  ' make digit
  For i = 4 To 0
    xcnt(i) = fcnt % 100
    fcnt = fcnt / 100
  Next
End Sub

Sub GenerateDigit
  For i = 0 To 4
    l = xcnt(i)
    j = 2 * i
    k = j + 1
    msg(j) = l / 10
    msg(k) = l % 10
  Next
End Sub

Sub ZeroSurpress
  '
  If msg(0) = 0 Then
    msg(0) = 15
    If msg(1) = 0 Then
      msg(1) = 15
      If msg(2) = 0 Then
        msg(2) = 15
        If msg(3) = 0 Then
          msg(3) = 15
          If msg(4) = 0 Then
            msg(4) = 15
            If msg(5) = 0 Then
              msg(5) = 0
            End if
          End if
        End If
      End If
    End If
  End If
  ' add Decimal Point
  msg(3).7 = 1
  msg(6).7 = 1
  ' copy
  For i = 0 To 7
    j = i + 2
    lsx(i) = msg(j)
  Next
End Sub

Sub ExecuteCalc
  ' get external counter
  LoadECounter
  ' get timer 1
  LoadTimer1
  ' get 2 counter
  GetCounter
  ' ajust
  AjustFreq
  '
  SeparateDigit
  ' convert
  GenerateDigit
  ' zero surpress
  ZeroSurpress
End Sub

' LED handling
Sub PLC
  ' disable 74HC138
  XENA = 1
  ' send
  For j = 0 To 7
    ' impress
    SDIN = xtmp.7
    ' 74HC164_CLK : H
    SCLK = 1
    ' shift
    xtmp = xtmp + xtmp
    ' 74HC164_CLK : L
    SCLK = 0
  Next
  ' enable 74HC138
  XENA = 0
End Sub

 MikroCではプログラム容量の46%を使ったのに
 GCBASICでは33%程度でした。

 アセンブリ言語のコードで比較すると、MikroCでは
 R0からR15の16バイトの16レジスタを使い、スタック
 を消費しない仕様でした。このレジスタへの入出力
 で、余計なコードが含まれてしまうので、コード量
 が増えているようです。

 CとBASICのコンパイラともに、次の記述をした方が
 アセンブリ言語レベルで、レジスタ間転送をしない
 で短いコードになっています。

  tl++ -> tl = tl + 1

 PICではWレジスタと、レジスタファイル間でのデータ転送
 命令をもっていると加減算と論理演算では、Wレジスタと
 レジスタファイルを独立して扱えます。

 Cで「tl++」とすると、Wレジスタ経由で扱おうとするのに
 対して「tl = tl + 1」では、レジスタファイルだけで事が
 おさまるように解釈されていました。

 BASICでは、「tl = tl + 1」という記述しか許されてない
 ので、レジスタファイルだけでの処理になるようなコード
 を生成できています。

 7セグメントLEDの基板と本体は、20ピンコネクタで接続。
 信号とピン番号の組み合わせは、以下。

Vcc  1  2 GND
     3  4 
nY7  5  6 nY6
nY5  7  8 nY4
nY3  9 10 nY2
nY1 11 12 nY0
Dp  13 14 g
 f  15 16 e
 d  17 18 c
 b  19 20 a

 写真では、右上がピン2と対応します。




目次

inserted by FC2 system