UCR CS122A Prof. Frank Vahid Homework 2 Working in groups is encouraged. 5% extra credit for groups of 3 or more working together for an hour or more. Write down each others' names, and when/where you met. Every student should still solve every problem and submit his/her own solutions; working together is useful to understand the problems, discussion solution ideas, and compare answers. For the following problems, assume that you are using a microcontroller with the following: * an external interrupt input Int1 that causes a call to an interrupt service routine names ISR_Int1(). * an internal 16-bit timer that can be set by writing to the following registers, accessed as global variables in C: - short int timer1init: starting count value, counts down to 0 - int timer1enable: set to 1 to enable counting, 0 to disable - int timer1wrap: set to 1 to wrap around to starting count value each time the counter hits 0 - int timerdir: counts down if 0, up if 1. * an internal interrupt generated by timer1 hitting 0, causing a call to ISR_Timer1(). * 8-bit ports P0, P1, P2 and P3 accessible as global variables, each also accessible as individual bits (e.g., P0.0-P0.7). * A clock with a period of 100 nanoseconds. 1. Program the microcontroller (including the main function and needed ISRs, for this problem and all others) to pulse pin P0.1 for one millisecond high, one millisecond low, forever. One solution: int outpinval = 0; // state of the output pin, updated by ISR_Timer1 ISR_Timer1() // called every 1 ms { if (outpinval == 0) { outpinval = 1; } else { // outpinval must be 1 outpinval = 0; } } main() { timer1init = 10,000; // calls ISR every 10,000*100ns = 1 ms timer1wrap = 1; timer1enable = 1; while (1) { P0.1 = outpinval; // ISR_Timer1 updates outpinval state. We could // also assigned P0.1 in the ISR, but it seems // cleaner to assign P0.1 in main. } } The above solution works, but it may be cleaner to use code that more closely reflects a state machine, as we'll see in subsequent problems. 2. Program the microcontroller to pulse pin P0.1 for one second high, one second low, forever. Hint: your 16-bit timer driven by a 100 nanosecond clock can't count for 1 second -- you'll need to use a variable to count the number of ISR calls. int outpinval = 0; // state of the output pin int ms_cnt = 0; // number of milliseconds counted, 0-999 ISR_Timer1() // called every 1 ms { ms_cnt = (ms_cnt + 1) % 1000; // wraps around every second if (ms_cnt == 0) // time to update output pin's state (1 sec passed) { if (outpinval == 0) { outpinval = 1; } else { // outpinval must be 1 outpinval = 0; } } } main() { timer1init = 10,000; // calls ISR every 10,000*100ns = 1 ms timer1wrap = 1; timer1enable = 1; while (1) { P0.1 = outpinval; // ISR updates outpinval every second } } 3. Program the microcontroller to pulse pin P0.1 every one second with a 90% duty cycle: 0.9 seconds high, 0.1 seconds low. Now a cleaner state machine implementation in the C code would benefit us greatly. int globaltime = 0; // number of milliseconds counted, 0-999 int ms_wrap = 100,000; // wraps every 100 seconds // why not wrap every 1 second? We could, but every 100 seconds // reduces errors in case we are slow in comparing timeout values ISR_Timer1() // called every 1 ms { globaltime = (globaltime + 1) % ms_wrap; } // returns 1 (true) after ms_timeout milliseconds have passed since // init_globaltime. Returns 0 otherwise. Note: doesn't work properly // if globaltime wraps around and passes init_globaltime. int Timeout(int init_globaltime, int ms_timeout) { int diff; diff = globaltime - init_globaltime; if (diff >= 0) { return (diff >= ms_timeout); } else { // negative diff, meaning globaltime wrapped around return ( (ms_wrap + diff) >= ms_timeout); } } void main() { int state_start_time; // records the global time when we entered a state timer1init = 10,000; // calls ISR every 10,000*100ns = 1 ms timer1wrap = 1; timer1enable = 1; while (1) { // Since our state machine just goes from one state to the next, // we'll use a simplified state machine in C implementation method // state 0: high for 0.9 s (900 ms) P0.1 = 1; state_start_time = globaltime; while (! Timeout(state_start_time, 900) ) ; // wait 900 ms // state 1: low for 0.1 s (100 ms) P0.1 = 0; state_start_time = globaltime; while (! Timeout(state_start_time, 900) ) ; // wait 900 ms } } 4. Connect and program the microcontroller to implement the following system. The system has one button and four LEDs. When the button is pressed, LED1 lights for 1 second, then LED2 lights for 1 second, then LED3 for 1 second, then LED4 for 1 second. When one LED is on, all others are off. Once the sequence starts, button presses are ignored until the sequence completes. Although we could use the same style as the previous solution, noting that everything here is a multiple of 1 second, let's try a different style here that involves enabling and disabling the timer. int activate_sequence=0; // pressing button changes to 1 int ms_cnt = 0; int ms_wrap = 10000; // we should never even come close to this void ISR_Timer1() // called every 1 ms { ms_cnt = (ms_cnt + 1) % ms_wrap; } void ISR_Int1() // called when button pressed { activate_sequence = 1; } void main() { timer1init = 10,000; // calls ISR every 10,000*100ns = 1 ms timer1wrap = 1; timer1enable = 0; // note that timer is not initially enabled while (1) { // state 0: wait for the button press, turn off all LEDs LED1 = LED2 = LED3 = LED4 = 0; while (! activate_sequence) ; // state 1: LED1 on for one second LED1 = 1; LED2=LED3=LED4=0; ms_cnt = 0; timer1enable = 1; // start timer while (ms_cnt < 1000) ; // wait for 1 second timer1enable = 0; // state 2: LED2 on for one second LED2 = 1; LED1=LED3=LED4=0; ms_cnt = 0; timer1enable = 1; // start timer while (ms_cnt < 1000) ; // wait for 1 second timer1enable = 0; // Hmm, maybe each state should have been made into a function... // state 3: LED3 on for one second LED3 = 1; LED1=LED2=LED4=0; ms_cnt = 0; timer1enable = 1; // start timer while (ms_cnt < 1000) ; // wait for 1 second timer1enable = 0; // state 4: LED4 on for one second LED4 = 1; LED1=LED2=LED3=0; ms_cnt = 0; timer1enable = 1; // start timer while (ms_cnt < 1000) ; // wait for 1 second timer1enable = 0; // All done, return to state 0 and wait for next button press activate_sequence = 0; } } 5. Program the microcontroller having the four output LEDs to use an integer array A[10] to indicate which lights to light, as follows. When the input button is pressed, the program should then step through the array one element per second, starting with A[0]. If the array element is 1, the program should light LED1 for 1 second, then LED2 for 1 second, then LED3 for 1 second, then LED4 for 1 second. If the element is 0, that element does not cause LEDs to light. Because the program proceeds to the next element each second, note that more than one LED may be lit at a given time. The program should proceed through all ten elements, and then wait for the next button press, which starts the sequence all over again. Getting a bit complicated. Similar to the previous problem, except that we have to step through that array after the button press. What model is most appropriate? One could try four different FSMs. But perhaps the simplest model is an FSMD, where we maintain a "shift register" with four elements that indicate which LEDs to light. On each state when active, we shift the current array element into the shift register, while also shifting the other register items to the right. int activate_sequence=0; // pressing button changes to 1 int ms_cnt = 0; int ms_wrap = 10000; // we should never even come close to this void ISR_Timer1() // called every 1 ms { ms_cnt = (ms_cnt + 1) % ms_wrap; } void ISR_Int1() // called when button pressed { activate_sequence = 1; } void main() { int A[10] = {1, 0, 1, 1, 1, 0, 0, 0, 1, 1}; // example values int LED_ARRAY[4] = {0, 0, 0, 0}; timer1init = 10,000; // calls ISR every 10,000*100ns = 1 ms timer1wrap = 1; timer1enable = 0; // note that timer is not initially enabled while (1) { // wait for the button press, turn off all LEDs LED1 = LED2 = LED3 = LED4 = 0; while (! activate_sequence) ; // Now we read the array items, one per second, shift into // LED array, and update output array for (int i=0; i < 10; i++) { // shift LED array LED_ARRAY[3] = LED_ARRAY[2]; LED_ARRAY[2] = LED_ARRAY[1]; LED_ARRAY[1] = LED_ARRAY[0]; LED_ARRAY[0] = A[i]; // shift in new value // light the LEDs LED0 = LED_ARRAY[0]; LED1 = LED_ARRAY[1]; LED2 = LED_ARRAY[2]; LED3 = LED_ARRAY[3]; // stay in this state for 1 second ms_cnt = 0; timer1enable = 1; // start timer while (ms_cnt < 1000) ; // wait for 1 second timer1enable = 0; // Move on to next array item } // for loop // All done, return to state 0 and wait for next button press activate_sequence = 0; } // while (1) } // main 6. Connect and program the microcontroller to implement a car speed timer. Two sensors are embedded in a highway, separated by 30 feet. Each sensor outputs a 1 when a car is over the sensor. Thus, measuring the time it takes for the car to go from the first sensor to the second sensor, and knowing the distance, we can compute the speed (ignore the possibility of a second car triggering the first sensor before the first car passes the second sensor, or of the car changing lanes after crossing over the first sensor). Your program should output the speed on an 8-bit port P3 in miles per hour. Be sure to compute reasonable ranges for typical car speeds, and ensure your systems handles those ranges.