AVR286: LIN Firmware Base for LIN/UART Controller LIN Features The LIN (Local Interconnect Network) is a serial communications protocol which efficiently supports the control of mechatronics nodes in distributed automotive applications. The main properties of the LIN bus are: • • • • • • Single master with multiple slaves concept Low cost silicon implementation based on common UART communication Self synchronization with on-chip oscillator in slave node Deterministic signal transmission with signal propagation time computable in advance Low cost single-wire implementation Speed up to 20 Kbit/s. 8-bit Microcontroller Application Note 1. Atmel® LIN/UART Controller The LIN/UART Controller is available on some AVR ® micro controllers such as ATmega32/64/M1/C1 or ATtiny167. 1.1 Features • Hardware Implementation of LIN 2.x (LIN 1.x Compatibility) • Small, CPU Efficient and Independent Master/Slave Routines Based on “LIN Work Flow • • • • • • • Concept” of LIN 2.x Specification Automatic LIN Header Handling and Filtering of Irrelevant LIN Frames Automatic LIN Response Handling Extended LIN Error Detection and Signaling Hardware Frame Time-out Detection “Break-in-data” Support Capability Automatic Re-synchronization to Ensure Proper Frame Integrity Fully Flexible Extended Frames Support Capabilities 1.2 Controller Overview The LIN/UART Controller is designed to match as closely as possible the LIN software application structure. The LIN software application is developed as independent tasks, several slave tasks and one master task. The controller conforms to this partitioning. The only link between a master task and a slave task in a master node is the (interrupt) flag IDOK. For a master task, this event represents the end of this task, for the slave task it represents the beginning of this task. When the slave task is warned of an identifier presence, it has first to analyze it to know what to do with the response. Hardware flags identify the presence of one of the specific identifiers from 60 (0x3C) up to 63 (0x3F). 8123A–AVR–03/08 2. Firmware Base 2.1 Description This application note provides a firmware base (drivers) to use the LIN/UART Controller on the ATmega32/64/M1/C1 and ATtiny167. It assumes that readers are familiar with the device architecture and with the LIN/UART Controller. A minimum knowledge of LIN specification v1.x or v2.x (www.lin-subbus.org) is also required to understand the content of this document. This document is written for the software developers to help in the development of their applications or their LIN stack construction with the Atmel micro controllers. The drivers are designed to simplify the programming of the LIN/UART Controller. Some general information and tips are also discussed to help the developers to build quickly their applications. In addition, this document provides application examples to illustrate the usage of these drivers. 2.2 Limitations • Diagnostics frames: handled by LIN library, software placed above the drivers, • Sleep & Wake-up: features application dependent, • Configuration for LIN hardware test purposes: not included. 2.3 Terminology ID: LIN Identifier Tx: Transmission Rx: Reception 2.4 Coding Style The coding style explained below are important to understand the firmware: • Defined constants use caps characters. #define CONF_LINBRR 25 • C-macro functions use the first character as cap #define Is_lin_header_ready() ((LINSIR & (1<<LIDOK)) ? TRUE: FALSE) • Functions use small caps characters void lin_get_response (unsigned char *l_data) 2.5 Device-specific I/O Header File The firmware described in this document is based upon the device-specific I/O header file provided by the compiler: 1. AVR GCC: “iom64m1.h”, “iom64c1.h”, “iom32m1.h” or “iom32c1.h”, 2. IAR: “iom64m1.h”, “iom64c1.h”, “iom32m1.h” or “iom32c1.h”, 3. ... In the device-specific I/O header file, the name, the address and the content (register-bit) of all the registers are defined. 2 AVR286 8123A–AVR–03/08 AVR286 3. Definitions First, an application will have to define the following values: #define FOSC 8000 #define LIN_BAUDRATE 19200 3.1 // Device frequency in KHz // LIN baudrate in bit/s Bit Time The bit time definition CONF_LINBRR, setup the baud rate for communication. This definition is controlled by FOSC and LIN_BAUDRATE. At the end, only one CONF_LINBRR value is retained to program the LIN baud rate register of the LIN/UART Controller. #ifndef FOSC #error You must define FOSC #elif #ifndef #error #elif #define #elif #define #elif #define #elif #define #else #error #endif FOSC == 16000 // LIN_BAUDRATE You must define LIN_BAUDRATE LIN_BAUDRATE == 19200 CONF_LINBRR 25 // LIN_BAUDRATE == 9600 CONF_LINBRR 51 // LIN_BAUDRATE == 4800 CONF_LINBRR 103 // LIN_BAUDRATE == 2400 CONF_LINBRR 207 // if device frequency = 16 MHz (0x19) 19.2 kbps, error = 0.2% (0x33) 9.6 kbps, error= 0.2% (0x67) 4.8 kbps, error= 0.2% (0xCF) 2.4 kbps, error=-0.2% Not available LIN_BAUDRATE value #elif #ifndef #error #elif #define #elif #define #elif #define #elif #define FOSC == 8000 // LIN_BAUDRATE You must define LIN_BAUDRATE LIN_BAUDRATE == 19200 CONF_LINBRR 12 // LIN_BAUDRATE == 9600 CONF_LINBRR 25 // LIN_BAUDRATE == 4800 CONF_LINBRR 51 // LIN_BAUDRATE == 2400 CONF_LINBRR 103 // #else #error #endif Not available LIN_BAUDRATE value #else #error #endif Not available FOSC value if device frequency = 8 MHz (0x0C) 19.2 kbps, error = 0.2% (0x19) 9.6 kbps, error= 0.2% (0x33) 4.8 kbps, error= 0.2% (0x67) 2.4 kbps, error=-0.2% This method uses the conditional compilation of the C preprocessor. This method saves time and code bytes compared to a code line in C. It is very easy to add a new set of CONF_LINBRR values for other FOSC definitions (ex: FOSC == 4000). The CONF_LINBRR value is to calculate using the following formula: CONF_LINBRR = 3.2 [round {(FOSC x 1000) / (32 x LIN_BAUDRATE)}]-1 Configuration // LIN protocols #define LIN_1X #define LIN_2X (1<<LIN13) (0<<LIN13) 3 8123A–AVR–03/08 // LIN commands #define LIN_CMD_MASK ((1<<LCMD1)|(1<<LCMD0)) #define LIN_RX_HEADER ((0<<LCMD1)|(0<<LCMD0)) #define LIN_ABORT ((0<<LCMD1)|(0<<LCMD0)) #define LIN_TX_HEADER ((0<<LCMD1)|(1<<LCMD0)) #define LIN_RX_RESPONSE((1<<LCMD1)|(0<<LCMD0)) #define LIN_TX_RESPONSE((1<<LCMD1)|(1<<LCMD0)) // LIN interrupt flags #define LIN_ERROR #define LIN_IDOK #define LIN_RXOK #define LIN_TXOK (1<<LERR ) (1<<LIDOK) (1<<LRXOK) (1<<LTXOK) // LIN ID masks #define LIN_1X_ID_MASK ((1<<LID3)|(1<<LID2)|(1<<LID1)|(1<<LID0)) #define LIN_1X_LEN_MASK((1<<LID5)|(1<<LID4)) #define LIN_2X_ID_MASK ((1<<LID5)|(1<<LID4)| LIN_1X_ID_MASK ) // Specific to ATmega32/64/M1/C1 #define LIN_PORT_IN PIND #define LIN_PORT_DIR DDRD #define LIN_PORT_OUT PORTD #define LIN_INPUT_PIN 4 #define LIN_OUTPUT_PIN 3 // Specific to ATtiny167 #define LIN_PORT_IN #define LIN_PORT_DIR #define LIN_PORT_OUT #define LIN_INPUT_PIN #define LIN_OUTPUT_PIN PINA DDRA PORTA 0 1 This method of definition tolerates changes of register address and allocation of register-bit. 4. C-macros C-macros are commonly used in C to define small snippets of code. During the preprocessing phase, each C-macro call is replaced, in-line, by the corresponding C-macro definition. If the Cmacro has parameters, they are substituted into the C-macro body during expansion; thus, a Cmacro can mimic a C-function. The usual reason for doing this is to avoid the overhead of a function call in simple cases, where the code is lightweight enough that function call overhead has a significant impact on performance. 4.1 C-macros for LIN 1.x // Use LIN 1.x protocol #define Lin_1x_set_type() #define Lin_1x_enable() #define Lin_1x_set_id(id) ( LINCR|= LIN_1X ) ( LINCR = LIN_1X|(1<<LENA)|(0<<LCMD2) ) { LINIDR &= ~LIN_1X_ID_MASK; \ LINIDR |= id & LIN_1X_ID_MASK;} #define Lin_1x_set_len(len) { LINIDR &= ~LIN_1X_LEN_MASK; \ LINIDR |= (((len+4)<<2) & LIN_1X_LEN_MASK;} #define Lin_get_len() ( LINDLR & (0x0F << LRXDL0) ) // Lin_set_rx_len(len) - Automatic setting in LIN 1.x for response // Lin_set_tx_len(len) - Automatic setting in LIN 1.x for response 4.2 C-macros for LIN 2.x // Use LIN 2.x protocol #define Lin_2x_set_type() #define Lin_2x_enable() #define Lin_2x_set_id(id) ( LINCR|= LIN_2X ) ( LINCR = LIN_2X|(1<<LENA)|(0<<LCMD2) ) { LINIDR &= ~LIN_2X_ID_MASK; \ LINIDR |= id & LIN_2X_ID_MASK;} // Lin_2x_set_len(len) - Not available in LIN 2.1 // Lin_get_len() ------- Not available in LIN 2.1 #define Lin_set_rx_len(len) ( LINDLR = len & (0x0F << LRXDL0) ) #define Lin_set_tx_len(len) ( LINDLR = len << LTXDL0 ) 4 AVR286 8123A–AVR–03/08 AVR286 4.3 Shared C-macros // Use of any protocol #define Lin_set_type(ltype)(( ltype == LIN_2X ) ? \ Lin_2x_set_type(): Lin_1x_set_type() ) #define Lin_get_type() ( LINCR & (1<<LIN1X) ) #define Lin_set_len(len) ( LINDLR = (len<<LTXDL0)|(len &(0x0F<<LRXDL0) ) // Miscellaneous C-macros #define Lin_get_error_status()( LINERR ) #define Lin_set_baudrate(br) \ { LINBRRH = (unsigned char)(((unsigned short)br)>>8);\ LINBRRL = (unsigned char) ((unsigned short)br); } #define Lin_sw_reset() ( LINCR = 1<<LSWRES ) #define Lin_full_reset() { Lin_sw_reset(); Lin_clear_enable_it(); \ LINBRRL = 0x00; LINBRRH = 0x00; } // Interrupt handling #define Lin_get_it() \ ( LINSIR & ((1<<LERR)|(1<<LIDOK)|(1<<LTXOK)|(1<<LRXOK)) ) #define Lin_set_enable_it() \ ( LINENIR = (1<<LENERR)|(1<<LENIDOK)|(1<<LENTXOK)|(1<<LENRXOK) ) #define Lin_clear_enable_it() \ ( LINENIR = (0<<LENERR)|(0<<LENIDOK)|(0<<LENTXOK)|(0<<LENRXOK) ) #define Lin_clear_err_it() ( LINSIR = 1<<LERR ) #define Lin_clear_idok_it() ( LINSIR = 1<<LIDOK ) #define Lin_clear_txok_it() ( LINSIR = 1<<LTXOK ) #define Lin_clear_rxok_it() ( LINSIR = 1<<LRXOK ) // LIN commands #define Lin_abort() (LINCR &= ~LIN_CMD_MASK) #define Lin_rx_header()(LINCR &= ~LIN_CMD_MASK) #define Lin_tx_header(){LINCR &= ~LIN_CMD_MASK; LINCR|= LIN_TX_HEADER ;} #define Lin_rx_response(){LINCR &= ~LIN_CMD_MASK; LINCR|= LIN_RX_RESPONSE;} #define Lin_tx_response(){LINCR &= ~LIN_CMD_MASK; LINCR|= LIN_TX_RESPONSE;} // LIN checking #define Is_lin_header_ready() ( (LINSIR & (1<<LIDOK)) ? 1 : 0 ) #define Is_lin_rx_response_ready()( (LINSIR & (1<<LRXOK)) ? 1 : 0 ) #define Is_lin_tx_response_ready()( (LINSIR & (1<<LTXOK)) ? 1 : 0 ) // ID & #define #define #define #define 4.4 data handling Lin_get_id() Lin_clear_index() Lin_get_data() Lin_set_data(data) ( ( ( ( LINIDR LINSEL LINDAT LINDAT & LIN_2X_ID_MASK) = (0<<LAINC)|(0x00<<LINDX0) ) ) = data ) Remark on C-macro If a long C-macro (several lines of C-code) is too often used, it penalizes the size of generated code (each C-macro call is replaced, in-line, by the corresponding C-macro definition). Then, it will be appropriate to encapsulate it in a C-function. 5 8123A–AVR–03/08 5. C-functions 5.1 LIN/UART Controller Initialization This function initializes the LIN controller and, if needed, the LIN interrupts. Two arguments are passed to the function: 1. ‘unsigned char l_type’: By construction, ‘l_type’ is either LIN_1X or LIN_2X 2. ‘unsigned long b_rate’: LIN Baud Rate Register (LINBRR) value The function returns: ‘unsigned char’: Warning: 5.1.1 == 0 : Initialization failed, LIN type is not in accordance != 0 : Initialization performed none Prototype extern unsigned char lin_init (unsigned char l_type, unsigned long b_rate); 5.1.2 Function unsigned char lin_init (unsigned char l_type, unsigned long b_rate) { // Pull-up on TxLIN & RxLIN (one by one to use bit-addressing) LIN_PORT_DIR &= ~(1<<LIN_INPUT_PIN ); LIN_PORT_DIR &= ~(1<<LIN_OUTPUT_PIN); LIN_PORT_OUT |= (1<<LIN_INPUT_PIN ); LIN_PORT_OUT |= (1<<LIN_OUTPUT_PIN); Lin_full_reset(); Lin_set_baudrate(b_rate); if (l_type == LIN_1X) { Lin_1x_enable(); } else if (l_type == LIN_2X) { Lin_2x_enable(); } else { return 0; } // If LIN is interrupt driven, enable the 2 following lines // Lin_set_enable_it(); // asm ("sei"); return 1; } 5.1.3 Notes & Remarks The LIN/UART Controller allows user customization of the protocol. • It is possible to remove the detection of the CHECKSUM field in received frames, the transmission of the CHECKSUM field or the frame time-out. • If these features are insufficient, the LIN/UART Controller can also be initialized as a pure UART to allow maximum user freedom. It is then possible to keep the automatic features to manage the header and switch to “manual” mode (UART mode) to manage the response. • A system mixing reception and transmission in a same response is also possible. 6 AVR286 8123A–AVR–03/08 AVR286 5.2 LIN Header Transmission This function commands the sending of the LIN header, MASTER task of MASTER node. Three arguments are passed to the function: 1. ‘unsigned char l_type’: By construction, ‘l_type’ is either ‘LIN_1X’ or ‘LIN_2X’ 2. ‘unsigned char l_id’: LIN identifier value. In case of ‘LIN_1X’, the coded length is transported into the LIN identifier. 3. ‘unsigned char l_len’: True length (not coded), number of data bytes transported in the response. This information is not used in ‘LIN_1X’ because it is coded in ‘l_id’. This function returns: ‘unsigned char’: Warning: 5.2.1 == 0 : Initialization failed, LIN type is not in accordance != 0 : Header transmission of the header on-going none Prototype extern unsigned char lin_tx_header \ (unsigned char l_type, unsigned char l_id, unsigned char l_len); 5.2.2 Function unsigned char lin_tx_header \ (unsigned char l_type, unsigned char l_id, unsigned char l_len) { Lin_abort(); // // // // // Useful if controller is still in 'lin_tx_response' or in 'lin_rx_response' mode. Note that when these modes are running, writing in LIN registers is disabled and the ID cannot be set in the controller. (c.f. “Break-in-Data” behavior”) if (l_type == LIN_1X) { Lin_1x_set_id(l_id); Lin_1x_set_len(l_len); } else if (l_type == LIN_2X) { Lin_2x_set_id(l_id); // No length transported in LIN 2.X } else { return 0; } Lin_tx_header(); return 1; // Set command } 5.2.3 Notes & Remarks It will be essential to verify that there will be no error during the transmission. 7 8123A–AVR–03/08 5.3 LIN Response Reception This function commands the reception of a LIN response, SLAVE task of MASTER or SLAVE node. Two arguments are passed to the function: 1. ‘unsigned char l_type’: By construction, ‘l_type’ is either ‘LIN_1X’ or ‘LIN_2X’ 2. ‘unsigned char l_len’: True length (not coded), number of data bytes expected in the response. This information is not used in ‘LIN_1X’ because it is coded in ‘l_id’. This function returns: ‘unsigned char’: == 0 : Initialization failed, LIN type is not in accordance != 0 : Response reception on-going Warning: At the end of the reception, the LIN/UART Controller data buffer will need to be emptied (read) (c.f. ‘Lin_get_response()’ function) 5.3.1 Prototype extern unsigned char lin_rx_response \ (unsigned char l_type, unsigned char l_len); 5.3.2 Function unsigned char lin_rx_response \ (unsigned char l_type, unsigned char l_len) { if (l_type == LIN_1X) { Lin_1x_set_type(); } else if (l_type == LIN_2X) { Lin_2x_set_type(); Lin_set_rx_len(l_len); } else { return 0; } Lin_rx_response(); return 1; // Change is necessary // Change is necessary // Set command } 5.3.3 Notes & Remarks It will be essential to verify that there will be no error during the reception. 8 AVR286 8123A–AVR–03/08 AVR286 5.4 LIN Response Transmission This function commands the sending of a LIN response, SLAVE task of MASTER or SLAVE node. Three arguments are passed to the function: 1. ‘unsigned char l_type’: By construction, ‘l_type’ is either ‘LIN_1X’ or ‘LIN_2X’ 2. ‘unsigned char *l_data’: Internal SRAM array pointer. This array contains the data bytes to transmit. 3. ‘unsigned char l_len’: True length (not coded), number of data bytes transported in the response. This information is not used in ‘LIN_1X’ because it is coded in ‘l_id’. This function returns: ‘unsigned char’: Warning: 5.4.1 == 0 : Initialization failed, LIN type is not in accordance != 0 : Response transmission on-going none Prototype extern unsigned char lin_tx_response \ (unsigned char l_type, unsigned char *l_data, unsigned char l_len); 5.4.2 Function unsigned char lin_tx_response \ (unsigned char l_type, unsigned char *l_data, unsigned char l_len) { unsigned char i; if (l_type == LIN_1X) { Lin_1x_set_type(); } else if (l_type == LIN_2X) { Lin_2x_set_type(); Lin_set_tx_len(l_len); } else { return 0; } // Change is necessary // Change is necessary Lin_clear_index(); for (i = 0; i < l_len; i++) { Lin_set_data(*l_data++); } // Data processing Lin_tx_response(); return 1; // Set command } 5.4.3 Notes & Remarks It will be essential to verify that there will be no error during the transmission. 9 8123A–AVR–03/08 5.5 Read Data Received This function reads (empties) the reception data buffer when a LIN response had been received. This function is additional of the ‘lin_rx_response()’ function. Only one argument is passed to the function: 1. ‘unsigned char *l_data’: Internal SRAM array pointer. This array will contain the data bytes received. This function returns nothing. Warning: 5.5.1 none Prototype extern void lin_get_response (unsigned char *l_data); 5.5.2 Function void lin_get_response (unsigned char *l_data) { unsigned char i, l_len; l_len = Lin_get_len(); Lin_clear_index(); for (i = 0; i < l_len; i++) { (*l_data++) = Lin_get_data(); } } 5.5.3 Notes & Remarks (none) 10 AVR286 8123A–AVR–03/08 AVR286 6. Firmware Packaging Two files are delivered. 6.1 ‘lin_drv.h’ File “lin_drv.h” file includes the “config.h” file where are defined the main clock frequency, the LIN baudrate used and the device-specific I/O header file. In “lin_drv.h” file, we will find: 1. Definitions: – ‘Bit Time’ definitions, – ‘Configuration’ definitions. 2. C-macros: – ‘LIN 1.x’ C-macros, – ‘LIN 2.x’ C-macros, – Shared C-macros. 3. Prototypes of functions: – ‘lin_init()’ – ‘lin_tx_header()’, – ‘lin_rx_response()’, – ‘lin_tx_response()’, – ‘lin_get_response()’. 6.2 ‘lin_drv.c’ File “lin_drv.c” file includes the “lin_drv.h” file. In “lin_drv.c” file, we will only find the C-functions: 1. ‘lin_init()’ 2. ‘lin_tx_header()’, 3. ‘lin_rx_response()’, 4. ‘lin_tx_response()’, 5. ‘lin_get_response()’. 11 8123A–AVR–03/08 7. LIN Communication Management This paragraph shows a LIN communication management based on a polling method. Management based on interrupts needs to build its own LIN message object descriptor. But many items discussed below can be reused if interrupt method is preferred. 7.1 Using LIN Status & Interrupt Register To manage LIN communication, only reading LIN Status & Interrupt Register (LINSIR) is necessay. Note that this register houses all the interrupt sources. Look at this register: 7 6 5 4 3 2 1 0 LIDST2 LIDST1 LIDST0 LBUSY LERR LIDOK LTXOK LRXOK LINSIR There can be only four events: 1. ID transmission completed / ID received: When the ID flag rises (LIDOK), this means two things (both are important): – The LIN header phase has been performed, the header transmission was completed (MASTER node) or an header was received (SLAVE node), – The LIN response phase must take place as soon as possible if the node is concerned with the received LIN ID. 2. Response reception completed: When the Rx flag rises (LRXOK), this means that the LIN response reception is now completed and there was no detected error. 3. Response transmission completed: When the Tx flag rises (LTXOK), this means that the LIN response transmission is now completed and there was no detected error. 4. Error: If the error flag rises (LERR), this root cause is contained in the LIN Error Register (LINERR). It is important to note when an error rises. The meaning of the error often depends on it. 7.2 LIN Slave Example //----------------------------------------------------------------------// T I T L E : lin_slave_example.c //----------------------------------------------------------------------//----- I N C L U D E S #include "config.h" #include "lin_drv.h" //----- D E C L A R A T I O N S #define LIN_ID_0 0x12 #define LIN_ID_1 0x13 #define #define LEN_0 LEN_1 1 2 unsigned char lin_motor_contol[LEN_0]; unsigned char lin_node_status[LEN_1]; volatile unsigned char lin_slave_err_nb = 0; //----- F U N C T I O N S //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_id_task function of "lin_slave_example.c" 12 AVR286 8123A–AVR–03/08 AVR286 // // The LIN ID received must be checked and compared to // the ones that the node must manage. // void lin_id_task (void) { switch (Lin_get_id()) { case LIN_ID_0: lin_rx_response(LIN_2X, LEN_0); break; case LIN_ID_1: lin_tx_response(LIN_2X, lin_node_status, LEN_1); break; default: // ID: absent/refused break; } } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_rx_task function of "lin_slave_example.c" // // - Save response data // - Update application signals. // void lin_rx_task (void) { unsigned char l_temp; lin_get_response (lin_motor_contol); // Update of the application signals l_temp = lin_motor_contol[0] & 0x03; if((l_temp == 0x01) || (l_temp == 0x03)) { PORTB |= 1<<PORT0; } else { PORTB &= ~(1<<PORT0); if(l_temp == 0x02) { PORTB |= 1<<PORT1; } else { PORTB &= ~(1<<PORT1); } } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_tx_task function of "lin_slave_example.c" // // - Update buffer array with application signals // to be ready for the next transmission // void lin_tx_task (void) { // Update of the application signals lin_node_status[0] = PINB & 0x03; lin_node_status[1] = lin_slave_err_nb; } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_err_task function of "lin_slave_example.c" 13 8123A–AVR–03/08 // // - If LIN error, increment the node error number // void lin_err_task (void) { // Global variable update lin_slave_err_nb++; } //. . . M A I N ( ) . . . . . . . . . . . . . . . . // // main function of "lin_slave_example.c" // // In a 'no end' loop, do: // - if LIN_ID_0 (Rx response 1 Byte ) // . PORT-B.0 = DC motor command -> clockwise // . PORT-B.1 = DC motor command -> counterclockwise // - if LIN_ID_1 (Tx response 2 bytes) // . Byte[0] = motor status // . Byte[1] = node error number // int main (void) { // Port B setting for motor control DDRB |= 1<<PORTB0; DDRB |= 1<<PORTB1; PORTB &= ~(1<<PORTB0); PORTB &= ~(1<<PORTB1); // LIN Initialization lin_init((unsigned char)LIN_2X, ((unsigned short)CONF_LINBRR)); // No End Loop while (1) { switch (Lin_get_it()) { case LIN_IDOK: lin_id_task(); Lin_clear_idok_it(); break; case LIN_RXOK: lin_rx_task(); Lin_clear_rxok_it(); break; case LIN_TXOK: lin_tx_task(); Lin_clear_txok_it(); break; case LIN_ERROR: lin_err_task(); Lin_clear_err_it(); break; default: break; } } return 0; } Program size (compiler option: -Os): Code = 816 Flash bytes, Data = 4 RAM bytes. 14 AVR286 8123A–AVR–03/08 AVR286 7.3 LIN Master Example A real time task must be added to send the headers. For instance the Timer0 will be used to send them. Else the program looks like the LIN slave example. //----------------------------------------------------------------------// T I T L E : lin_master_example.c //----------------------------------------------------------------------//----- I N C L U D E S #include "config.h" #include "lin_drv.h" #include <avr/interrupt.h> // Use AVR-GCC library //----- D E C L A R A T I O N S #define LIN_ID_0 0x12 #define LIN_ID_1 0x13 #define #define LEN_0 LEN_1 1 2 #define MAGIC_NUMBER 77 unsigned char lin_motor_command[LEN_0]; unsigned char lin_slave_node_status[LEN_1]; volatile unsigned char lin_master_err_nb = 0; volatile unsigned char rtc_tics = 0; //----- F U N C T I O N S //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_id_task function of "lin_master_example.c" // // The LIN ID received must be checked and compared to // the ones that the node must manage. // void lin_id_task (void) { switch (Lin_get_id()) { case LIN_ID_0: lin_tx_response(LIN_2X, lin_motor_command, LEN_0); break; case LIN_ID_1: lin_rx_response(LIN_2X, LEN_1); break; default: // ID: absent/refused break; } } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_rx_task function of "lin_master_example.c" // // - Save response data // - Update application signals. // void lin_rx_task (void) { 15 8123A–AVR–03/08 lin_get_response (lin_slave_node_status); // If command OK and no error on slave node // - PORTB[6] = 0 // - else PORTB[6] = 1 if (lin_slave_node_status[0] != lin_motor_command[0]) { PORTB |= 1<<PORT6; } else if (lin_slave_node_status[1] != 0){ PORTB |= 1<<PORT6; } else { PORTB &= ~(1<<PORT6); } } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_tx_task function of "lin_master_example.c" // // - Update buffer array with application signals // to be ready for the next transmission // void lin_tx_task (void) { // Update of the application signals (active-low switches) lin_motor_command[0] = (~PINB) & 0x03; } //. . . . . . . . . . . . . . . . . . . . . . . . . // // lin_err_task function of "lin_master_example.c" // // - If LIN error, increment the node error number // void lin_err_task (void) { // Global variable update lin_master_err_nb++; } //. . . . . . . . . . . . . . . . . . . . . . . . . // // OCR0A interrupt service routine of "lin_master_example.c" // // AVR-GCC declaration (default): // ISR(TIMER0_COMPA_vect) // IAR declaration: // #pragma vector TIMER0_COMPA_vect // __interrupt void timer0_compa_vect(void) // // The appropriate LIN header is sent following the rtc_tic value // ISR(TIMER0_COMPA_vect) { rtc_tics++; if((rtc_tics & 0x01) == 0x01) { lin_tx_header((unsigned char)LIN_2X, (unsigned char)LIN_ID_0, 0); } else { lin_tx_header((unsigned char)LIN_2X, (unsigned char)LIN_ID_1, 0); } } 16 AVR286 8123A–AVR–03/08 AVR286 //. . . M A I N ( ) . . . . . . . . . . . . . . . . // // main function of "lin_master_example.c" // // In a 'no end' loop, do: // - if LIN_ID_0 (Tx response 1 Byte) // . PORT-B.0 = DC motor command -> clockwise // . PORT-B.1 = DC motor command -> counterclockwise // - if LIN_ID_1 (Rx response 2 bytes) // . Byte[0] = motor status // . Byte[1] = slave node error number // Timer0 ensures that each header is sent in 20ms intervals. // int main (void) { // Port B setting for motor control // PORTB0 & PORTB1 switches active low with internal pull-up DDRB &= ~(1<<PORTB0); DDRB &= ~(1<<PORTB1); PORTB |= 1<<PORTB0; PORTB |= 1<<PORTB1; // Port B setting for motor status flag DDRB |= 1<<PORTB6; PORTB &= ~(1<<PORTB6); // LIN initialization lin_init((unsigned char)LIN_2X, ((unsigned short)CONF_LINBRR)); // Timer0 & INT initialization (no output) // Timer0 Reset TIMSK0 = 0; TCCR0B = 0; TCCR0A = 0; TCNT0 = 0; OCR0A = 0; TIFR0 = ((1<<OCF0A) | (1<<TOV0)); // No output, mode 2, 10 ms interrupt TCCR0A = 0x02; OCR0A = MAGIC_NUMBER; TCCR0B = 0x05; // Timer0 compare A interrupt enable TIMSK0 |= 1<<OCIE0A; asm ("sei"); // No End Loop while (1) { switch (Lin_get_it()) { case LIN_IDOK: lin_id_task(); Lin_clear_idok_it(); break; case LIN_RXOK: lin_rx_task(); Lin_clear_rxok_it(); break; case LIN_TXOK: lin_tx_task(); Lin_clear_txok_it(); break; case LIN_ERROR: lin_err_task(); Lin_clear_err_it(); break; default: break; } } return 0; } Program size (compiler option: -Os): Code = 946 Flash bytes, Data = 5 RAM bytes. 17 8123A–AVR–03/08 7.4 Configuration Header File //----------------------------------------------------------------------// config.h for "lin_slave_example.c" or "lin_master_example.c" //----------------------------------------------------------------------//----- I N C L U D E S #include <avr/io.h> //----// #define #define 18 // Use AVR-GCC library D E C L A R A T I O N S Use an external crystal oscillator for "lin_master_example.c" FOSC 8000 // In KHz LIN_BAUDRATE 19200 // In bit/s AVR286 8123A–AVR–03/08 Headquarters International Atmel Corporation 2325 Orchard Parkway San Jose, CA 95131 USA Tel: 1(408) 441-0311 Fax: 1(408) 487-2600 Atmel Asia Room 1219 Chinachem Golden Plaza 77 Mody Road Tsimshatsui East Kowloon Hong Kong Tel: (852) 2721-9778 Fax: (852) 2722-1369 Atmel Europe Le Krebs 8, Rue Jean-Pierre Timbaud BP 309 78054 Saint-Quentin-enYvelines Cedex France Tel: (33) 1-30-60-70-00 Fax: (33) 1-30-60-71-11 Atmel Japan 9F, Tonetsu Shinkawa Bldg. 1-24-8 Shinkawa Chuo-ku, Tokyo 104-0033 Japan Tel: (81) 3-3523-3551 Fax: (81) 3-3523-7581 Technical Support [email protected] Sales Contact www.atmel.com/contacts Product Contact Web Site www.atmel.com Literature Requests www.atmel.com/literature Disclaimer: The information in this document is provided in connection with Atmel products. No license, express or implied, by estoppel or otherwise, to any intellectual property right is granted by this document or in connection with the sale of Atmel products. EXCEPT AS SET FORTH IN ATMEL’S TERMS AND CONDITIONS OF SALE LOCATED ON ATMEL’S WEB SITE, ATMEL ASSUMES NO LIABILITY WHATSOEVER AND DISCLAIMS ANY EXPRESS, IMPLIED OR STATUTORY WARRANTY RELATING TO ITS PRODUCTS INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE, SPECIAL OR INCIDENTAL DAMAGES (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, OR LOSS OF INFORMATION) ARISING OUT OF THE USE OR INABILITY TO USE THIS DOCUMENT, EVEN IF ATMEL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Atmel makes no representations or warranties with respect to the accuracy or completeness of the contents of this document and reserves the right to make changes to specifications and product descriptions at any time without notice. Atmel does not make any commitment to update the information contained herein. Unless specifically provided otherwise, Atmel products are not suitable for, and shall not be used in, automotive applications. Atmel’s products are not intended, authorized, or warranted for use as components in applications intended to support or sustain life. © 2008 Atmel Corporation. All rights reserved. Atmel ®, logo and combinations thereof, AVR ® and others are registered trademarks or trademarks of Atmel Corporation or its subsidiaries. Other terms and product names may be trademarks of others. 8123A–AVR–03/08