[25 marks]
#include <stdlib.h> /* required to use itoa */ #define MOUSE_X_ADDR 0xAC3000 #define MOUSE_Y_ADDR 0xAC3002 #define SLOW_SCAN_PERIOD 4 /* 64 / 4 = 16 Hz */ #define FAST_SCAN_PERIOD 2 /* 64 / 2 = 32 Hz */ #define QUEUE_SIZE 100 #define SS_LEN 8 /* length of substring is 8 chars */ x_posn_ptr = MOUSE_X_ADDR; /* initialize the register pointers */ y_posn_ptr = MOUSE_Y_ADDR; OS_EVENT *queue_ecb_ptr; /* create pointer to queue ECB */ void *queue_ptr[QUEUE_SIZE]; /* allocate memory for msg queue */ /* The following line should be in the start task */ queue_ecb_ptr = OSQCreate( **queue_ptr, (INT16U)QUEUE_SIZE ); void MousePollTask( void *data ) { INT8U err; /* var for return codes */ INT32U sys_time; INT16U last_x, last_y, curr_x, curr_y; INT16U *x_posn_ptr, *y_posn_ptr; INT16U scan_period = SLOW_SCAN_PERIOD; integer time_out = 0; char move_msg[] = "time = tttttttt X = xxxxxxxx Y = yyyyyyyy\n"; char substring[SS_LEN]; int t_offset = 7; /* offset to first time char in move_msg */ int x_offset = 22; /* offset to first x posn char in move_msg */ int y_offset = 37; /* offset to first y posn char in move_msg */ last_x = *x_posn_ptr; /* initialize the last mouse position */ last_y = *y_posn_ptr; OSTimeDly( scan_period ); /* first scanning delay */ while ( TRUE ) { curr_x = *x_posn_ptr; /* poll current mouse position */ curr_y = *y_posn_ptr; if ( (last_x != curr_x) || (last_y != curr_y) ) then { /* mouse position changed */ sys_time = OSTimeGet(); substring = itoa( (int)sys_time, substring, SS_LEN ); for (i=0; i<SS_LEN; i++) { /* load time chars */ move_msg[t_offset + i] = substring[i]; } substring = itoa( (int)curr_x, substring, SS_LEN ); for (i=0; i<SS_LEN; i++) { /* load X posn chars */ move_msg[x_offset + i] = substring[i]; } substring = itoa( (int)curr_y, substring, SS_LEN ); for (i=0; i<SS_LEN; i++) { /* load Y posn chars */ move_msg[y_offset + i] = substring[i]; } err = OSQPost( queue_ptr, move_msg ); scan_period = FAST_SCAN_PERIOD; time_out = 3; /* scan rapidly for at least next 3 times */ } else { /* mouse position did not change */ if ( time_out > 0) time_out--; /* leave scan_period as is */ else /* have timed out; go back to slower scanning period */ scan_period = SLOW_SCAN_PERIOD; } last_x = curr_x; /* curr posn now becomes last posn */ last_y = curr_y; OSTimeDelay( scan_period ); } }
.org 0x10000 /* arbitrary place to put the data structure */
.equ N, 1024 /* 1024 is an arbitrary buffer capacity */
EMPTY: .byte 0
FULL: .byte 0
RD_PTR: .word 0
WR_PTR: .word 0
CIRC_BUF: .org CIRC_BUF + (N * 4) /* reserve space for N longwords */
.org 0x20000 /* arbitrary place to put the code */
INIT_CIRC_BUF:
CLR.B EMPTY
CLR.B FULL
CLR.W RD_PTR
CLR.W WR_PTR
RTS
WRITE_BUF:
MOVEM.L A1-A2,-(SP)
TST.B FULL /* is the buffer full? */
BNE EXIT_WR_BUF /* if yes, exit */
LEA.L CIRC_BUF,A1
MOVE.W WR_PTR,A2 /* get write index */
LSL.W #2,A2 /* multiply index by 4 for longwords */
MOVE.L A0,0(A1,A2.W) /* fetch the stored pointer */
ADDQ.W #1,A2 /* increment the index */
CMPI.W #N,A2 /* check for index wrap-around */
BLT LD_WR_PTR
CLR.W A2 /* reset index back to 0 */
LD_WR_PTR:
MOVE.W A2,WR_PTR /* update stored write index */
EXIT_WR_BUF:
MOVEM.L (SP)+,A1-A2
RTS
READ_BUF:
MOVEM.L A1-A2,-(SP)
TST.B EMPTY /* is the buffer full? */
BNE EXIT_RD_BUF /* if yes, exit */
LEA.L CIRC_BUF,A1
MOVE.W RD_PTR,A2 /* get read index */
LSL.W #2,A2 /* multiply index by 4 for longwords */
MOVE.L 0(A1,A2.W),A0 /* fetch the stored pointer */
ADDQ.W #1,A2 /* increment the index */
CMPI.W #N,A2 /* check for index wrap-around */
BLT LD_RD_PTR
CLR.W A2 /* reset index back to 0 */
LD_RD_PTR:
MOVE.W A2,RD_PTR /* update stored read index */
EXIT_RD_BUF:
MOVEM.L (SP)+,A1-A2
RTS
+--------+--------+
0x010000 | EMPTY | 0x010001
+--------+--------+
| FULL |
+--------+--------+
| RD_PTR |
+--------+--------+
| WR_PTR |
+--------+--------+
0x010008 |CIRC_BUF | 0x010009
+--------+--------+
| |
*
*
*
0x011006 | | 0x011007
+--------+--------+
| Position Number |
Teeth Alignment |
Rotor Angle |
Winding 1 Current |
Winding 2 Current |
|---|---|---|---|---|
| #1 | a - 1 | 0 deg | forward | off |
| #2 | b - 2 | 9 deg | off | forward |
| #3 | c - 1' | 18 deg | reverse | off |
| #4 | d - 2' | 27 deg | off | reverse |
| #5 | e - 1 | 36 deg | forward | off |
| #6 | f - 2 | 45 deg | off | forward |
| #7 | g - 1' | 54 deg | reverse | off |
| #8 | h - 2' | 63 deg | off | reverse |
| #9 | i - 1 | 72 deg | forward | off |
| #10 | j - 2 | 81 deg | off | forward |
| Position Number |
Teeth Alignment |
Rotor Angle |
Winding 1 Current |
Winding 2 Current |
|---|---|---|---|---|
| #1 | a - 1 | 0 deg | forward | off |
| #2 | aj - 12' | -4.5 deg | forward | reverse |
| #3 | j - 2' | -9.0 deg | off | reverse |
| #4 | ij - 1'2' | -13.5 deg | reverse | reverse |
| #5 | i - 1' | -18.0 deg | reverse | off |
| #6 | hi - 1'2 | -22.5 deg | reverse | forward |
| #7 | h - 2 | -27.0 deg | off | forward |
| #8 | gh - 12 | -31.5 deg | forward | forward |
| #9 | g - 1 | -36.0 deg | forward | off |
| #10 | fg - 12' | -40.5 deg | forward | reverse |
The Channel Interrupt Status Register (CISR, at offset $020) can be read to determine which interrupts are currently active. After an ISR bit is set by an active interrupt-producing event, the bit must be explicitly cleared by the interrupt service routine after the interrupt-producing event has been handled.
TPU interrupts can be either autovectored or user vectored. Autovectoring is set up in the System Integration Module, or is forced for all system interrupts by asserting the AVEC input pin signal active (this is what we did in lab exercise #4).
The Interrupt Configuration Register (ICR, at offset $008) stores the TPU interrupt priority level in bits #10, 9 and 8. This priority level is shared by all 16 possible TPU channel interrupts. In addition, the ICR stores the channel interrupt base vector in bits #7, 6, 5 and 4. This vector gives the upper four bits of the exception vector numbers used by all TPU interrupts when user vectoring is being used. The lower four bits of the exception vector numbers are defined by the channel number. Thus the 16 TPU exception vectors lie in a contiguous block in the CPU's exception vector table.
[15 marks]
The PWM function is used to produce a train of binary pulses as an output
signal on any desired TPU channel(s).
The period of the signal is written by the CPU as the PWMPER parameter (the word
at parameter RAM offset $04).
The width of each pulse is written by the CPU as the PWMHI parameter (the word
at parameter RAM offset $06).
Both PWMPER and PWMHI represent time as counts of either TCR1 or TCR2.
The clock that is actually used is specified by bits 8 down to 5 in the
CHANNEL_CONTROL parameter (bits 7 down to 0 in the word at parameter RAM
offset $00).
The field value 0b0100 forces the use of TCR1, while the field value
0b0111 forces the use of TCR2.
Bits 1 and 0 of CHANNEL_CONTROL are used to hold pin commands: Command 0b01 causes normal operation (a train of output pulses). Command 0b10 force the output signal to stay low.
Note that the PWMHI and PWMPER parameters must be written together as a longword write into the parameter RAM at offset $04. (Note: Perhaps the hardware gets confused if this is not done.) The period and/or pulse width can be changed in this way at any time by the CPU, but the change will not take effect on the output signal until the start of the next output pulse. If the change needs to take effect right away, then ths can be done by issuing a $01 service request.
Parameter RAM word PWMRIS is updated automatically by the TPU. It gives the clock count that corresponds to the next expected rising transition in the output signal (that is, the start time of the next output pulse).
If the PWM is enabled to produce interrupts, then an interrupt will be triggered at the start (rising edge) of every output pulse.