Sunteți pe pagina 1din 37

Introduction:

we have built an intelligent, wearable pedometer. This wireless pedometer can calculate many useful statistics such as the number of steps a user has taken, the distance and the speed the person has walked/run, as well as the number of calories the person has burned. This information is wirelessly transmitted to the base-station, and it is displayed on the PC in a useful manner. This project is in collaboration with the Ambumedics Project with John Belina. Traditionally, pedometers only measure the number of steps a user has taken. Recently, more advanced pedometers include GPS units to calculate the distance a person has travelled, but using GPS substantially increase the cost of the product. Our project uses a low-cost accelerometer to determine the distance, speed, number of steps taken, and calories burned of a user instead. High Level Design

The project will consist of two main components, the wearable sensor and the base-station. The wearable sensor includes an accelerometer, an Atmel Mega 644V microcontroller and an RF transceiver. The base-station will consist of an Atmel Mega 644V, RF transceiver, and MAX233 chip for RS-232 serial communication. The wearable sensor will take the raw data produced by the accelerometer and make determinations on the speed, distance, and number of steps the user has taken. The wearable component will send its interpreted data wirelessly to the base station for aggregating and displaying pertinent data to the user. The following is a highlevel block diagram of the system we built.

Figure 1 - High-level block diagram of the wireless pedometer system

The wearable module is powered from a 3V battery source with two AA batteries in series. The device is tied around the user's leg, with the accelerometer strapped on the top of the user's shoe. The calculations on step detection, distance, and speed are done on the wearable sensor, and the result is transmitted wirelessly to the base-station. The result is displayed on the PC through RS232 serial communication with MAX233. The following image shows the actual hardware used.

Figure 2: Image of the actual pedometer system Although the wearable sensor seems bulky and impractical to wear, keep in mind that this is only a prototype. If all components are populated on a single PCB using small packages (DFN or BGA), the size can be significantly reduced. Unfortunately we do not have the budget to fabricate a PCB that costs thousands of dollars for a prototype spin :). For now, the most important aspect is to make the system work with the available resources given to us.

Hardware Design:
The two major hardware components in this project are the wearable sensor module and the basestation, which are detailed below. A previous 476 project (Movement of Music) is used as a helpful reference for the hardware design. Wearable Sensor The wearable sensor includes the accelerometer, AT Mega644V MCU and the RZ502 wireless module, as illustrated in the block diagram in Figure 1. The following image shows the wearable sensor and its various components.

Figure 3: Wearable sensor hardware

Tri-Axis Accelerometer The accelerometer we chose is KXP74 from Kionix. It is a 2g tri-axis digital accelerometer. This is an ideal part for our application for the following reasons:

2g tri-axis, allows an output range for 19.6m/s^2 acceleration, suitable for a pedometer application operating supply voltage (Vdd) from 2.7V to 5.25V, which will work well on a 3V battery supply easy-to-use SPI interface with internal ADC, which can communicate directly with ATMega644V company based in Ithaca and is supportive of student projects at Cornell University, thank you Kionix for the free samples and support!

The accelerometer comes in a 5x5x1.2 mm DFN package only, which can make hand soldering fairly stressful. Luckily, Kionix provides free evaluation board (EVAL-74) with a standard 10-pin connector. The following shows the schematic of the evaluation board, and its connection to the AT Mega644V board.

Wireless Transceiver We have based our wireless design on Andrew Godbeheres and Nathan Wards previous 476 project, which uses Atmels 2.4GHz radio transceiver AT86RF230. This wireless chip has several advantages:

a high baudrate of 250kbps low-powered, runs on 3V support SPI interface 2.4GHz is unlicensed, which does not violate FCC regulations in a tiny 44-pad, 7x7x1mm QFN package

However, we wanted to avoid the troubles of hand soldering 32-QFN package. Luckily, the AT86RF230 chip also comes in an Atmel evaluation board (RZ502). Since this is a conjoint project with John Belinas Amubumedics Project, we are able to borrow two extra RZ502 boards that Amubumedics Group previously owned. The following is the schematic for the RZ502 wireless transceiver board from Atmels application note.

http://courses.cit.cornell.edu/ee476/FinalProjects/s2008/hc454_pl328/hc454_pl328/antenna Schematic.jpg

ATMega644V MCU board The ATMega644V board is populated using Bruces Prototype Board for Atmel Mega32. We have used ATMega644V instead of ATMega32 because:

ATMega644V offers 64K Bytes of in-system programmable flash memory, which is important for our application since we are doing the computation on the wearable sensor It is low-powered and can run on 3V, which is not possible on the Mega32 It is pin-to-pin compatible with Mega32, so it will still work with Bruces Prototype Board

The following is the schematic of the ATMega644V MCU Prototype board.

Figure 6 - Schematic for ATMega644V prototype board on the wearable sensor

The accelerometer and the wireless transceiver is connected to the MCU through Port B using SPI for data transmission. Port C is used as GPIOs to command the transceiver. Please note that the schematic only shows the MCU connections to the accelerometer and the transceiver, external components for the MCU that are on Bruces prototype board are not shown. Please refer to Bruces Prototype Board webpage for that information. We are using a 8MHz crystal on the Mega644V, instead of a 16MHz crystal usually found on the Mega32. This is because Mega644V can only run on 8MHz if it is in low-power mode running at 3V. For our pedometer application, detecting step information for a fast runner will require a maximum sampling rate on the order of hundreds of ms. As a result, operating the MCU at 8MHz is more than sufficient, and it is possible to make the speed tradeoff for lower power consumption.

Base-Station Hardware The base-station is essentially the same as the wearable sensor, except that it does not require the connection of an accelerometer. The following image is the actual base-station hardware.

Figure 7 - Base-station hardware Originally, the SDK500 with Mega32 is intended to use as the base-station for its simplicity. Nonetheless, the wireless transceiver RZ502 which is connected to the MCU through SPI can only run on 3V. This proposes the problem of maintaining separate power supplies for the SDK and the wireless transceiver. Moreover, using different voltage levels requires additional level shifting circuitry between SPI communications. As a result, we decided to build the base-station using the same hardware as the wearable sensor for consistency. The schematic for the base-station is depicted below.

http://courses.cit.cornell.edu/ee476/FinalProjects/s2008/hc454_pl328/hc454_pl328/mcu_bas estation.jpg

Figure 8 - Schematic for ATMega644V prototype board for the base station Similarly to the wearable sensor, external components for the MCU that are on Bruces prototype board are not shown. Please refer to Bruces Prototype Board webpage for that information. The MAX233 chip is also populated on the prototype board for serial communication to the PC.
Software Design

Two Mega644Vs were used, each with its own code which was split into two separate CodeVisionAVR projects. One Mega644V, which was part of the wearable hardware, interpreted raw data from the Kionix

KXP74 three-axis accelerometer and used the Atmel AT86RF230 transceiver to send information such as the distance the user has traveled or the users speed. This software is built upon code from "Movement of Music" 476 project for the RF transceiver and the accelerometer, which were libraries in header files. The other Mega644V was part of the base station hardware, and it received information from the wearable hardware as well as performing calculations for the amount of calories burned. In order to carry out this calculation, it prompts the user for their weight from a terminal emulation programmed via a serial port. The base station also utilized the Atmel AT86RF230 transceiver header file. All code was written in the C programming language through the CodeVisionAVR IDE.

