ATtiny10 用プログラム (9)

ATtiny10 の内蔵 8 MHz オシレータの周波数 (周期) を「自力」で測定するプログラム「osc_tn10.c」のソース・リストを下に示します。
AVR Studio 4.19 で新規 AVR GCC プロジェクトを作成し、プロジェクト・フォルダに以下のプログラムをコピー・アンド・ペーストし、拡張子が「.c」のプログラムをプロジェクトに追加します。
「osc_tn10.c」

/*************************************************/
/* osc_tn10.c : ATtiny10 internal RC oscillator  */
/*              period measurement               */
/*              by stepping OSCCAL value         */
/*                                               */
/* 2012/01/03 : Created by pcm1723               */
/*************************************************/
  
#include <avr/io.h>
#include <avr/interrupt.h>

#include "pcnt_tn10.h"
#include "sft_uart.h"

#define outchar(x)  attiny10_soft_uart_send_byte(x)

//
// OSCCAL scan start and stop value
//
#define OSCCAL_L 0x20
#define OSCCAL_H 0xe4

//
// use 'unused' hardware register as a variable
//
#define osc_val OCR0BL

//
// flag to use '0x' prefix
//
#define USE_PREFIX

void attiny10_port_setup( void )
{
// set clock prescaler to 1/1 (8 MHz clock)
  CCP    = 0xD8; // set protection signature
  CLKPSR = 0;    // clock prescale = 1/1 (8 MHz clock)
} // void attiny10_port_setup()

void outhex1(uint8_t d)
{
  d &= 0x0f;
  outchar((10 > d) ? ('0' + d) : ('A' - 10 + d));
} // void outhex1()

void outhex2(uint16_t d)
{
  outhex1(0x0f & (d >> 4));
  outhex1(0x0f & d);
} //void outhex2()

void outhex4(uint16_t d)
{
  outhex1(0x0f & (d >> 12));
  outhex1(0x0f & (d >> 8));
  outhex1(0x0f & (d >> 4));
  outhex1(0x0f & d);
} //void outhex4()

// output delimiter
void outdelim( void )
{
  outchar(' '); // space as a delimiter
#if defined(USE_PREFIX) // enable '0x' prefix for hex number
  outchar('0');
  outchar('x');
#endif
} // void outdelim()

int main() 
{
  attiny10_port_setup(); 
  attiny10_soft_uart_setup();
  sei(); // enable interrupt
  osc_val = 0;
  for (;;) { // infinite loop
// step OSCCAL value and frequency mesurement
    for (osc_val = OSCCAL_L; osc_val <= OSCCAL_H; osc_val++) {
      CCP    = 0xD8;    // set protection signature
      OSCCAL = osc_val; // set OSCCAL value
      outdelim();
      outhex2(osc_val); // OSCCAL value
// dummy measurement to wait for oscillator settle
      pcnt_start(1000); // gate time = 1000 ms
      while (0 == p_ready) { sleep_mode(); }
// actual frequency measurement
      pcnt_start(200);   // gate time = 200 ms
      while (0 == p_ready) { sleep_mode(); }
      outdelim();
      outhex2(p_den); // denominator value (8 bit)
      outdelim();
      outhex2(0xff & (p_num >> 16)); // numerator value (24 bit)
      outhex4(0xffff & p_num);
      outchar(0x0D); // CR 
      outchar(0x0A); // LF
    } // for (osc_val = ...
  } // for (;;)
} // int main()

「pcnt_tn10.h」

/********************************************************/
/* Period measurement library for Arduino-0017 or above */
/*                                                      */
/* 2012/01/01 Modified for ATtiny10 by pcm1723          */
/* 2009/12/06 Created by pcm1723                        */
/********************************************************/
#include <inttypes.h>
#include <avr/interrupt.h>

#ifndef pcnt_tn10_h
#define pcnt_tn10_h

#define interrupts(x) sei()
#define noInterrupts(x) cli()

//namespace PeriodCounter {
extern void pcnt_start(int16_t ms);
extern void pcnt_stop( void );

extern volatile int16_t  p_den;
extern volatile uint32_t p_num;
extern volatile uint8_t  p_ready; 
//extern volatile uint8_t  p_mode;
//} // namespace PeriodCounter
#endif // #ifndef pcnt_tn10_h

「pcnt_tn10.c」

/********************************************************/
/* Period measurement library for Arduino-0017 or above */
/*                                                      */
/* 2012/01/01 Modified for ATtiny10 by pcm1723          */
/* 2009/12/18 Modified by pcm1723                       */
/* 2009/12/6  Created  by pcm1723                       */
/********************************************************/
#include "pcnt_tn10.h"

