いま、野球が好きな友人のために投球スピードを測る装置を作っています。
スピードガンは高いので代わりとなる装置を自作してみていますw
その過程を動画にしていますので、よかったら見ていってください。
んで、そのプログラムのソースコードを以下に掲載します。
疑問点等ありましたら、このブログか動画のコメント欄でご質問いただければ、可能な限り回答いたします。
/* * psmts01.cpp * * Created: 2021/06/03 23:29:02 * Author: piske */ // CPU の速度を指定します。 #define F_CPU 16000000UL // ライブラリをインクルードします。 #include <avr/io.h> #include <avr/interrupt.h> #include <avr/eeprom.h> #include <util/delay_basic.h> #include <util/delay.h> // Arduino の millis() の仕組みから転用 ここから #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) // the prescaler is set so that timer0 ticks every 64 clock cycles, and the // the overflow handler is called every 256 ticks. #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) // the whole number of milliseconds per timer0 overflow #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) // the fractional number of milliseconds per timer0 overflow. we shift right // by three to fit these numbers into a byte. (for the clock speeds we care // about - 8 and 16 MHz - this doesn't lose precision.) #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) #define FRACT_MAX (1000 >> 3) volatile unsigned long timer0_overflow_count = 0; volatile unsigned long timer0_millis = 0; static unsigned char timer0_fract = 0; ISR(TIMER0_OVF_vect) { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = timer0_millis; unsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; } unsigned long millis() { unsigned long m; uint8_t oldSREG = SREG; // disable interrupts while we read timer0_millis or we might get an // inconsistent value (e.g. in the middle of a write to timer0_millis) cli(); m = timer0_millis; SREG = oldSREG; return m; } // 転用ここまで // ビット操作のマクロ定義 #define sbi(PORT,BIT) PORT |= _BV(BIT) #define cbi(PORT,BIT) PORT &=~_BV(BIT) // 設定距離の EEPROM アドレス #define EEPROM_DISTANCE 0x0000 // 押下継続フラグ #define PRESS_DISTANCE_DOWN 0 #define PRESS_DISTANCE_UP 1 #define PRESS_MEASURE 2 // 関数プロトタイプの定義 void show7Seg(unsigned char digit, unsigned char pattern); // 7 セグメント点灯パターンの定義 unsigned char segments[] = {0b11110011, 0b01100000, 0b11010101, 0b11110100, 0b01100110, 0b10110110, 0b10110111, 0b11100010, 0b11110111, 0b11110110}; // 7 セグメント点灯パターンの定義 (配線間違えていないバージョン) //unsigned char segments[] = {0b11111100, // 0b01100000, // 0b11011010, // 0b11110010, // 0b01100110, // 0b10110110, // 0b10111110, // 0b11100100, // 0b11111110, // 0b11110110}; // メイン関数 int main(void) { unsigned char distance; // 設定距離 unsigned short speed; // 測定速度 unsigned long pitchTime; // 投球時間 unsigned char pressFlg; // 押下継続フラグ // ポート方向と内部プルアップを設定します。 DDRB = 0b00111111; PORTB = 0b11000000; DDRD = 0b00000000; PORTD = 0b01000000; // タイマーの設定をします。 TIMSK = (1<<TOIE0); TCCR0A = (0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00); TCCR0B = (0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00); sei(); // 設定距離を EEPROM から読み込みます。 eeprom_busy_wait(); distance = eeprom_read_byte(EEPROM_DISTANCE); // 設定距離が範囲外の場合、初期値を設定します。 if (distance < 1 || distance > 99) { distance = 15; } // グローバル変数を初期化します。 speed = 0; pitchTime = 0; pressFlg = 0; // メイン ループ while(1) { // 設定距離の桁を分けます。 unsigned char distance2 = distance / 10; unsigned char distance1 = distance - (distance2 * 10); // 設定距離を表示します。 show7Seg(0, segments[distance1]); if (distance2 > 0) { show7Seg(1, segments[distance2]); } // 測定速度の桁を分けます。 unsigned char speed3 = speed / 100; unsigned char speed2 = (speed - (speed3 * 100)) / 10; unsigned char speed1 = speed - (speed2 * 10) - (speed3 * 100); // 測定速度を表示します。 if (speed1 > 0 || speed2 > 0 || speed3 > 0) { show7Seg(2, segments[speed1]); // 手元表示器用 show7Seg(5, ~segments[speed1]); // 主表示器用 } if (speed2 > 0 || speed3 > 0) { show7Seg(3, segments[speed2]); // 手元表示器用 show7Seg(6, ~segments[speed2]); // 主表示器用 } if (speed3 > 0) { show7Seg(4, segments[speed3]); // 手元表示器用 show7Seg(7, ~segments[speed3]); // 主表示器用 } // 設定距離ダウンを検出します。 if (bit_is_clear(PINB, PB6)) { // 押下継続でない場合 if (bit_is_clear(pressFlg, PRESS_DISTANCE_DOWN)) { // 設定距離が 1m より大きい場合、1m ダウンします。 if (distance > 1) { distance--; // 設定距離を EEPROM に保存します。 eeprom_busy_wait(); eeprom_write_byte(EEPROM_DISTANCE, distance); } // 押下継続フラグを立てます。 sbi(pressFlg, PRESS_DISTANCE_DOWN); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } } else { // 押下継続フラグを落とします。 cbi(pressFlg, PRESS_DISTANCE_DOWN); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } // 設定距離アップを検出します。 if (bit_is_clear(PINB, PB7)) { // 押下継続でない場合 if (bit_is_clear(pressFlg, PRESS_DISTANCE_UP)) { // 設定距離が 99m より小さい場合、1m アップします。 if (distance < 99) { distance++; // 設定距離を EEPROM に保存します。 eeprom_busy_wait(); eeprom_write_byte(EEPROM_DISTANCE, distance); } // 押下継続フラグを立てます。 sbi(pressFlg, PRESS_DISTANCE_UP); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } } else { // 押下継続フラグを落とします。 cbi(pressFlg, PRESS_DISTANCE_UP); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } // 測定ボタンを検出します。 if (bit_is_clear(PIND, PD6)) { // 押下継続でない場合 if (bit_is_clear(pressFlg, PRESS_MEASURE)) { if (pitchTime == 0) { // 測定中でない場合、測定速度をクリアし、投球時間を保持します。 speed = 0; pitchTime = millis(); } else { // 測定中の場合、現在の時間 (捕球時間) と投球時間の差から測定速度を求めます。 speed = (double) distance * 3600 / (millis() - pitchTime); // 測定速度が 999km/h を超える場合、999km/h に設定します。 if (speed > 999) { speed = 999; } // 投球時間に 0 を設定します (測定中でないことを表します)。 pitchTime = 0; } // 押下継続フラグを立てます。 sbi(pressFlg, PRESS_MEASURE); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } } else { // 押下継続フラグを落とします。 cbi(pressFlg, PRESS_MEASURE); // 100マイクロ秒待ちます (チャタリング対策)。 _delay_us(100); } } } // 7 セグメント表示器に数字を表示します。 void show7Seg(unsigned char digit, unsigned char pattern) { unsigned char i; // シフト レジスタにパターンを送り込みます。 for (i = 0; i < 8; i++) { if ((pattern & 1) == 0) { sbi(PORTB, PB5); } sbi(PORTB, PB4); cbi(PORTB, PB4); cbi(PORTB, PB5); pattern = pattern >> 1; } // ライン デコーダに値を設定します。 for (i = 0; i < 3; i++) { if ((digit >> i & 1) == 1) { sbi(PORTB, PB3 - i); } else { cbi(PORTB, PB3 - i); } } // ライン デコーダのイネーブルをオンにし、100マイクロ秒待ちます。 sbi(PORTB, PB0); _delay_us(100); // ライン デコーダのイネーブルをオフにします。 cbi(PORTB, PB0); }
Comment feed