Wearable Hardwares Data Acquisition The Mega644V on the wearable hardware polls the Kionix KXP74 accelerometer for information every 16 ms. All communication between the two devices are carried out over a SPI interface with the Mega644V acting as the master and the KXP74 acting as one of the two slave devices, the other being the RF transceiver. Each axis must be polled separately with a minimum of 40 s in between polls to give enough time for each poll to complete. SPI initialization code written for a previous ECE 476 project by Andrew Godbehere and Nathan Ward was used upon start up to initialize the clock phase/polarity and to correctly set the operating modes of the accelerometer.

Step and Distance Calculation


As mentioned previously, the step detection and distance calculations occur on the wearable sensor so that dropped packets will not have effects on the information provided to the user. In designing the algorithm for step detection, a walking wave-form from the three-axis accelerometer was plotted and analyzed.

Figure 9: 3-axis acceleration profile when walking It was noted that each distinct step was always characterized by two accelerations on the X-axis along with one steep deceleration in the Z-direction. This was used in coming up with the finite state machine depicted below.

Figure 10: State machine for step detection In addition to the detection of the steps, a distance calculation was also a part of the pedometer. To properly calculate the distance traveled, one needs to take into account the X and Z accelerations because the foot (and sensor) does not remain parallel to the ground during the entire step. To complicate matters, the faster one walks, the more different the foot positions are. This method of calculation is extremely complex which was not desirable for our simple 8-bit MCU controlled real-time embedded system, and so it was decided that a much simpler approach would be used. Our method consisted of simply using the X-directions acceleration readings to determine the distance traveled, as well as scaling that reading up whenever it was determined that the user was running instead of walking. The calculation for distance from a known velocity and time is integrating the velocity and evaluating the time interval in question, or in other words taking an integral. To approximate a good parabolic equation's integral, Simpsons's rule was used on every set of three samples, with the third sample of one data set being the first sample of the next data set. The method of differentiating walking versus running was by using a counter in between steps and depending on the count value, it could be determined how fast the user was moving. The exact values of the X-axis readings for 1 meter was calibrated by many trials of walking at a slow pace. In order to take into account physiological differences in users and the orientation of how the sensor sat on the users foot, a simple adaptive calibration was done upon start up where the initial default X value was recorded and all step detections as well as distance calculations were done when outside of that default position.

Simpsons Rule By finding the area under the curve of the accelerometer data in Figure 9, we can determine the distance the user has travelled. In order to perform this calculation accurately, Simpsons Rule is used to improve our numerical approximation. Simpsons Rule states that:

where f(x) is a signal such as: In our case, f(a) is the initial value of the axis acceleration, f((a+b)/2) is the middle value, and f(b) is the end value As a result, we need to take 3 samples of the acceleration information in order to perform such integration. The time interval between each sample is 16ms, hence our numerical integration becomes:

Wireless Link The AT86RF230 transceiver is a slave SPI device on both Mega644V chips. The useful library exists in the aforementioned header file (at86rf230.h) that takes care of register initializations and transmit/receive functionality. A general SPI initialize function exists for the radio board, along with a clock setting API. There are separate functions to initialize the radio for transmit and receive, and a simple transmit-before-receive technique is used to ensure synchronization among the transmitter and receiver. This technique consists of the receiver module changing its settings as a transmitter, sending a data request message, and then turning itself back as a receiver and waiting 10ms for a response. If no response is received within 10ms, it will try the process again in the next running of the task. On the transmitter end, if the task has information to send, it will first change its settings to become a receiver and look for the data request message and then turns itself back into a transmitter. Only if it receives this message, does the module actually transmit RF data. In addition to the SPI, there are some control lines that control the radios reset, transmit, and interrupt requests that indicate successful transmissions to the microcontroller. These are hardwired to PORTC.0-3 on each of the Mega644Vs. While the communication protocol employed in the project is not 100% 802.15.4 compliant, it is a simpler subset, and with additional changes can become fully compliant with the 802.15.4 standard. Aside from the

transmit-before-receive scheme, no other error checking is in place. As a result, if this project is running where other 2.4GHz devices are operating, it may receive garbage data and try to interpret them in the context of our project. However, we have not noticed significant performance degrade/malfunction when we tested our project in the 476 digital lab where Wi-Fi is present. Details of the radio/Mega644V interface such as required delay times between radio state transitions are included in the AT86RF230 datasheet.

Results
The accuracy of the step detection as well as the distance calculations were within acceptable accuracy limits. The following table summarizes the experiments done for walking slowly, walking fast, and jogging. The overall accuracy for the number of steps and distance in each category are included in the table. Table 1: Percentage error for different walking speeds
Slow Walking (~2-3mph) Percentage Error in distance Percentage Error in step count 2% Fast Walking (~4mph) 2%

Jogging (~5mph)
4%

7%

10%

14%

The wireless communication drops a few frames, estimated to about 10% of all packets. This is the result of possible interference with other 2.4 GHz devices such as wi-fi. In addition, the communication protocol had no retry mechanism in place. Since our focus of the project is not on entirely based on wireless, we did not implement any detailed wireless stack. Information are transmitted and received with a simple handshaking acknowledgement. However, because the wearable sensor side is carrying out distance, step, and speed calculations, the missed packets do not affect the accuracy of the data shown to the user. In addition, the transmitter sends data so frequently and the user's movements are much slower than the 8 MHz CPU, the dropped frames are not visible to the user. The following table shows the accuracy of our wireless pedometer. Experiments are done on a 30-meter distance in the lab with 5 trials for each speed.

Conclusion
The pedometer performed well beyond our expectation. The dropped wireless packets did not affect the visible results to the user, who sees a continuous update of distance, steps, speed, and calories burned. The method of calculating distance by performing approximate intregals using Simpson's rule proved to be accurate within reason and was very consistent over countless trials. The same can be said for the step detecting state machine which is accurate within a ten steps over 60 meters. While encountering sporadic hardware problems throughout

testing, the final product is well protected and secured safely ensuring no hardware malfunctions. In addition to accuracy, our project is compliant with regulations for transmitting wireless signals. Future Works This project was done as a proof-of-concept, but it is conceivable that with some additional work, this could be turned into a commercial product. The Atmel AT86RF230 comes in a smaller form factor as does many of the other parts used. While the Mega644V was ideal for this prototype because of its lower operating power than the Mega32, a different microcontroller all together would probably be a better fit for a commercial product because additional robustness needs to be added. For instance, being a 2.4GHz product, the communication protocol needs error checking from other interfering sources such as wireless router and cordless telephones, i.e. some level of checksum/retry mechanism needs to be added. The other major area of work would be to have a more complex step/distance detection that is able to handle all ranges of walking and running at different speeds. This would involve accounting for the different angles involved in the walking/running. Other additional work! should be put into packaging the product. Standards These are the standards that may apply to our project:
1. CISPR 14-2 Ed. 1.1 b:2001: Electromagnetic compatibility - Requirements for household appliances, electric tools and similar apparatus - Part 2: Immunity - Product family standard 2. Electromagnetic Compatibility EMC (IEC) 3. Protection Against Electric Shock (IEC) 4. IEC 62209-1 Ed. 1.0 b:2005: Human exposure to radio frequency fields from hand-held and body-mounted wireless communication devices - Human models, instrumentation, and procedures - Part 1: Procedure to determine the specific absorption rate (SAR) for hand-held devices used in close proximity to the ear (frequency range of 300 MHz to 3 GHz)