//namespace PeriodCounter {
// public
volatile uint8_t  p_ready; 
volatile int16_t  p_den;
volatile uint32_t p_num;
// private 
static volatile uint8_t  max_ucnt;
static volatile uint8_t  icr0x; // software extended ICR0
static volatile uint8_t  capt_cnt;
//static volatile uint16_t capt;
//static volatile uint16_t capt0;
//static volatile uint8_t  tifr0s; // saved TIFR0
static volatile uint8_t  tifr0s0; // saved TIFR0
static volatile uint8_t  capt0H;

//
// 'stunt' for SRAM usage saving
//
#define tifr0s  U8_3(p_num)
#define captH   U8_2(p_num)
#define capt    U16_L(p_num)
#define capt0   p_den

#define OVF_MASK (0x8000)

typedef volatile union tag_u32_u16_2_t
{
  uint32_t u32;
  uint16_t u16[2];
  uint8_t  u8[4];
} u32_u16_2_t; // 

// macro for 16 bit access
#define U16_L(x) (((u32_u16_2_t *)&x)->u16[0])
#define U16_H(x) (((u32_u16_2_t *)&x)->u16[1])

#define U8_0(x) (((u32_u16_2_t *)&x)->u8[0])
#define U8_1(x) (((u32_u16_2_t *)&x)->u8[1])
#define U8_2(x) (((u32_u16_2_t *)&x)->u8[2])
#define U8_3(x) (((u32_u16_2_t *)&x)->u8[3])

#define CAPT_MAX 0xFE

void pcnt_stop( void )
{
  noInterrupts();
  TIMSK0 &= ~(_BV(ICIE0) | _BV(TOIE0)); // disable CAP/OVF int
  interrupts();
  p_ready = 0; 
} // viod pcnt_stop()

void pcnt_timer0_setup(void)
{
  pcnt_stop(); // disable Timer interrupts
// Timer1 setup  
// FastPWM (WGM03:WGM02:WGM01:WGM00 = 1:1:1:1)
// clk=CPUCLK       (CS02:CS01:CS00 = 0:0:1)
  OCR0A   = 0xFFFF;      // MAX = 0xFFFF
  TCCR0A |= (_BV(WGM01) | _BV(WGM00)); // Fast PWM
  TCCR0B = (0xE0 & TCCR0B) 
         | (_BV(WGM03) | _BV(WGM02)  | _BV(CS00));  // clock = 8MHz
//  TCNT0  = 0x0000;     // clear counter
  capt_cnt = 0; // clear capture count
  icr0x    = 0; // clear ICR0 extension
  ACSR &= ~(_BV(ACIC)); // force ICP0 input 
  noInterrupts();
  TIFR0  |= (_BV(ICF0)  | _BV(TOV0)) ; // clear  CAPT/OVF flag
  TIMSK0 |= (_BV(ICIE0) | _BV(TOIE0)); // enable CAPT/OVF int
  interrupts();
} // void timer1_setup()

void pcnt_start(int16_t ms)
{
// convert millisec to OVF count
  max_ucnt = (ms / ((1000 * 0x10000UL) / F_CPU) ); 
  pcnt_timer0_setup();
} // void pcnt_start()

ISR(TIM0_OVF_vect)
{
  if (max_ucnt <= (++icr0x)) {
    TIMSK0 &= ~(_BV(ICIE0) | _BV(TOIE0)); // disable CAPT/OVF int
// detect unprocessed overflow for the first edge
    if (_BV(TOV0) & tifr0s0) { // 
      if (0 == (OVF_MASK & capt0)) {
       capt0H++; // adjust overflow
      } // if (0 == 
    } // if (_BV(TOV1) ...
// detect unprocessed overflow for the last edge
    if (_BV(TOV0) & tifr0s) { // 
      if (0 == (OVF_MASK & capt)) {
       captH++; // adjust overflow
      } // if (0 == 
    } // if (_BV(TOV1) ...
    U16_H(p_num) = captH - capt0H; // high word
    U16_L(p_num) = capt  - capt0;  // low word
    if (capt < capt0) { // detect borrow
      --U16_H(p_num);
    } // if (capt < capt0)
    p_den   = (capt_cnt-1);
    icr0x   = 0; // reset upper count
    p_ready = 1;
  }
} // ISRITIMER0_OVF_vect)

