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()