Ethical Consideration Due to the wearable nature of this project, many ethical considerations were taken into account. First and foremost, the safety of the user and those around them are of greatest importance. To ensure protection for the user, layers of separation were used to isolate the user from both heat and electric current. However, if the product were to be commercialized, many more issues would have to be taken into consideration such as the packaging and the usability of the device, i.e. adjustable fit and non-interference with the users natural walking/running motions. As for dangers from emission, harmful emissions are dependent on the emission power, and 2.4GHz technology was designed to emit very little power. In comparison, a cellular telephone emits about two thousand times the power emission of a simple 2.4GHz device.

APPENDIX: Wearable sensor Code:


/***************************************************************************** * wearable.c * Lab 6: Final Project * ECE 476: Digital Systems Design Using Microcontrollers * Cornell University * April 2008 * Andrew Chin (hc454) and Ping-Hong Lu (pl328) ****************************************************************************/ /***************************************************************************** * This program will periodically take a sample from the accelerometers and * then listen for a short while for a command from the base station. * *****************************************************************************/ #include <mega644.h> #include <spi.h> #include <at86rf230.h> #include <kxp74.h> #include <stdio.h> #define SAMPLE_TIMEOUT 10 #define SIMPSON_THRESHOLD 11000 unsigned char sample_timer; unsigned char led; unsigned int led_toggle_count; unsigned unsigned unsigned unsigned unsigned unsigned char char char char char char x_step_began; z_step_began; x_step_done; z_step_done; step_count; step_lockout_timer;

unsigned char step_state; int accumulated_x; unsigned char meter_count; unsigned char integration_samples[3]; unsigned char three_count; int simpsons; int pace_counter1; int pace_counter2; long padding; unsigned char calibration_done; unsigned int calibration_count; unsigned int calibration_total; unsigned char default_x; unsigned char send_count; unsigned int timer_count; unsigned char speed; unsigned char calculated_speed; unsigned char every_other_step; unsigned char half_step;

//State Machine States #define NO_STEP 0 #define X_START 1 #define Z_START 2 #define X_DONE 3 #define Z_DONE 4 #define X_START_Z_DONE 5 #define Z_START_X_DONE 6 #define X_START_Z_START 7 #define STEP_DETECTED 8 /****************************************************************************/ // This interrupt runs when a compare-match occurs every 1 ms. It decrements // the sample_timer global variable which is then used to determine when the // "sample" task runs. interrupt [TIM0_COMPA] void ms_timer(void) if (sample_timer > 0) sample_timer--; timer_count++; } /****************************************************************************/ // This task runs every 15 ms and takes readings from the accelerometer and // makes interpretations of the user's walking/running movements. The // distance calculations, which utilize the Simpson's rule, occur after every // three samples, with the last sample becoming the first sample point of the // next set. A finite state machine determines discrete steps based on the x // and z accelerations going above and below certian thresholds. The distance, // steps, and speed information is sent to the basestation every time sample runs. void sample(void) { unsigned char x,y,z; set_sensor_clock(); //Set clock phase & polarity for accelerometers tx_frame_length = 0; // Take 313 cycles to calibrate the default x sensor reading if (calibration_done == 0 && calibration_count != 312) { x = get_sensor(1, CONVERT_XAXIS); calibration_total += x; calibration_count++; // After taking the final calibration sample, take the average and // set calibration flag as done if (calibration_count == 312) { default_x = calibration_total / 312; calibration_done = 1; PORTD.2 = 0; } return; } // Variable used to detect if data stack has overrun the alloted size if (padding != 0) printf("data corruption occurred\n\r"); //4/19 printf("calibrated x is: %d meter count is %d\n\r", default_x, meter_count); // printf("speed is %d timer count is: %d\r", speed, timer_count); { //Triggers once per ms

if (step_lockout_timer > 0) step_lockout_timer--; x = get_sensor(1, CONVERT_XAXIS); //printf("x value is %d\n\r", x); // Update the triple set for doing simpson's rule calculation of area // under the curve integration_samples[three_count] = x; // On the third sample, calculate the simpsons if (three_count == 2)//x > 74 || x < 70 { if ((integration_samples[0] > (default_x - 4) && integration_samples[0] < (default_x + 3))&& (integration_samples[1] > (default_x - 4) && integration_samples[1] < (default_x + 3))&& (integration_samples[2] > (default_x - 4) && integration_samples[2] < (default_x + 3))) { // standing still with slight movements, ignore } else { simpsons = (int)((int)integration_samples[0]-default_x + ((int)integration_samples[1]-default_x)* 4 + (int)integration_samples[2]-default_x)* 16 / 3; // If the user is fast walking or running, scale the x readings up and // set the step detection to every other detection if (pace_counter1 < 50 && pace_counter1 > 0) { simpsons = simpsons * 1.35; every_other_step = 1; } else { simpsons = simpsons * 0.9; every_other_step = 1; } // Don't let an underflow occur from too many negative x readings if ((accumulated_x + simpsons) > 0) { accumulated_x += simpsons; } // If the total x distance is enough to count as one step if (accumulated_x > SIMPSON_THRESHOLD) { accumulated_x = accumulated_x - SIMPSON_THRESHOLD; meter_count++; // save value calculated_speed = ((2237/timer_count) > 10 ? calculated_speed : ((char)(2237/timer_count))); // Increase speed incrementally to avoid unrealistic jumps from // inaccurate speed calculations if (calculated_speed > speed) speed++; else if (calculated_speed < speed) speed--; else ; timer_count = 0;

// Remove any erroneous x readings that cause the accumulated_x to go // 2 x the threshold while (accumulated_x > SIMPSON_THRESHOLD) accumulated_x -= SIMPSON_THRESHOLD; } } } // Send the meters, step count, and speed over RF { send_count = 0; tx_frame_length = 3; transmit_frame[0] = meter_count; transmit_frame[1] = step_count; transmit_frame[2] = speed; set_transceiver_clock(); if (RF_quick_listen() == 1) { RF_upload_frame(); if (receive_frame[0] == DATA_REQ) PORTD.7 = ~PORTD.7; RF_rx_to_tx(); RF_download_frame(); RF_transmit_frame(); RF_wait_for_transmit(); } } receive_frame[0] = 0; RF_tx_to_rx(); } send_count++; delay_us(1); y = get_sensor(1, CONVERT_YAXIS); delay_us(1); z = get_sensor(1, CONVERT_ZAXIS); delay_us(1); //printf("x y z %d %d %d\n\r", x, y, z); // State machine for step detection switch (step_state) { // Look for breaking either the x or z threshold case NO_STEP: if (step_lockout_timer != 0) { break; } if (x > 90) { step_state = X_START; break; } if (z < 75) { step_state = Z_START; break; } break;