ISR(TIM0_CAPT_vect)
{
  tifr0s = TIFR0;
  capt   = ICR0;
  captH  = icr0x;
  if (0 == (capt_cnt++)) { // first time
    capt0   = capt;
    capt0H  = captH; // remember capture value
    tifr0s0 = tifr0s;
  } // if (0 == ... 
  if (CAPT_MAX == capt_cnt) { // is max count?
    TIMSK0 &= ~(_BV(ICIE0)); // no more capture
  } // if (INT_MAX == 
} // ISR(TIMER0_CAPT_vect)
//} // namespace PeriodCounter

「sft_uart.h」

/**************************************************/
/* sft_uart.h : ATtiny10 software UART Tx (only)  */
/*              using PB1 pin change interrupt    */
/*                                                */
/*              double speed (both edge) of       */
/*              Baud clock applied to PB1 (pin 3) */
/*                                                */
/*  (8N1) = 1 start + 8 data + 0 parity + 1 stop  */
/*                                                */
/* 2012/01/03 : Created by pcm1723                */
/**************************************************/
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#ifndef sft_uart_h
#define sft_uart_h

//
// port bit allocation to TxD
//
#define TX_BIT PORTB0

extern volatile int8_t uart_stat;
extern uint8_t uart_tx_buf;

extern void attiny10_soft_uart_setup( void );
extern void attiny10_soft_uart_enable( void );
extern void attiny10_soft_uart_disable( void );
extern void attiny10_soft_uart_send_byte(uint8_t d);

#endif // #ifndef sft_uart_h

「sft_uart.c」

/**************************************************/
/* sft_uart.c : ATtiny10 software UART Tx (only)  */
/*              using PB1 pin change interrupt    */
/*                                                */
/*              double speed (both edge) of       */
/*              Baud clock applied to PB1 (pin 3) */
/*                                                */
/*  (8N1) = 1 start + 8 data + 0 parity + 1 stop  */
/*                                                */
/* 2012/01/03 : Created by pcm1723                */
/**************************************************/
  
#include "sft_uart.h"

volatile int8_t uart_stat; // uart state counter
uint8_t uart_tx_buf; // uart Tx data buffer

//
// Pin Change Interrupt Service Routine
//
ISR(PCINT0_vect)
{
  if (0 == uart_stat) { // send a start bit
    PORTB &= ~(_BV(TX_BIT)); // send a '0'
  } else { // data bit or stop bit
    if (0x01 & uart_tx_buf) { // test LSB
      PORTB |=   _BV(TX_BIT); // send a '1'
    } else { 
      PORTB &= ~(_BV(TX_BIT)); // send a '0'
    } // if (0x01 & uart_tx_buf) { ...
    uart_tx_buf >>= 1; // shift right
    uart_tx_buf |= 0x80; // stop bits
  }  // if (0 == uart_stat) { ...
// advance uart state and check
  if (10 == (++uart_stat)) { // sending stop bit
    uart_stat = -1; // set to idle state
    PCICR &= ~(_BV(PCIE0)); // disable Pin Change Interrupt
  } // if (10 == ...
} // ISR(PCINT0_vect)

void attiny10_soft_uart_enable( void )
{
  cli(); // diable interrupt
  PCIFR |= _BV(PCIF0);  // clear pending interrupt flag
  PCICR |= _BV(PCIE0);  // enable Pin Change Interrupt
  sei(); // enable interrupt
} // void attiny10_soft_uart_enable()

void attiny10_soft_uart_disable( void )
{
  PCICR &= ~(_BV(PCIE0)); // disable Pin Change Interrupt
} // void attiny10_soft_uart_enable()

void attiny10_soft_uart_send_byte(uint8_t d)
{
  while (0 <= uart_stat) { sleep_mode(); } // wait for idle
  uart_tx_buf = d; // set tx data
  uart_stat   = 0; // send a start bit
  attiny10_soft_uart_enable();
} // void attiny10_soft_uart_send_byte()

void attiny10_soft_uart_setup( void )
{
  DDRB   = _BV(TX_BIT); // set PB0 (pin 1) to OUT, others to IN
  PORTB |= _BV(TX_BIT); // PB0 = 'H'
  PORTB |= _BV(PORTB1); // PB1 = pullup
  uart_stat   = -1;     // set idle state
  uart_tx_buf = 0xff;   // set MARK 
  attiny10_soft_uart_disable();
  PCMSK   = _BV(PORTB1); // enable PB1 Pin Change Interrupt
  set_sleep_mode(SLEEP_MODE_IDLE);
} // vod attiny10_soft_uart_setup()