7.5 Threaded User Mode Driver
7.5.2 Threaded Analog Output
The analog output uses a worker thread. This thread waits until it receives an analog output command from the main thread. Then it executes this command and waits for the next. This means that the main thread gives a short command to the worker thread, and then it can continue executing the rest of its code.
Chapter 8
Implementation of I/O-card
Software
This chapter describes how the design from chapter 7 was implemented in several files of C-code. Only the header files are shown here. The code files can be found in the appendix. All the functions definitions and variables are commented in the header files, which makes it possible to know the purpose of the different functions by looking at the headers. Some parts of the code files are shown here and used as examples.
8.1
ATmega128 Firmware
The code for ATmega128 are divided into several pairs of headers and code files. Each of these pairs provides the code for controlling a part of the ATmega128.
8.1.1 am128main
This file contain the main function of the ATmega128, which is where the code execution starts. This main function will initialize the I/O-card, with the help of the other code files and then start the main program-loop. This loop will run as long as the ATmega128 has power, and will listen for SPI messages from AVR32.
The code below is the header file am128main.h, and the code file am128main.c is in the appendix C.1.2. The header file has some #define statements that enables or disables different features in the code. These are used to change how the I/O-card works without having to make changes in the rest of the code.
1 // D e f i n e s t h e c l o c k f r e q u e n c y o f t h e d e v i c e . Needed f o r u t i l / d e l a y . h . 2 #d e f i ne F CPU 1 6 0 0 0 0 0 0 3 4 // I n c l u d e s . 5 #include <a v r / i o . h> 6 #include < u t i l / d e l a y . h> 7 #include <a v r / i n t e r r u p t . h> 8 9 // Macros f o r b i t o p e r a t i o n s . 10 #d e f i ne s e t b i t ( r e g , b i t ) ( r e g |= ( 1 << b i t ) ) 57
11 #d e f i ne c l e a r b i t ( r e g , b i t ) ( r e g &= ˜ ( 1 << b i t ) ) 12 #d e f i ne t e s t b i t ( r e g , b i t ) ( r e g & ( 1 << b i t ) ) 13 #d e f i ne l o o p b i t i s s e t ( r e g , b i t ) while ( ! t e s t b i t ( r e g , b i t ) ) 14 #d e f i ne l o o p b i t i s c l e a r ( r e g , b i t ) while ( t e s t b i t ( r e g , b i t ) ) 15 16 // E n a b l e s d e b u g g i n g o v e r RS−232. 17 // T h i s w i l l g i v e p r o b l e m s , s i n c e t h e d e b u g g i n g t a k e s a l o n g t i m e . 18 // #d e f i n e RS232 DEBUG 19 20 // E n a b l e s t i m o u t o f SPI t r a n s f e r s . 21 // Timeout p e r i o d = TIMEOUT ∗ 20 m i c r o s e c o n d s . 22 //#d e f i n e TIMEOUT ENABLE 23 #d e f i ne TIMEOUT 25 24
25 // D e f i n e s i f b u f f e r i n g mode o r i n t e r r u p t mode w i l l b e u s e d f o r ADC. 26 #d e f i ne BUFFERING MODE
27 //#d e f i n e INTERRUPT MODE 28
29 // Main f u n c t i o n t h a t i n i t i a l i z e t h e I /O−c a r d and t h e n s t a r t s t h e main program l o o p , w h i c h c o n t i n u o u s l y c h e c k s f o r new m e s s a g e s from t h e AVR32 .
30 i n t main ( ) ; 31
32 #include ”am128uart . h ” 33 #include ”am128io . h ”
The code below is the main program loop, which will wait for new commands. On line 14 the SPI data register are set to the SLAVE_READY code. This makes sure that the next byte the it sends to the master will be this code. Line 17 waits for a new SPI transfer, and the code received in this SPI transfer is sent to the function at line 24. This function will validate the code and continue the SPI command.
11 // Main program l o o p . 12 while ( 1 ) { 13 // P r e p a r e t o s e n d r e a d y s i g n a l , and w a i t f o r c o d e from m a s t e r . 14 SPDR = SLAVE READY ; 15 16 // Wait f o r SPI t r a n s f e r . 17 while ( ! t e s t b i t ( SPSR , SPIF ) ) { 18 #i f d e f BUFFERING MODE 19 // Checks i f c o n v e r s i o n i s f i n i s h e d i f u s i n g b u f e r i n g mode . 20 ADC CheckBuffer ( ) ; 21 #e n d i f 22 } 23 // S t a r t r e c e i v i n g new command . 24 IO NewCommand (SPDR) ; 25 }
When in buffering mode, the ATmega128 checks for completed ADC conversions while waiting for SPI messages. This is only checked inside this loop, because checking it during a SPI message can disturb the synchronization between ATmega128 and AVR32. It would be more logic to use interrupts when a ADC conversion was completed. However, when doing this about 6% of the SPI messages returned errors. This was because these interrupts happend in the middle of a SPI command, and it made the ATmega128 unable to return the correct data at the correct time.
By polling the ADC whenever the ATmega128 waits for a new message (not new transfer) the work of managing ADC will only be done when the ATmega128 normally would do a busy wait for SPI. For this to work robustly, it’s important that all the other “wait for SPI” functions in the program has timeouts as described in 7.2.3. This is because the ATmega128 will be unable to check the ADC conversions if it doesn’t return to the the loop described here.
8.1. ATMEGA128 FIRMWARE 59
8.1.2 am128io
am128io.h and am128io.c contains functions that receive a code that the main function has received from AVR32. These functions will receive the rest of the message. The code below is the header file am128io.h, and the code file am128io.c is in the appendix C.1.3. This header file is quite long, but it also defines how all of the different messages are implemented. 1 #i f n d e f AM128 IO 2 #d e f i ne AM128 IO 3 4 // I n i t i a l i z e t h e d i f f e r e n t p a r t s o f t h e I /O−c a r d . 5 void I O I n i t ( ) ; 6 7 // F u n c t i o n t h a t c h e c k s i f an r e c e i v e d d a t a ( r x ) was a c o r r e c t a c k o f t h e t r a n s m i t t e d d a t a ( t x ) .
8 signed char IO TestAck ( unsigned char tx , unsigned char r x ) ; 9
10 // Checks t h e r e c e i v e d c o d e and s t a r t s r e c e i v i n g a new command i f c o d e i s v a l i d . 11 signed char IO NewCommand ( unsigned char c o d e ) ;
12 13 /∗
14 Analog i n SPI message . 15
16 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r 17 1 DUMMY c o d e + 1 18 Wait f o r i n t e r r u p t i f i n i n t e r r u p t mode . 19 2 DUMMY v a u l e H 20 3 v a l u e H + 1 v a u l e L 21 4 v a l u e L + 1 DUMMY 22 5 MASTER OK SLAVE OK 23 ∗/
24 signed char I O A n a l o g I n ( unsigned char c o d e ) ; 25
26 /∗
27 Analog o u t SPI message . 28
29 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r 30 1 v a l u e H c o d e + 1
31 2 v a l u e L v a u l e H + 1 32 3 DUMMY v a u l e L + 1 33 4 MASTER OK SLAVE OK 34 ∗/
35 signed char IO AnalogOut ( unsigned char c o d e ) ; 36
37 /∗
38 D i g i t a l i n SPI message . 39
40 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r 41 1 DUMMY c o d e + 1
42 2 DUMMY v a l u e
43 3 v a l u e + 1 DUMMY 44 4 MASTER OK SLAVE OK 45 ∗/
46 signed char I O D i g i t a l I n ( unsigned char c o d e ) ; 47
48 /∗
49 D i g i t a l o u t SPI message . 50
51 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r
52 1 mask c o d e + 1
53 2 v a l u e mask + 1 54 3 DUMMY v a u l e + 1 55 4 MASTER OK SLAVE OK 56 ∗/
58
59 #i f d e f BUFFERING MODE 60 /∗
61 Analog i n e n a b l e SPI message . ( Only u s e d i n b u f f e r i n g mode ) 62
63 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r 64 1 e n a b l e c o d e + 1
65 2 DUMMY e n a b l e + 1 66 3 MASTER OK SLAVE OK 67 ∗/
68 signed char I O A n a l o g I n E n a b l e ( unsigned char c o d e ) ; 69 #e n d i f
70 71 /∗
72 Analog o u t r e s o l u t i o n SPI message . 73
74 Number R e c e i v e d from m a s t e r Retu rned t o m a s t e r 75 1 r e s o l u t i o n c o d e + 1
76 2 DUMMY r e s o l u t i o n + 1 77 4 MASTER OK SLAVE OK 78 ∗/
79 signed char IO AnalogOutRes ( unsigned char c o d e ) ; 80 81 // I n c l u d e s . 82 #include ” . . / s p i B y t e C o d e s . h ” 83 #include ” a m 1 2 8 s p i S l a v e . h ” 84 #include ”am128pwm . h ” 85 #include ”am128adc . h ” 86 87 #include ”am128io . c ” 88 89 #e n d i f //AM128 IO
Below is the function IO_AnalogOut() from am128io.c. This function is a good example on how the functions in this file works. The other functions are all similar.
113 signed char IO AnalogOut ( unsigned char c o d e ) 114 {
115 unsigned char valueH , va l u e L , r x ; 116 char r e t ; 117 118 // 1 s t b y t e : s t o r e r e c e i v e d valueH , r e t u r n c o d e + 1 . 119 r e t = S P I S l a v e T r a n s f e r B y t e ( c o d e + 1 , &valueH , TIMEOUT) ; 120 i f ( r e t < 0 ) return r e t ; 121 122 // 2nd b y t e : s t o r e r e c e i v e d v a l u e L , r e t u r n v a l u e H + 1 . 123 r e t = S P I S l a v e T r a n s f e r B y t e ( valueH + 1 , &va l u e L , TIMEOUT) ; 124 i f ( r e t < 0 ) return r e t ; 125 126 // 3 r d b y t e : c h e c k r e c e i v e d DUMMY, r e t u r n v a l u e L + 1 . 127 r e t = S P I S l a v e T r a n s f e r B y t e ( v a l u e L + 1 , &rx , TIMEOUT) ; 128 i f ( r e t < 0 ) return r e t ; 129 i f ( r x != DUMMY) return −1; 130
131 // 4 t h b y t e : c h e c k r e c e i v e d MASTER OK, r e t u r n SLAVE OK . 132 r e t = S P I S l a v e T r a n s f e r B y t e (SLAVE OK, &rx , TIMEOUT) ; 133 i f ( r e t < 0 ) return r e t ;
134 i f ( r x != MASTER OK) return −1; 135
136 // When t h e message i s c o n f i r m e d t h e mask and d a t a a r e a p p l i e d t o d i g i t a l o u t . 137 PWM SetOutValue (CODE TO CH AOUT( c o d e ) , CODE TO SCH AOUT( c o d e ) , valueH , v a l u e L ) ; 138
139 // Debug p r i n t i n g , i f e n a b l e d . 140 UART Print ( ’ c ’ , c o d e ) ;
141 UART Print ( ’ h ’ , valueH ) ; 142 UART Print ( ’ l ’ , v a l u e L ) ; 143 }
8.1. ATMEGA128 FIRMWARE 61
Each of the transfers is marked with a comment explaining what ATmega128 sends and what it expects to receive. After each transfer the return value is checked. If this is negative it means that the function timed out before a transfer was received. If the return value is negative or the received data wasn’t the expected data, the function returns an error.
After the 4th transfer is controlled, the received data is written to PWM. The channel and subchannel of the PWM is defined by the code. Before the function returns, it also prints some debugging information via UART, if this is enabled.
8.1.3 am128slaveSpi
am128slaveSpi.h and am128slaveSpi.c contain functions for using the ATmega128 as a SPI slave device. During the development several different slave transfer functions were used. The end result was just one function that sends and receive one byte and has timeout if this is enabled.
The functions that receives the SPI messages does this one transfer at the time. As opposed to the AVR32 SPI driver, the ATmega128 is able to handle each SPI transfer individually without losing effectivity. This gives code that’s easier to read and understand.
The code below is the header file am128spiSlave.h, and the code file am128spiSlave.c is in the appendix C.1.4.
1 #i f n d e f AM128 SPI SLAVE 2 #d e f i ne AM128 SPI SLAVE 3
4 // I n i t i a l i z e t h e ATmega128 a s a SPI s l a v e . I f t i m e o u t a r e e n a b l e d , t h e n t h i s f u n c t i o n w i l l i n i t i a l i z e a t i m e r a s w e l l .
5 void S P I S l a v e I n i t ( void ) ; 6
7 // F u n c t i o n f o r t r a n s f e r one b y t e o f d a t a . I t w i l l w a i t f o r an SPI t r a n s f e r from t h e m a s t e r . I f t i m e o u t s a r e e n a b l e d , t h e f u n c t i o n w i l l r e t u r n an e r r o r a f t e r some t i m e . S i n c e SPI i s f u l l y d u p l e x , t h i s f u n c t i o n w i l l b o t h s e n d and r e c e i v e d a t a .
8 signed char S P I S l a v e T r a n s f e r B y t e ( unsigned char tx , unsigned char ∗ rx , unsigned char u s e c ) ;
9
10 #include ” a m 1 2 8 s p i S l a v e . c ” 11
12 #e n d i f //AM128 SPI SLAVE
Below is the while loop that is used to wait for a SPI transfer. To wait for something to happen with a loop like this, is called a busy-wait, and normally it’s something that should be avoided. This is because a busy-wait wastes many processor cycles that could be used by other processes. For the ATmega128 this isn’t a problem since it can only have