// Look for finishing the x step or breaking the z threshold case X_START: if (x < 80) { step_state = X_DONE; break; } if (z < 75) { step_state = X_START_Z_START; break; } break; // Look for finishing the z step or breaking the x threshold case Z_START: if (z > 80) { step_state = Z_DONE; break; } if (x > 90) { step_state = X_START_Z_START; break; } break; // Look for finishing the x or z steps case X_START_Z_START: if (x < 80) { step_state = Z_START_X_DONE; break; } if (z > 80) { step_state = X_START_Z_DONE; break; } break; // Look for finishing the x step case X_START_Z_DONE: if (x < 80) { step_state = STEP_DETECTED; break; } break; // Look for finishing the z step case Z_START_X_DONE: if (z > 80) { step_state = STEP_DETECTED; break; } break; // Look for breaking either the z threshold case X_DONE: if (z < 75) { step_state = Z_START_X_DONE; break; } break;

// Look for breaking either the x threshold case Z_DONE: if (x > 90) { step_state = X_START_Z_DONE; break; } break; // Both axis done, increment whole or half step depending on speed. case STEP_DETECTED: if (every_other_step == 1) { if (half_step == 1) { step_count++; half_step = 0; } else half_step = 1; } else step_count++; step_state = NO_STEP; step_lockout_timer = 9; pace_counter1 = pace_counter2; pace_counter2 = 0; break; default: break; }

//

printf("pace counter = %d\n\r", pace_counter1);

pace_counter2++; three_count++; if (three_count == 3) { integration_samples[0] = integration_samples[2]; three_count = 1; } /* if (led_toggle_count++ == 8) { led_toggle_count = 0; if (led == 0) { led = 1; PORTD.2 = 0; } else { led = 0; PORTD.2 = 1; } } */ } /****************************************************************************/ // This function correctly initializes the necessary control registers and // I/O ports as well as initializes global variables to zero. Lastly, it

// enables all interrupts. void initialize(void) { DDRD.2 = 1; PORTD.2 = 1; DDRD = 0xFF; DDRA = 0xFF; PORTA = 0x00; PORTD.7 = 1; sample_timer = 0; // Initialize Mega64's SPI settings init_spi(); // Initialize accelerometer's SPI init_sensor_spi(); set_sensor_clock(); // Get accelerometers in proper mode init_sensors(); // Initialize transceiver SPI RF_init_spi(); delay_us(100); set_transceiver_clock(); // Put transceiver in receiver mode RF_init_transmitter(); sample_timer = SAMPLE_TIMEOUT; TCCR0A = 0b00000010; //OC0A & OC0B disconnected, WGM set up for CTC mode TCCR0B = 0b00000011; //Timer set up to osc/64... count to 125 for 1 ms OCR0A = 125; // Interrupt on compare match A TIMSK0 = 0b00000010; led = 0; led_toggle_count = 0; x_step_began = 0; z_step_began = 0; x_step_done = 0; z_step_done = 0; step_count = 0; step_lockout_timer = 0; integration_samples[0] = 0; integration_samples[1] = 0; integration_samples[2] = 0; three_count = 0; step_state = NO_STEP; accumulated_x = 0; meter_count = 0; calibration_done = 0; calibration_count = 0; calibration_total = 0; default_x = 0; padding = 0; pace_counter1 = 0; pace_counter2 = 0; send_count = 0; timer_count = 0; speed = 0;

calculated_speed = 0; every_other_step = 0; half_step = 0; //set up UART UCSR0B = 0x18 ; UBRR0L = 51 ; printf("starting...\n\r") ; //Enable interrupts #asm sei #endasm } /****************************************************************************/ // The main function starts by running a delay to allow the other components // time to reach a steady state. It then calls the initialization function // and runs whenever the interrupt has counted down the sample_timer variable // to zero. void main(void) { delay_ms(2000); initialize(); while(1) { if (sample_timer == 0) { sample_timer = SAMPLE_TIMEOUT; sample(); } } } /****************************************************************************/

Base-station code :
/***************************************************************************** * basestation.c * Lab 6: Final Project * ECE 476: Digital Systems Design Using Microcontrollers * Cornell University * April 2008 * Andrw Chin (hc454) and Ping-Hong Lu (pl328) ****************************************************************************/

/****************************************************************************/ #include <mega644.h> #include <at86rf230.h> #include <stdio.h> #define SAMPLE_TIMEOUT 15 unsigned char sample_timer; unsigned char led; unsigned int led_toggle_count;

unsigned char start_time_count; unsigned int timer_count; unsigned int cal_burned; unsigned char r_buffer[6]; //input string unsigned char old_meters; unsigned int speed_timer; unsigned char speed; float cal_const; /****************************************************************************/ // This interrupt runs when a compare-match occurs every 1 ms. It decrements // the sample_timer global variable which is then used to determine when the // "sample" task runs. interrupt [TIM0_COMPA] void ms_timer(void) if (sample_timer > 0) sample_timer--; timer_count++; PORTD.2 = ~PORTD.2; } /****************************************************************************/ // This task runs every 15 ms and requests data from the transmitter each // time sample runs. It receives the distance, steps, and speed information // from the wearable sensor and calculates the calories burned. // and z accelerations going above and below certian thresholds. The distance, // steps, and speed information is sent to the basestation every time sample runs. void sample(void) { unsigned char delay; // Turn the radio from receive to transmit mode RF_rx_to_tx(); tx_frame_length = 1; transmit_frame[0] = DATA_REQ; // Get data ready for tx RF_download_frame(); // Transmit RF_transmit_frame(); RF_wait_for_transmit(); // Turn the radio from transmit to receive mode RF_tx_to_rx(); //get ready to rx RF_clear_IRQ(); delay_us(100); delay = RF_receiver_listen_timeout(40); //listen for 10ms if (delay != 0) { //we have received something PORTD.7 = ~PORTD.7; RF_upload_frame(); {

// Calculations are based on the speed at which the user moves // and can change dynamically so set the mult factor if (receive_frame[2] < 3) cal_const = .00098; else if (receive_frame[2] < 5) cal_const = .0015; else if (receive_frame[2] < 7) cal_const = .00192; else cal_const = .00348; // calories burned = factor * weight * time cal_burned = (int)(cal_const * (float)r_buffer[0] * (float)receive_frame[0] / (float)receive_frame[2]);

