////////////////////////////////////////////////////////////////// // example93.cpp // // Copyright (c) 2015 Robert Ramey // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include // include headers to support safe integers #include #include #include #include #include // use same type promotion as used by the pic compiler // target compiler XC8 supports: using pic16_promotion = boost::safe_numerics::cpp< 8, // char 8 bits 16, // short 16 bits 16, // int 16 bits 16, // long 16 bits 32 // long long 32 bits >; // *************************** // 1. Specify exception policies so we will generate a // compile time error whenever an operation MIGHT fail. // *************************** // generate runtime errors if operation could fail using exception_policy = boost::safe_numerics::default_exception_policy; // generate compile time errors if operation could fail using trap_policy = boost::safe_numerics::loose_trap_policy; // *************************** // 2. Create a macro named literal an integral value // that can be evaluated at compile time. #define literal(n) make_safe_literal(n, pic16_promotion, void) // For min speed of 2 mm / sec (24.8 format) // sec / step = sec / 2 mm * 2 mm / rotation * rotation / 200 steps #define C0 literal(5000 << 8) // For max speed of 400 mm / sec // sec / step = sec / 400 mm * 2 mm / rotation * rotation / 200 steps #define C_MIN literal(25 << 8) static_assert( C0 < make_safe_literal(0xffffff, pic16_promotion,trap_policy), "Largest step too long" ); static_assert( C_MIN > make_safe_literal(0, pic16_promotion,trap_policy), "Smallest step must be greater than zero" ); // *************************** // 3. Create special ranged types for the motor program // These wiil guarantee that values are in the expected // ranges and permit compile time determination of when // exceptional conditions might occur. using pic_register_t = boost::safe_numerics::safe< uint8_t, pic16_promotion, trap_policy // use for compiling and running tests >; // note: the maximum value of step_t would be: // 50000 = 500 mm / 2 mm/rotation * 200 steps/rotation. // But in one expression the value of number of steps * 4 is // used. To prevent introduction of error, permit this // type to hold the larger value. using step_t = boost::safe_numerics::safe_unsigned_range< 0, 200000, pic16_promotion, exception_policy >; // position using position_t = boost::safe_numerics::safe_unsigned_range< 0, 50000, // 500 mm / 2 mm/rotation * 200 steps/rotation pic16_promotion, exception_policy >; // next end of step timer value in format 24.8 // where the .8 is the number of bits in the fractional part. using ccpr_t = boost::safe_numerics::safe< uint32_t, pic16_promotion, exception_policy >; // pulse length in format 24.8 // note: this value is constrainted to be a positive value. But // we still need to make it a signed type. We get an arithmetic // error when moving to a negative step number. using c_t = boost::safe_numerics::safe_unsigned_range< C_MIN, C0, pic16_promotion, exception_policy >; // index into phase table // note: The legal values are 0-3. So why must this be a signed // type? Turns out that expressions like phase_ix + d // will convert both operands to unsigned. This in turn will // create an exception. So leave it signed even though the // value is greater than zero. using phase_ix_t = boost::safe_numerics::safe_signed_range< 0, 3, pic16_promotion, trap_policy >; // settings for control value output using phase_t = boost::safe_numerics::safe< uint16_t, pic16_promotion, trap_policy >; // direction of rotation using direction_t = boost::safe_numerics::safe_signed_range< -1, +1, pic16_promotion, trap_policy >; // some number of microseconds using microseconds = boost::safe_numerics::safe< uint32_t, pic16_promotion, trap_policy >; // *************************** // emulate PIC features on the desktop // filter out special keyword used only by XC8 compiler #define __interrupt // filter out XC8 enable/disable global interrupts #define ei() #define di() // emulate PIC special registers pic_register_t RCON; pic_register_t INTCON; pic_register_t CCP1IE; pic_register_t CCP2IE; pic_register_t PORTC; pic_register_t TRISC; pic_register_t T3CON; pic_register_t T1CON; pic_register_t CCPR2H; pic_register_t CCPR2L; pic_register_t CCPR1H; pic_register_t CCPR1L; pic_register_t CCP1CON; pic_register_t CCP2CON; pic_register_t TMR1H; pic_register_t TMR1L; // *************************** // special checked type for bits - values restricted to 0 or 1 using safe_bit_t = boost::safe_numerics::safe_unsigned_range< 0, 1, pic16_promotion, trap_policy >; // create type used to map PIC bit names to // correct bit in PIC register template struct bit { T & m_word; constexpr explicit bit(T & rhs) : m_word(rhs) {} // special functions for assignment of literal constexpr bit & operator=(decltype(literal(1))){ m_word |= literal(1 << N); return *this; } constexpr bit & operator=(decltype(literal(0))){ m_word &= ~literal(1 << N); return *this; } // operator to convert to 0 or 1 constexpr operator safe_bit_t () const { return m_word >> literal(N) & literal(1); } }; // define bits for T1CON register struct { bit RD16{T1CON}; bit T1CKPS1{T1CON}; bit T1CKPS0{T1CON}; bit T1OSCEN{T1CON}; bit T1SYNC{T1CON}; bit TMR1CS{T1CON}; bit TMR1ON{T1CON}; } T1CONbits; // define bits for T1CON register struct { bit GEI{INTCON}; bit PEIE{INTCON}; bit TMR0IE{INTCON}; bit RBIE{INTCON}; bit TMR0IF{INTCON}; bit INT0IF{INTCON}; bit RBIF{INTCON}; } INTCONbits; #include "motor3.c" #include #include // round 24.8 format to microseconds microseconds to_microseconds(ccpr_t t){ return (t + literal(128)) / literal(256); } using result_t = uint8_t; const result_t success = 1; const result_t fail = 0; // move motor to the indicated target position in steps result_t test(position_t new_position){ try { std::cout << "move motor to " << new_position << '\n'; motor_run(new_position); std::cout << "step #" << ' ' << "delay(us)(24.8)" << ' ' << "delay(us)" << ' ' << "CCPR" << ' ' << "motor position" << '\n'; while(busy()){ std::this_thread::sleep_for(std::chrono::microseconds(to_microseconds(c))); c_t last_c = c; ccpr_t last_ccpr = ccpr; isr_motor_step(); std::cout << i << ' ' << last_c << ' ' << to_microseconds(last_c) << ' ' << std::hex << last_ccpr << std::dec << ' ' << motor_position << '\n'; }; } catch(const std::exception & e){ std::cout << e.what() << '\n'; return fail; } return success; } int main(){ std::cout << "start test\n"; result_t result = success; try { initialize(); // move motor to position 1000 result &= test(literal(1000)); // move to the left before zero position // fails to compile ! // result &= ! test(-10); // move motor to position 200 result &= test(literal(200)); // move motor to position 200 again! Should result in no movement. result &= test(literal(200)); // move motor to position 50000. result &= test(literal(50000)); // move motor back to position 0. result &= test(literal(0)); } catch(...){ std::cout << "test interrupted\n"; return EXIT_FAILURE; } std::cout << "end test\n"; return result == success ? EXIT_SUCCESS : EXIT_FAILURE; }