• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //////////////////////////////////////////////////////////////////
2 // example93.cpp
3 //
4 // Copyright (c) 2015 Robert Ramey
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 #include <iostream>
11 
12 // include headers to support safe integers
13 #include <boost/safe_numerics/cpp.hpp>
14 #include <boost/safe_numerics/exception.hpp>
15 #include <boost/safe_numerics/safe_integer.hpp>
16 #include <boost/safe_numerics/safe_integer_range.hpp>
17 #include <boost/safe_numerics/safe_integer_literal.hpp>
18 
19 // use same type promotion as used by the pic compiler
20 // target compiler XC8 supports:
21 using pic16_promotion = boost::safe_numerics::cpp<
22     8,  // char      8 bits
23     16, // short     16 bits
24     16, // int       16 bits
25     16, // long      16 bits
26     32  // long long 32 bits
27 >;
28 
29 // ***************************
30 // 1. Specify exception policies so we will generate a
31 // compile time error whenever an operation MIGHT fail.
32 
33 // ***************************
34 // generate runtime errors if operation could fail
35 using exception_policy = boost::safe_numerics::default_exception_policy;
36 
37 // generate compile time errors if operation could fail
38 using trap_policy = boost::safe_numerics::loose_trap_policy;
39 
40 // ***************************
41 // 2. Create a macro named literal an integral value
42 // that can be evaluated at compile time.
43 #define literal(n) make_safe_literal(n, pic16_promotion, void)
44 
45 // For min speed of 2 mm / sec (24.8 format)
46 // sec / step = sec / 2 mm * 2 mm / rotation * rotation / 200 steps
47 #define C0      literal(5000 << 8)
48 
49 // For max speed of 400 mm / sec
50 // sec / step = sec / 400 mm * 2 mm / rotation * rotation / 200 steps
51 #define C_MIN   literal(25 << 8)
52 
53 static_assert(
54     C0 < make_safe_literal(0xffffff, pic16_promotion,trap_policy),
55     "Largest step too long"
56 );
57 static_assert(
58     C_MIN > make_safe_literal(0, pic16_promotion,trap_policy),
59     "Smallest step must be greater than zero"
60 );
61 
62 // ***************************
63 // 3. Create special ranged types for the motor program
64 // These wiil guarantee that values are in the expected
65 // ranges and permit compile time determination of when
66 // exceptional conditions might occur.
67 
68 using pic_register_t = boost::safe_numerics::safe<
69     uint8_t,
70     pic16_promotion,
71     trap_policy // use for compiling and running tests
72 >;
73 
74 // note: the maximum value of step_t would be:
75 // 50000 = 500 mm / 2 mm/rotation * 200 steps/rotation.
76 // But in one expression the value of number of steps * 4 is
77 // used.  To prevent introduction of error, permit this
78 // type to hold the larger value.
79 using step_t = boost::safe_numerics::safe_unsigned_range<
80     0,
81     200000,
82     pic16_promotion,
83     exception_policy
84 >;
85 
86 // position
87 using position_t = boost::safe_numerics::safe_unsigned_range<
88     0,
89     50000, // 500 mm / 2 mm/rotation * 200 steps/rotation
90     pic16_promotion,
91     exception_policy
92 >;
93 
94 // next end of step timer value in format 24.8
95 // where the .8 is the number of bits in the fractional part.
96 using ccpr_t = boost::safe_numerics::safe<
97     uint32_t,
98     pic16_promotion,
99     exception_policy
100 >;
101 
102 // pulse length in format 24.8
103 // note: this value is constrainted to be a positive value. But
104 // we still need to make it a signed type. We get an arithmetic
105 // error when moving to a negative step number.
106 using c_t = boost::safe_numerics::safe_unsigned_range<
107     C_MIN,
108     C0,
109     pic16_promotion,
110     exception_policy
111 >;
112 
113 // 32 bit unsigned integer used for temporary purposes
114 using temp_t = boost::safe_numerics::safe_unsigned_range<
115     0, 0xffffffff,
116     pic16_promotion,
117     exception_policy
118 >;
119 
120 // index into phase table
121 // note: The legal values are 0-3.  So why must this be a signed
122 // type?  Turns out that expressions like phase_ix + d
123 // will convert both operands to unsigned.  This in turn will
124 // create an exception.  So leave it signed even though the
125 // value is greater than zero.
126 using phase_ix_t = boost::safe_numerics::safe_signed_range<
127     0,
128     3,
129     pic16_promotion,
130     trap_policy
131 >;
132 
133 // settings for control value output
134 using phase_t = boost::safe_numerics::safe<
135     uint16_t,
136     pic16_promotion,
137     trap_policy
138 >;
139 
140 // direction of rotation
141 using direction_t = boost::safe_numerics::safe_signed_range<
142     -1,
143     +1,
144     pic16_promotion,
145     trap_policy
146 >;
147 
148 // some number of microseconds
149 using microseconds = boost::safe_numerics::safe<
150     uint32_t,
151     pic16_promotion,
152     trap_policy
153 >;
154 
155 // ***************************
156 // emulate PIC features on the desktop
157 
158 // filter out special keyword used only by XC8 compiler
159 #define __interrupt
160 // filter out XC8 enable/disable global interrupts
161 #define ei()
162 #define di()
163 
164 // emulate PIC special registers
165 pic_register_t RCON;
166 pic_register_t INTCON;
167 pic_register_t CCP1IE;
168 pic_register_t CCP2IE;
169 pic_register_t PORTC;
170 pic_register_t TRISC;
171 pic_register_t T3CON;
172 pic_register_t T1CON;
173 
174 pic_register_t CCPR2H;
175 pic_register_t CCPR2L;
176 pic_register_t CCPR1H;
177 pic_register_t CCPR1L;
178 pic_register_t CCP1CON;
179 pic_register_t CCP2CON;
180 pic_register_t TMR1H;
181 pic_register_t TMR1L;
182 
183 // ***************************
184 // special checked type for bits - values restricted to 0 or 1
185 using safe_bit_t = boost::safe_numerics::safe_unsigned_range<
186     0,
187     1,
188     pic16_promotion,
189     trap_policy
190 >;
191 
192 // create type used to map PIC bit names to
193 // correct bit in PIC register
194 template<typename T, std::int8_t N>
195 struct bit {
196     T & m_word;
bitbit197     constexpr explicit bit(T & rhs) :
198         m_word(rhs)
199     {}
200     // special functions for assignment of literal
201     constexpr bit & operator=(decltype(literal(1))){
202         m_word |= literal(1 << N);
203         return *this;
204     }
205     constexpr bit & operator=(decltype(literal(0))){
206         m_word &= ~literal(1 << N);
207         return *this;
208     }
209     // operator to convert to 0 or 1
operator safe_bit_tbit210     constexpr operator safe_bit_t () const {
211         return m_word >> literal(N) & literal(1);
212     }
213 };
214 
215 // define bits for T1CON register
216 struct  {
217     bit<pic_register_t, 7> RD16{T1CON};
218     bit<pic_register_t, 5> T1CKPS1{T1CON};
219     bit<pic_register_t, 4> T1CKPS0{T1CON};
220     bit<pic_register_t, 3> T1OSCEN{T1CON};
221     bit<pic_register_t, 2> T1SYNC{T1CON};
222     bit<pic_register_t, 1> TMR1CS{T1CON};
223     bit<pic_register_t, 0> TMR1ON{T1CON};
224 } T1CONbits;
225 
226 // define bits for T1CON register
227 struct  {
228     bit<pic_register_t, 7> GEI{INTCON};
229     bit<pic_register_t, 5> PEIE{INTCON};
230     bit<pic_register_t, 4> TMR0IE{INTCON};
231     bit<pic_register_t, 3> RBIE{INTCON};
232     bit<pic_register_t, 2> TMR0IF{INTCON};
233     bit<pic_register_t, 1> INT0IF{INTCON};
234     bit<pic_register_t, 0> RBIF{INTCON};
235 } INTCONbits;
236 
237 #include "motor3.c"
238 
239 #include <chrono>
240 #include <thread>
241 
242 // round 24.8 format to microseconds
to_microseconds(ccpr_t t)243 microseconds to_microseconds(ccpr_t t){
244     return (t + literal(128)) / literal(256);
245 }
246 
247 using result_t = uint8_t;
248 const result_t success = 1;
249 const result_t fail = 0;
250 
251 // move motor to the indicated target position in steps
test(position_t new_position)252 result_t test(position_t new_position){
253     try {
254         std::cout << "move motor to " << new_position << '\n';
255         motor_run(new_position);
256         std::cout
257         << "step #" << ' '
258         << "delay(us)(24.8)" << ' '
259         << "delay(us)" << ' '
260         << "CCPR" << ' '
261         << "motor position" << '\n';
262         while(busy()){
263             std::this_thread::sleep_for(std::chrono::microseconds(to_microseconds(c)));
264             c_t last_c = c;
265             ccpr_t last_ccpr = ccpr;
266             isr_motor_step();
267             std::cout << i << ' '
268             << last_c << ' '
269             << to_microseconds(last_c) << ' '
270             << std::hex << last_ccpr << std::dec << ' '
271             << motor_position << '\n';
272         };
273     }
274     catch(const std::exception & e){
275         std::cout << e.what() << '\n';
276         return fail;
277     }
278     return success;
279 }
280 
main()281 int main(){
282     std::cout << "start test\n";
283     result_t result = success;
284     try {
285         initialize();
286         // move motor to position 1000
287         result &= test(literal(9000));
288         // move to the left before zero position
289         // fails to compile !
290         // result &= ! test(-10);
291         // move motor to position 200
292         result &= test(literal(200));
293         // move motor to position 200 again! Should result in no movement.
294         result &= test(literal(200));
295         // move motor to position 50000.
296         result &= test(literal(50000));
297         // move motor back to position 0.
298         result &= test(literal(0));
299     }
300     catch(...){
301         std::cout << "test interrupted\n";
302         return EXIT_FAILURE;
303     }
304     std::cout << "end test\n";
305     return result == success ? EXIT_SUCCESS : EXIT_FAILURE;
306 }
307