if (cal_burned == -1) cal_burned = 0; printf("meters: %d steps: %d speed: %d cals burned: %d \r", receive_frame[0], receive_frame[1], receive_frame[2], cal_burned); if (old_meters != receive_frame[0]) { // speed = distance / time * 2.237 speed = (int)(((1 / timer_count)) * 2237); old_meters = receive_frame[0]; timer_count = 0; } } speed_timer++; // LED Toggling code to indicate Mega64 not hung /* led_toggle_count++; if (led_toggle_count == 8) { led_toggle_count = 0; if (led == 0) { led = 1; PORTD.2 = 0; } else { led = 0; PORTD.2 = 1; } } */ } /****************************************************************************/ // This function correctly initializes the necessary control registers and // I/O ports as well as initializes global variables to zero. Lastly, it // enables all interrupts. void initialize(void) { DDRD.2 = 1; PORTD.2 = 1; DDRD.7 = 1; DDRA = 0xFF; PORTA = 0; // Initialize Mega64's SPI settings init_spi(); // Initialize transceiver SPI RF_init_spi(); set_transceiver_clock(); // Put transceiver in receiver mode RF_init_receiver(); PORTD.7 = 0; sample_timer = SAMPLE_TIMEOUT; TCCR0A = 0b00000010; //OC0A & OC0B disconnected, WGM set up for CTC mode TCCR0B = 0b00000011; //Timer set up to osc/64... count to 125 for 1 ms OCR0A = 125; // Interrupt on compare match A TIMSK0 = 0b00000010;

led = 0; led_toggle_count = 0; start_time_count = 0; timer_count = 0; cal_burned = 0; old_meters = 0; speed_timer = 0; // Set up UART & print welcome message UCSR0B = 0x18 ; UBRR0L = 51 ; printf("\rWelcome to the ECE 476 pedometer! //Enable interrupts #asm sei #endasm }

\n\n\n\r") ;

/****************************************************************************/ // This function prints the welcome message to the screen via a serial // connection. It also prompts the user for their weight which will be used // in the calories burned calculation. void get_inputs(void) { printf("1) Please keep the wearable sensor off\n\r2) Please enter your weight: "); scanf("%d",r_buffer); printf("%d\n\r3) Please turn on sensor and wait for green light\n\n\n\r", r_buffer[0]); }

/****************************************************************************/ // The main function starts by calling the initialization function and // getting the user's weight. It then runs whenever the interrupt has counted // down the sample_timer variable to zero. void main(void) initialize(); get_inputs(); {

while(1) { if (sample_timer == 0) { sample_timer = SAMPLE_TIMEOUT; sample(); } } } /****************************************************************************/

Wireless header:
/***************************************************************************** * at86rf230.h * Lab 6: Final Project * ECE 476: Digital Systems Design Using Microcontrollers * Cornell University * April 2007 * Andrew Godbehere (abg34) and Nathan Ward (njw23) ****************************************************************************/

/****************************************************************************/ #ifndef _DELAY_INCLUDED_ #include <delay.h> #endif #ifndef _AT86RF230_INCLUDED_ #define _AT86RF230_INCLUDED_ #endif #ifndef _SPI_INCLUDED_ #include <spi.h> #endif /****************************************************************************/ //List of Transceiver Registers #define TRX_STATUS 0x01 #define TRX_STATE 0x02 #define TRX_CTRL_0 0x03 #define PHY_TX_PWR 0x05 #define PHY_RSSI 0x06 #define PHY_ED_LEVEL 0x07 #define PHY_CC_CCA 0x08 #define CCA_THRES 0x09 #define IRQ_MASK 0x0E #define IRQ_STATUS 0x0F #define VREG_CTRL 0x10 #define BATMON 0x11 #define XOSC_CTRL 0x12 #define FTN_CTRL 0x18 #define PLL_CF 0x1A #define PLL_DCU 0x1B #define PART_NUM 0x1C #define VERSION_NUM 0x1D #define MAN_ID_0 0x1E #define MAN_ID_1 0x1F #define SHORT_ADDR_0 0x20 #define SHORT_ADDR_1 0x21 #define PAN_ID_0 0x22 #define PAN_ID_1 0x23 #define IEEE_ADDR_0 0x24 #define IEEE_ADDR_1 0x25 #define IEEE_ADDR_2 0x26 #define IEEE_ADDR_3 0x27 #define IEEE_ADDR_4 0x28 #define IEEE_ADDR_5 0x29 #define IEEE_ADDR_6 0x2A #define IEEE_ADDR_7 0x2B #define XAH_CTRL 0x2C #define CSMA_SEED_0 0x2D #define CSMA_SEED_1 0x2E

//List of Transceiver States #define NOP 0x00 #define TX_START 0x02 #define FORCE_TRX_OFF 0x03 #define RX_ON 0x06 #define TRX_OFF 0x08 #define PLL_ON 0x09 #define RX_AACK_ON 0x22 #define TX_ARET_ON 0x25 //SPI data direction register (DDR) #define DDR_SS DDRB.4 //Slave Select #define DDR_MOSI DDRB.5 //Master Out - Slave In #define DDR_MISO DDRB.6 //Master In - Slave Out #define DDR_SCLK DDRB.7 //SPI Clock //SPI chip select (CS) #define RF230_SEL PORTC.3 #define DDR_RF230_SEL DDRC.3 //GPIO (General purpose input output) interface #define RF230_IRQ PINC.0 #define DDR_RF230_IRQ DDRC.0 #define RF230_SLP_TR PORTC.1 #define DDR_RF230_SLP_TR DDRC.1 #define RF230_RESET PORTC.2 #define DDR_RF230_RESET DDRC.2 //IRQ status masks #define IRQ_BAT_LOW #define IRQ_TRX_UR #define IRQ_TRX_END #define IRQ_RX_START #define IRQ_PLL_UNLOCK #define IRQ_PLL_LOCK //Protocol Commands #define PING #define DATA_REQ

0x80 0x40 0x08 0x04 0x02 0x01

//Battery low signal //FIFO underrun signal //End of frame (transmit and receive) //Beginning of receive frame //PLL goes from lock to unlock state //PLL goes from unlock to lock state

0xA1 0xA5

//See if there is a transmitter in range //Request data from transmitter

//energy detection threshold #define ED_THRESH 0x10 /****************************************************************************/ //Function Prototypes unsigned char RF_RX_channel_scan(void); unsigned char RF_TX_channel_scan(void); void init_spi(void); void set_transceiver_clock(void); void RF_init_spi(void); unsigned char RF_read_register(unsigned char address); void RF_write_register(unsigned char address, unsigned char data); void RF_init_transmitter(void); void RF_transmit_test(void); void RF_init_receiver(void); void RF_receive_test(void); void RF_download_frame(void); void RF_transmit_frame(void); void RF_receiver_listen(void); void RF_upload_frame(void); unsigned char RF_update_IRQ_status(void); unsigned char RF_transmit_done(void); unsigned char RF_receiver_listen_timeout(unsigned char loop_count); unsigned char RF_TX_channel_scan(void);

void void void void

RF_TX_sync(void); RF_tx_to_rx(void); RF_rx_to_tx(void); RF_RX_sync(void);

/****************************************************************************/ //Global Variables to use for interfacing unsigned char transmit_frame[20]; //User code fills transmit_frame unsigned char receive_frame[20]; //User code reads receive_frame unsigned char LQI; //User may use LQI after receiving frame unsigned char tx_frame_length; //User can set frame length before transmit unsigned char rx_frame_length; //Read frame length while receiving unsigned char curr_IRQ_status; /****************************************************************************/ void RF_clear_IRQ(void) { curr_IRQ_status = RF_read_register(IRQ_STATUS); curr_IRQ_status = 0; } /****************************************************************************/ //Function determines if channel is clear or not using CCA //CCA = clear channel assessment //channel parameter must be between 11 and 26 //Returns 0 if channel is busy and 1 if channel is clear unsigned char RF_CCA(unsigned char channel) { unsigned char status = 0; if ((channel < 11) || (channel > 26)) return 0; //invalid input channel = (channel | 0b10000000); //Set bit 7 to 1 to start CCA check RF_write_register(PHY_CC_CCA,channel); delay_us(140); status = RF_read_register(TRX_STATUS); if ((status&0b01000000) != 0) return 1; //channel is available else return 0; } /****************************************************************************/ //Function scans for clear channel using CCA. Sets channel to best option //for reception. Need another function to transmit beacon frame over that //channel, listen for a response. This function returns 0 if there is no good //channel, or it returns the value of the channel with the strongest signal. unsigned char RF_RX_channel_scan(void) { unsigned char i = 0; unsigned char status; for (i=16;i<=26;i++) { RF_write_register(PHY_CC_CCA,(0b10000000 | i)); //see if channel i is available do { status = RF_read_register(TRX_STATUS); } while((status&0b10000000) == 0); if ((status&0b01000000) == 0) return i; //return clear channel } for (i = 11; i<16; i++) { RF_write_register(PHY_CC_CCA,(0b10000000 | i)); //see if channel i is available do { status = RF_read_register(TRX_STATUS); } while((status&0b10000000) == 0); if ((status&0b01000000) != 0) return i; //return clear channel } return 0; }

/****************************************************************************/ //Function scans for channel with highest signal quality above a certain //threshold, returns best channel. Need another function to listen for beacon //frame, respond with acknowledgement frame. unsigned char RF_TX_channel_scan(void) { unsigned char max_channel_strength = 0; unsigned char max_channel = 0; unsigned char curr_channel_strength; unsigned char i; for (i=11;i<=26;i++) { RF_write_register(PHY_CC_CCA,i); //change to specific channel delay_us(400); //wait for channel change to complete RF_write_register(PHY_ED_LEVEL,0x00); //start ED calculation delay_us(130); //wait for conversion to complete curr_channel_strength = RF_read_register(PHY_ED_LEVEL); if ((curr_channel_strength > ED_THRESH) && (curr_channel_strength > max_channel_strength)) { max_channel_strength = curr_channel_strength; max_channel = i; } } return max_channel; } /****************************************************************************/ void RF_TX_sync(void) { unsigned char channel; while (1) { //keep searching until a receiver is found do { //find best channel, if any available channel = RF_TX_channel_scan(); } while(channel == 0); RF_write_register(PHY_CC_CCA,channel); //switch to channel delay_us(200); RF_tx_to_rx(); //switch to reception mode delay_us(200); if (RF_receiver_listen_timeout(50) == 1) break; //wait for a short time for a frame to be received } RF_rx_to_tx(); delay_us(200); tx_frame_length = 1; transmit_frame[0] = 0xAA; RF_download_frame(); RF_transmit_frame(); } /****************************************************************************/ void RF_RX_sync(void) { unsigned char channel,i; do { channel = RF_RX_channel_scan(); } while(channel==0); RF_write_register(PHY_CC_CCA,channel); //switch to channel delay_us(200); while (1) { RF_rx_to_tx(); //switch to transmission mode delay_us(200); tx_frame_length = 10; for (i=0;i<10;i++) { transmit_frame[i] = 0xAA; }

RF_download_frame(); RF_transmit_frame(); RF_tx_to_rx(); //switch to reception mode delay_us(200); if (RF_receiver_listen_timeout(50) == 1) break; } } /****************************************************************************/ unsigned char RF_receiver_listen_timeout(unsigned char loop_count) { do { if (RF230_IRQ != 0) { curr_IRQ_status = RF_read_register(IRQ_STATUS); } delay_us(250); loop_count--; } while (((curr_IRQ_status & IRQ_TRX_END) == 0 )&&((loop_count) > 0)); if ((curr_IRQ_status&IRQ_TRX_END) != 0) { curr_IRQ_status = 0; return (loop_count+1); } curr_IRQ_status = 0; return loop_count; } /****************************************************************************/ void init_spi(void) { //Set up SPI I/O data direction DDR_MOSI = 1; DDR_MISO = 0; DDR_SCLK = 1; DDR_SS = 1; //SS must be configured as output to set MCU as SPI master PORTB.4 = 0; //Set up SPI Control Registers //Bit 7 - Interrupt Enable SPIE=0 -> no ISR //Bit 6 - SPI Enable SPE=1 -> enable spi //Bit 5 - Data Order DORD=0 -> msb first //Bit 4 - Master/Slave Select MSTR=1 ->MCU is SPI master //Bit 3 - Clock Polarity CPLO=0 (for the transceiver initially) //Bit 2 - Clock Phase CPHA=0 (for the transceiver initially) //Bits 1:0 - SPR1, SPR0: SPI Clock Rate Select 1 and 0 //SPR1:SPR0=00 along with SPI2X=1 sets SCK to f_osc/2 = 8MHz/2 = 4MHz SPCR0 = 0b01010000; //SPI Control Register SPSR0 = 1; //SPI Status Register (SPI2X) } /****************************************************************************/ void set_transceiver_clock(void) { //Set the clock polarity and phase for the transceiver if other slaves //(with different clock settings) are also being used by the MCU SPCR0 = 0b01010000; SPSR0 = 1; //SPCR0 &= 0b11110111; //Bit 3 Clock Polarity CPLO=0 //SPCR0 &= 0b11111011; //Bit 2 Clock Phase CPHA=0 } /****************************************************************************/ void RF_init_spi(void) { //Set up GPIO data directions DDR_RF230_IRQ = 0; DDR_RF230_SLP_TR = 1;

DDR_RF230_RESET = 1; DDR_RF230_SEL = 1; RF230_SEL = 1; //Initialize chip select signal high RF230_RESET = 1; RF230_SLP_TR = 0; } /****************************************************************************/ //2 byte SPI transmit. Information in second byte is thrown away, just //transmit junk data to receive byte. unsigned char RF_read_register(unsigned char address) { unsigned char junk; RF230_SEL = 0; //Address should only be 6 bits. MSB should be 1, bit 6 should be 0. //Writing to SPDR0 starts transmission SPDR0 = 128 + (address & 0b00111111); while((SPSR0&0x80) == 0); //Wait for transfer to complete junk = SPDR0; //Start next transmission, transmit junk, get back data from register SPDR0 = 0x00; while((SPSR0&0x80) == 0); //Wait for transfer to complete RF230_SEL = 1; return SPDR0; //Return contents of register in transceiver } /****************************************************************************/ //2 byte SPI transmit. Information in second byte is data to write to register void RF_write_register(unsigned char address, unsigned char data) { unsigned char junk; RF230_SEL = 0; //First 2 bits are 11, rest is address. Start transmitting command SPDR0 = 192 + (address & 0b00111111); while((SPSR0&0x80) == 0); //Wait for transfer to complete junk = SPDR0; SPDR0 = data; while ((SPSR0&0x80) == 0); //Wait for transfer to complete RF230_SEL = 1; } /****************************************************************************/ //Initialize basic one-way transmitter void RF_init_transmitter(void) { //RF_write_register(TRX_CTRL_0, //Should write to TRX_CTRL_0 register to set output current and //clockspeed settings. RF230_RESET = 0; #asm nop nop nop nop nop #endasm RF230_RESET = 1; RF_write_register(TRX_CTRL_0,0b01001000); delay_us(800); //enable ONLY the TRX_done interrupt RF_write_register(IRQ_MASK,0b00001000); delay_us(200); RF_write_register(PHY_TX_PWR,0); //reduced power on transmit delay_us(200);

RF_write_register(PHY_CC_CCA,11); //channel 11 delay_us(200); RF_write_register(TRX_STATE,FORCE_TRX_OFF); delay_us(1880); RF_write_register(TRX_STATE,PLL_ON); delay_us(200); RF_write_register(TRX_CTRL_0,0b01001000); delay_us(800); RF_write_register(TRX_STATE,FORCE_TRX_OFF); delay_us(1880); RF_write_register(TRX_STATE,PLL_ON); delay_us(200); } /****************************************************************************/ void RF_tx_to_rx(void) { RF_write_register(TRX_STATE,FORCE_TRX_OFF); delay_us(1880); RF_write_register(TRX_STATE,RX_ON); delay_us(200); } /****************************************************************************/ void RF_rx_to_tx(void) { RF_write_register(TRX_STATE,FORCE_TRX_OFF); delay_us(1880); RF_write_register(TRX_STATE,PLL_ON); delay_us(200); } /****************************************************************************/ //Transmit once per second... call this once every second void RF_transmit_test(void) { unsigned char i; RF_write_register(TRX_STATE,PLL_ON); RF230_SEL = 0; SPDR0 = 0b01100000; //Frame transmit mode while((SPSR0&0x80) == 0); //Wait for transfer to complete SPDR0 = 100; //Frame length while((SPSR0&0x80) == 0); //Wait for transfer to complete for (i=0;i<100;i++) { SPDR0 = i; while((SPSR0&0x80) == 0); //Wait for transfer to complete } RF230_SEL = 1; RF230_SLP_TR = 1; //Signal to transmit frame #asm nop nop nop nop nop #endasm RF230_SLP_TR = 0; do { //Loop until end of transmit frame, when we break out of loop if (RF230_IRQ != 0) { //Get the status of the IRQ curr_IRQ_status = RF_read_register(IRQ_STATUS); PORTA = curr_IRQ_status; } //Loop until done done transmitting frame } while ((curr_IRQ_status & IRQ_TRX_END) == 0);

} /****************************************************************************/ void RF_init_receiver(void) { RF230_RESET = 0; #asm nop nop nop nop nop #endasm RF230_RESET = 1; RF_write_register(TRX_CTRL_0,0b01001000); delay_us(800); //enable ONLY the TRX_done interrupt RF_write_register(IRQ_MASK,0b00001000); delay_us(200); RF_write_register(TRX_STATE,FORCE_TRX_OFF); delay_us(1880); RF_write_register(TRX_STATE,RX_ON); delay_us(200); } /****************************************************************************/ void RF_receive_test(void) { //Put transceiver in listen mode //Wait for IRQ indicating end of frame //Upload frame, digest //Done, start again immediately unsigned char length,LQI; unsigned char junk = 0; unsigned char frame[128]; unsigned char index = 0; do { if (RF230_IRQ != 0) { curr_IRQ_status = RF_read_register(IRQ_STATUS); } //Loop until done receiving frame, then we can start uploading } while ((curr_IRQ_status & IRQ_TRX_END) == 0); RF230_SEL = 0; SPDR0 = 0b00100000; //Frame receive mode while((SPSR0&0x80)==0); //Wait for frame transfer to complete SPDR0 = junk; //Transmit a junk byte to receive a byte of frame while((SPSR0&0x80) == 0); //Wait for transfer to complete length = SPDR0; while (length>0) { SPDR0 = junk; while((SPSR0&0x80) == 0); //Wait for transfer to complete frame[index++]=SPDR0; //Get byte of frame length--; } SPDR0 = junk; while((SPSR0&0x80) == 0); LQI = SPDR0; //LQI is a byte between 0 (bad) and 255(excellent) PORTA = LQI; RF230_SEL = 1; } /****************************************************************************/

//Transceiver must be in appropriate state before calling this void RF_download_frame(void) { unsigned char i; RF_write_register(TRX_STATE,PLL_ON); RF230_SEL = 0; SPDR0 = 0b01100000; //Frame transmit mode while((SPSR0&0x80) == 0); //Wait for transfer to complete SPDR0 = tx_frame_length; //Frame length while((SPSR0&0x80) == 0); //Wait for transfer to complete for (i=0;i<tx_frame_length;i++) { SPDR0 = transmit_frame[i]; while((SPSR0&0x80) == 0); //Wait for transfer to complete } RF230_SEL = 1; } /****************************************************************************/ //Function must be called AFTER RF_download_frame() and when Transceiver is //in PLL_ON state. void RF_transmit_frame(void) { //TODO: First test to make sure transceiver is in proper state. RF230_SLP_TR = 1; //Signal to transmit frame #asm nop nop nop nop nop #endasm RF230_SLP_TR = 0; } /****************************************************************************/ //Call this function when in Receiver mode. When this function returns, it is //time to start receiving. void RF_receiver_listen(void) { do { if (RF230_IRQ != 0) { curr_IRQ_status = RF_read_register(IRQ_STATUS); } //if ((IRQ_status & IRQ_TRX_END) != 0) break; //Loop until done receiving frame, then we can start uploading } while ((curr_IRQ_status & IRQ_TRX_END) == 0); curr_IRQ_status = 0; } /****************************************************************************/ unsigned char RF_quick_listen(void) { unsigned char returnval = 0; if (RF230_IRQ != 0) { curr_IRQ_status = RF_read_register(IRQ_STATUS); if ((curr_IRQ_status & IRQ_TRX_END) != 0) returnval = 1; else returnval = 0; } curr_IRQ_status = 0; return returnval; } /****************************************************************************/ void RF_upload_frame(void) { unsigned char i; RF230_SEL = 0; SPDR0 = 0b00100000; //Frame receive mode

while((SPSR0&0x80)==0); //Wait for transfer to complete SPDR0 = 0x00; //Transmit a junk byte to receive a byte of frame while((SPSR0&0x80) == 0); //Wait for transfer to complete rx_frame_length = SPDR0; i = 0; while (i<rx_frame_length) { SPDR0 = 0x00; while((SPSR0&0x80) == 0); //Wait for transfer to complete receive_frame[i++]=SPDR0; //Get byte of frame } SPDR0 = 0x00; while((SPSR0&0x80) == 0); LQI = SPDR0; //LQI is a byte between 0 (bad) and 255(excellent) RF230_SEL = 1; } /****************************************************************************/ unsigned char RF_update_IRQ_status(void) { if (RF230_IRQ == 1) { curr_IRQ_status = RF_read_register(IRQ_STATUS); return 1; } else return 0; } /****************************************************************************/ unsigned char RF_transmit_done(void) { RF_update_IRQ_status(); if ((curr_IRQ_status & IRQ_TRX_END) != 0) return 1; //if transmit has completed, return 1. else return 0; } /****************************************************************************/ void RF_wait_for_transmit(void) { do { //Loop until end of transmit frame, when we break out of loop if (RF230_IRQ != 0) { //Get the status of the IRQ curr_IRQ_status = RF_read_register(IRQ_STATUS); //PORTA = curr_IRQ_status; } } while ((curr_IRQ_status & IRQ_TRX_END) == 0); curr_IRQ_status = 0; } /****************************************************************************/

Accelerometer header :
/***************************************************************************** * kxp74.h * Lab 6: Final Project * ECE 476: Digital Systems Design Using Microcontrollers * Cornell University * April 2007 * Andrew Godbehere (abg34) and Nathan Ward (njw23) ****************************************************************************/

/****************************************************************************/

//Include #ifndef _KXP74_INCLUDED_ #define _KXP74_INCLUDED_ #endif #include <spi.h> #include <delay.h> //SPI Chip Select (CS) #define DDR_SENSOR1_CS #define SENSOR1_CS #define DDR_SENSOR2_CS #define SENSOR2_CS #define DDR_SENSOR3_CS #define SENSOR3_CS #define DDR_SENSOR4_CS #define SENSOR4_CS //KXP74 SPI Commands //The accelerometer uses #define CONVERT_XAXIS #define CONVERT_ZAXIS #define CONVERT_YAXIS #define READ_REGISTER #define WRITE_REGISTER

DDRC.4 PORTC.4 DDRC.5 PORTC.5 DDRC.6 PORTC.6 DDRC.7 PORTC.7

an 8-bit command register to carry out its functions 0x00 0x01 0x02 0x03 0x04

//Operational Modes //The 8-bit read/write control register selects the various operational modes // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 // 0 0 0 Speed_0 Speed_1 Enable Self test Parity #define DEFAULT_MODE 0b00100 //Enable and set to 32KHz low noise mode #define STANDBY_MODE 0b00000 #define SELF_TEST 0b00110 /****************************************************************************/ void init_sensor_spi(void) { //Set SPI chip select data direction DDR_SENSOR1_CS = 1; // DDR_SENSOR2_CS = 1; // DDR_SENSOR3_CS = 1; // DDR_SENSOR4_CS = 1; //Initialize chip select signals high SENSOR1_CS = 1; // SENSOR2_CS = 1; // SENSOR3_CS = 1; // SENSOR4_CS = 1; } /****************************************************************************/ void set_sensor_clock(void) { //Set the clock polarity and phase for the accelerometers if other slaves //(with different clock settings) are also being used by the MCU SPCR0 = 0b01011101; SPSR0 = 0; //SPCR0 &= 0b11110111; //SPCR0 &= 0b11111011; } /****************************************************************************/ void init_sensors(void) { //Upon power up, the Master must write the operational mode command to //each accelerometer's 8-bit control register. unsigned char junk = 0; //Sensor 1

SENSOR1_CS = 0; //CS low to select sensor 1 and start transmission //On the falling edge of CS, 2-byte command written to control register junk = spi(WRITE_REGISTER); //Initiate write to the control register junk = spi(DEFAULT_MODE); //Set enable bit in the register SENSOR1_CS = 1; //CS goes high at the end of transmission //Sensor 2 // SENSOR2_CS = 0; //CS low to select sensor 2 and start transmission //On the falling edge of CS, 2-byte command written to control register // junk = spi(WRITE_REGISTER); //Initiate write to the control register // junk = spi(DEFAULT_MODE); //Set enable bit in the register // SENSOR2_CS = 1; //CS goes high at the end of transmission //Sensor 3 // SENSOR3_CS = 0; //CS low to select sensor 3 and start transmission //On the falling edge of CS, 2-byte command written to control register // junk = spi(WRITE_REGISTER); //Initiate write to the control register // junk = spi(DEFAULT_MODE); //Set enable bit in the register // SENSOR3_CS = 1; //CS goes high at the end of transmission //Sensor 4 // SENSOR4_CS = 0; //CS low to select sensor 4 and start transmission //On the falling edge of CS, 2-byte command written to control register // junk = spi(WRITE_REGISTER); //Initiate write to the control register // junk = spi(DEFAULT_MODE); //Set enable bit in the register // SENSOR4_CS = 1; //CS goes high at the end of transmission } /****************************************************************************/ unsigned char get_sensor(unsigned char sensor, unsigned char axis) { //Accelerometer Read Back Operation //Sample, convert and read back sensor data with 12-bit ADC unsigned char byte1, byte2, junk; switch(sensor) { case 1: //Sensor 1 SENSOR1_CS = 0; //CS low to select chip and start transmission //On the falling edge of CS, 2-byte command written to control reg junk = spi(axis); //Send command to initiate conversion of axis //There must be a minimum of 40us between the first and second //bytes to give the A/D conversion enough time to complete delay_us(50); byte1 = spi(0x00); //Read in most significant 8 bits byte2 = spi(0x00); //Read in least significant 4 bits SENSOR1_CS = 1; //CS goes high at the end of transmission //Return only 7 most significant bits return (byte1>>1); /* case 2: //Sensor 2 SENSOR2_CS = 0; //CS low to select chip and start transmission //On the falling edge of CS, 2-byte command written to control reg junk = spi(axis); //Send command to initiate conversion of axis //There must be a minimum of 40us between the first and second //bytes to give the A/D conversion enough time to complete delay_us(50); byte1 = spi(0x00); //Read in most significant 8 bits byte2 = spi(0x00); //Read in least significant 4 bits SENSOR2_CS = 1; //CS goes high at the end of transmission //Return only 7 most significant bits return (byte1>>1); case 3: //Sensor 3 SENSOR3_CS = 0; //CS low to select chip and start transmission //On the falling edge of CS, 2-byte command written to control reg junk = spi(axis); //Send command to initiate conversion of axis //There must be a minimum of 40us between the first and second //bytes to give the A/D conversion enough time to complete delay_us(50); byte1 = spi(0x00); //Read in most significant 8 bits

byte2 = spi(0x00); //Read in least significant 4 bits SENSOR3_CS = 1; //CS goes high at the end of transmission //Return only 7 most significant bits return (byte1>>1); case 4: //Sensor 4 SENSOR4_CS = 0; //CS low to select chip and start transmission //On the falling edge of CS, 2-byte command written to control reg junk = spi(axis); //Send command to initiate conversion of axis //There must be a minimum of 40us between the first and second //bytes to give the A/D conversion enough time to complete delay_us(50); byte1 = spi(0x00); //Read in most significant 8 bits byte2 = spi(0x00); //Read in least significant 4 bits SENSOR4_CS = 1; //CS goes high at the end of transmission //Return only 7 most significant bits return (byte1>>1); */ } }

S-ar putea să vă placă și