1. UNDERSTAND HOW THE GENERATOR WORKS First of all, I had to learn more about the generator. In fact; it is very simple: a small magnet is placed into a tube. Around this tube is a coil. Each time the magnet slides inside the coil, then every turn of the coil are under magnet’s flux, which is moving. Therefore, a differentiated flux is created by walking. According to Faraday’s Law of Electromagnetic Induction, there is a induced voltage in the coil, V(t), which expression is : N d (t ) V (t ) i with N the number of turns. dt i 1 As we consider here that the magnet has a constant velocity v, which is not far from the reality on small length (about 2centimeters/0.8inches), this relation becomes: N d ( xi ) V (t ) v dx i i 1 Thanks to Matlab simulation, we can predict the magnetic flux distribution generated by a specific magnet in space; therefore the induced voltage can be foretold. This previous model stuck literally to the reality, so that previous people who worked on this project have chosen to keep this analytical approach. The coil’s length has been calculated for an optimum way of harvesting energy trough a differentiate magnetic flux from these data. The problem was then to build a model of the movement of the magnet sliding into the coil… Some measurements with accelerometers have been made in the heel of a shoe during a walk to see on what force fields the magnet is under, and try to predict the movements and the speed of the magnet during a walk-that obviously depends on every walker’s profile. 2. SEEING HOW MUCH POWER CAN BE EXPECTED a. From our wearable generator Induced voltage waveform is as follow (in an heel during a step ~1s): Voltage level can be heavily improved but in disregard of overall performances. We can easily reach near 12V by increasing number of copper wire turns. Nonetheless, by doing this, we increase also the global length of this copper wire, which implies an internal resistance increasing that slashes output power. Using diodes with mega Schottky barrier effect enables to design a better rectifier stage that is more efficient. I found from Philips® such diodes with a very low Vdrop (average 130mV) when forward current is 10mA. Therefore, rectified voltage keeps suitable level and losses are reduced. P(t ) RL (V (t ) Vdrop ) 2 N with V (t ) Vi cos(i t i ) ( RL RS ) 2 i 0 from Fourier’s transformée of real movement induced voltage. Finding capacitors with low internal series resistance enables also to keep charged these capacitors longer than common ones and reduces losses in a dobler rectifier stage. The next section was a search throughout the web about devices consumption, in sleeping mode and in active mode. This part was also the occasion to learn how to find further information and other details by myself thanks to the Internet. b. For wearable devices consumption: Devices that can’t be powered by our generator: Devices that potentially could be powered by our generator: 3. DESIGN A RECTIFIER STAGE Now that we have an optimized generator, this induced voltage is still in alternative. However, our demonstrator needs a direct current supply to properly work. Previous works highlight that a rectifier stage such as a doubler stage provides the best output power. I have made some simulations under PSPICE from real measurements of the induced voltage during a walk that confirms these previous works. I have compared this result to other rectifier stage (like quadrupler, full wave & half wave rectifiers, etc…) Therefore, diodes with very low drop voltage and small in size capacitors (but with big capacity, too) are required. Previous researches have shown than the BAT series from Philips® have the lowest Vdrop, but I found some diodes better than previously found. They use the Schottky barrier effect to reach their performances: at 10mA, the Vdrop, or Vforward, is only 130mV whereas the BAT series have at the same current more than 300mV! Actually, as the dobler don’t provide us up to 5V alone, we can’t use any amplifier or other logical gates which are very common to regulate his kind of regulation. This dobler provides us with a Direct Current that can be regulated by a Step-Up/Down DC-DC Converter, just like the MAX1672 component, which allows us to input from continuous 1.8V and have an output up to 11V (with a lower current, there is obviously no generation of power at all, only conversion). Please, be sure not overpass 3.5V to supply the N RF24E1: it can only stand 3.6V maximum. On the other hand, voltage must be upper than 1.9 to enable a properly work to be done by RF transmission. See MAX1672 Datasheet for further details. 4. DESIGN A DEMONSTRATOR I found some different ideas for demonstrators: obviously, as generator is placed in shoe’s heel, it must be linked with the foot: a pedometer application sounds great both for Maeve and me. When we have seen partnership between Nike® and Ipod®, we also want to make a similar project; by designing a radio frequency transmitter sensor in Nike® shoes that synchronizes with Ipod while running, a small program can show distances, average speed and similar data from running. To make our own Radio Frequency (RF) pedometer system, it was envisaged trying several options. First of all, logic gates that realize a step counter which value is sent by a RF transmitter sounded great. However, our voltage level was not enough to perform this kind of technologies. We have also understood that if we can have the induced voltage waveform transmitted too, it should be good for future enhancement of our system. This new challenge brought us to a second choice, which was using a very low power, very low voltage microprocessor from Texas Instruments®: MSP430 which works from 1.8V. An issue was we must include RF transmitter that fits the MSP430 voltage range considering power consumption. Most of those I found during my researches weren’t efficient enough according to this parameter (actually only one found could do the job: CHIPCON1000 from Texas Instruments®). That’s why I considered a global component which features microcontroller, Analog to Digital Converter, and RF transmitter; two components are a cut above the rest: RFPIC12F675 (which also integrate an EEPROM) and NRF24E1 (working with an external EEPROM). According to datasheet, RFPIC12F675 is not as good as NRF24E1 considering power issue; moreover, NRF24E1 is a transceiver, that means it could perform both transmission and reception. We found this particularity interesting for future enhancements of our demonstrator. Number of analog inputs was a great advantage. Therefore, I choose, with Maeve’s validation, this NRF24E1 component, even if some drawback existed: for instance, small size and unusual package (QFN 36) for this component were going to present some problems for soldering. Availability was also an issue. See further details in the third part on third section of present report. 5. BUILDING DEMONSTRATOR Actually, I have just followed the schematic given by Nordic Semiconductor to build both the RF transmitter and RF Receiver, but some parameters result of personal choices. I explain why some choices have been made. I work with a crystal frequency of 16 MHz; this option was selected between ranges of 4 up to 20 MHz for datasheet mentions that this specific frequency gives best over all performances. Moreover, it enables us to transmit by Shockburst™ method (power saving). Principle: Here is the current consumption with 2 proceeding ways. The ShockBurst™ technology uses on-chip FIFO to clock in data at a low data rate and transmit at a very high rate thus enabling extremely power reduction. You gain access to the high data rates (1 Mbps) offered by the 2.4 GHz band without the need of a costly, highspeed microcontroller (MCU) for data processing. By putting all high speed signal processing related to RF protocol on-chip, the nRF24El offers the following benefits: • Highly reduced current consumption • Lower system cost (facilitates use of less expensive microcontroller) • Greatly reduced risk of 'on-air' collisions due to short transmission time NRF24E1 uses a bootstrap loader, that requires the use of a generic 25XX320 EEPROM. I have chosen 25AA320 memories from Microchip® as they work from 1.8V up to 5.5V at a maximum frequency of 1MHz, which could be useful as our generator only provides little power (range of few mW). I have also opted for some low current diodes to display on output pins their high or low state. As NRF24E1 can only provide 0.5mA under 2V on each digital output pins, I preferred use these diodes, even if their lightness will be weak, because it’s more convenient than use oscilloscope to watch high or low state. I want also warn for future works that specifications for Crystals are not well followed, because the used ones have been found directly from boxes in laboratory; however, I think they should work (very few characteristics are not fulfilled and this should not matter) but perhaps they will need to be changed. The same can be said about capacitors (4.7pF, which are not Tantallum); the convenient package size was the parameter I’ve considered for choosing the ones used in this circuit. 6. PROGRAMMING NRF24E1: The very first thing to begin with is to know well the different registers used by this component to work properly. Some of them are well initialised for our application, so that we don’t need to change their hexadecimal values. Some have 8 bits, other only 2. Registers’ values imply different behaviours from microcontroller. Good values for our demonstrator are entered in registers thanks to a C-language (c file with h file), and then translated by a compiler (Keil compiler was used) in series of hexadecimal bytes (hex file), which is the basic language for microcontroller. This hex file is sent by EEPROM Programmer into memory (EEPROM). NRF24E1 only has a small internal ROM which contains a bootstrap loader that downloads (thanks to Serial Peripheral Interface on Port 1 P1) program contained in external EEPROM to 256 byte data internal RAM that is erased each power reset. To make our Intel hex file compiled suitable for this bootstrap loader, we need to add 3 byte heading our file: data rate speed plus crystal frequency used, offset to start of our program, number of 256 byte blocks in our program. Before programming, I had to go through datasheet of NRF24E1 with a fine-tooth comb to understand how does work this microcontroller which is based on an 8051 structure. The red spots lay a stress on subsystems used in my program code: Here are listed some special function registers (SFR) used for my application. Their places in NRF24E1 memory are specified in the h file, so as simply bits definition too: Analog to Digital Converter: ADCCON, ADCSTATIC, ADCDATAH, ADCDATAL Serial Peripheral Interface: SPICLK, SPI_DATA, SPI_CTRL Radio Transceiver: RADIO, PWR_UP, CS, CE, DR1, CK_CTRL, CKCON Interruption: TCON, TH0, TMOD, EXIF, EIE, EIP, EICON Display: P0, P0_ALT, P0_DIR, P1, P1_ALT, P1_DIR a. Analog Digital Converter (ADC) Registers: ADCCON: Bit(s) Name 7 DIFFM 6 SLEEP 5 CLK8 4-2 ADCBIAS 1-0 ADCRES ADCSTATIC: Bit(s) 7 6 5 4 3-0 (in red, my selection) Function Enable differential measurements: 0 or 1 Set A/D converter in a reduced power mode ADCCLK frequency = CPU clock/32:0 or clock/8:1 No need to change for NRF24E1 operation 11 select resolution: 12-bit. Name CSTARTN ADCRUN NPD EXTREF Function Toggle High -> Low -> High to start A/D conversion. Set high to have the A/D converter run continuously 0 Set to 0 to put A/D converter in power down state Select reference for A/D converter 0: Use internal reference. 1: Use external pin AREF ADCSEL upper For n=0…7, ADCSEL=n willofselect input pin AINn ADCDATAH: bits of converted value AIN voltage ADCDATAL: lower bits and flags if under/overflow occurred Reference for ADC is software selectable between AREF input and an internal 1.22V reference. Here, internal reference is chosen, so that AREF input is ignored.ADC has 9 inputs. Typically used in start/stop mode, sampling time is then under software control. It is software configured to perform 12 bit conversions. ADC may also be used in differential mode with AIN0 used as inverting input and one of the other inputs used as noninverting. Unused input 8, enables software to monitor NRF24E1 supply voltage by converting internal VDD/3 with AREF pin. There are only 4 used; 2 modes are used; one in differential way (yellow), other in ‘classic’ way (pink or green). This enables user to measure voltage on coil whereas the coil is connected to rectifier stage (see InitADC(n) and InitDiffAdc() functions in Appendix 10). Best resolution (12bits) and frequency (Clk/8) are selected thanks to the ADCCON & ADCSTATIC registers. Continuous running of ADC is not used due to the power issue. Sampling performances are slashed by data sending frequency. Conversion is made by Conversion() function. It consists on a toggle on the CSTARTN pin. ReadADC(n) function reads converted value, which is stored in two 8bits registers ADCDATA(H&L). Four lower bits give conversion indications (overflow …). A flag (IE2 in EXIF register) highlights that the signal was sampled successfully. Waiting for this flag set high is needed before reading the values. So this bit must be reset low, once it goes high. b. Serial Peripheral Interface(SPI) Registers for Serial Peripheral Interface with EEPROM and Radio Transceiver: SPICLK: divider factor from CPU clock to SPI clock (2) SPI_DATA: SPI data input/output (used for read/write) (8) SPI_CTRL: how is used the SPI (with radio or port1) (2) 00 10 The SPI is very useful to control EEPROM (handled by bootstrap loader on P1) or to control RADIO transceiver (via CPU data bus). According to recommendations in datasheet, I have used this SPI to link both transceiver and microcontroller. Whenever SPI_DATA (8 bits) register is written to, a sequence of 8 pulses is started on SCK, and the 8 bits of SPI_DATA register are clocked out on SDO with msb first. Simultaneously 8 bits from SDI are clocked into SPI_DATA register. Ouput data is shifted on negedge SCK, and input data is read on posedge SCK. This means that we perform as well a reading as a writing thanks to SPI. Data in SPI_DATA register before SPI transaction is written in selected peripheral. In the same time, data in SPI_DATA register after SPI transaction is read from the peripheral. When the 8 bits are done, SPI_READY interrupt (fifth bit of EXIF register) goes active, and the 8 bits from SDI may be read from SPI_DATA register. The EXIF.5 bit must be cleared before starting another SPI transaction by writing to SPI_DATA register again. SCK, SDO and SDI may be external pins or internal signals, as defined in SPI_CTRL register: P1 port or Radio port are selected to be connected with SP Interface. You have certainly quoted that there are a lot of delays in a SPI transmission. However, to ensure that data has well been sent, 6th bit of EXIF (flag bit) is expected in high state and then must be cleared before ending SPI transaction. c. Radio Transmission There is only one register for the Radio Transceiver: RADIO. Actually, I use, in this register, some bits which have each a specific use: powering NRF2401: PWR_UP (8th bit) enable transmission mode: CE (7th) enable configuration mode: CS (4th) when reception ends: DR1 (3rd) link with SCK from SPI: CLK1 (2nd) link with SDO and SDI: DATA (1st) Transceiver part of the circuit has identical functionality to the NRF2401, which is a radio transceiver for the world wide 2.4-2.5 GHz ISM band integrated in the NRF24E1. This transceiver consists of a fully integrated frequency synthesizer, a power amplifier, a modulator and two receiver units. Output power and frequency channels and other RF parameters are easily programmable by RADIO register. It can perform 2 different signals reception simultaneously (thanks to DuoCeiver™ technology, unused here) and be accessed through an internal parallel port and / or an internal SPI. NRF2401 subsystem can be programmed using a simple 3-wire interface where the data rate is decided by the speed of the CPU. It is more convenient to use the built-in SPI interface to do the most common transceiver operations as RF configuration and SPI signal SPI_CTRL=10 ShockBurst™ RX (reception) or TX (transmission). CS (active high) CE for ShockBurst™ The radio port is connected in different ways to the CS for Configuration SPI hardware when SPI_CTRL is '10', which is the SCK nRF2401/CLK1 case (when SPI_CTRL is 'Ox', all radio pins are SDI nRF2401/DATA connected directly to their respective port pins). SDO nRF240l/DATA Data Ready DR1 All configuration of the NRF2401 subsystem is done via a 3-wire interface (CS, CLKl and DATA) to a single configuration register. The configuration word can be up to 18 bytes long. The configuration bits (DATA) must be clocked (by CLKl) into NRF2401 subsystem, with msb first, while CS=1. No more than 18 bytes may be downloaded. CE and CS may not be high at the same time. Setting one or the other decides whether configuration or active mode is entered. When a transmission occurs, a flag bit that informs the transmission is well ended is set high on both channels (DR1 & DR2; DR: Data Ready). These flags can be software programmed as interrupts to the microcontroller (or polled via a GPIO port). Here, I only use a unique channel (use of DR1 and CLK1 bits only). RF current consumption is only 10.5 mA in TX mode (output power -5dBm) and 18 mA in RX mode. For power saving the transceiver can be turned on / off under software control (thanks to PWR_UP & CE bits). Initialising transmission: Each time PWR_UP is set high, datasheet explains user must wait 3ms; that’s why there is a delay function in my program. CS bit is used to configure the transceiver at each powering up. Mode PWR UP CE CS Active (RX/TX) 1 l 0 Configuration 1 0 1 Stand by 1 0 0 Power down 0 X X The configuration word (144 bits) enables transceiver subsystem to handle the RF protocol and is loaded into the configuration register. Configuration blocks are as follows: Bit Length Function Value (hexa or bit) 143:120 24 Reserved for testing 0x8E081C: (default) 119:112 8 Length of data payload section RX channel 2 0x20: (unused) 111:104 8 Length of data payload section RX channel 1 0x20: (32 bits sent) 103:64 40 Up to 5 byte address for RX channel 2 0x00000000: (unused) 63:24 40 Up to 5 byte address for RX channel 1 0x000000BB: (1 byte*) 23:18 6 Number of address bits (both RX channels). 001000: (8) 17 1 8 or 16 bit CRC 1: 16bits CRC 16 1 Enable on-chip CRC generation/checking. 1: CRC enabled 15 1 Enable two channel receive mode 0: only one receiver used 14 1 Communication mode (Direct or ShockBurst™) 1: Shockburst™ enabled 13 1 RF data rate ( 1 Mbps requires 16MHz crystal) 1: 1Mbps 12:10 3 Crystal frequency 011: 16MHz 9:8 2 RF output power 00: -20dB 7:1 7 Frequency channel (arbitrary choice) 0000010: 2402MHz 0 1 RX or TX operation 0 for transmitter (1 receipter) This configuration word is shifted in MSB first on positive CLKl edges. *Suggestions for addresses use to have less false detection: equal bytes are not recommended. Level shifted only one time could be detected in noise. First byte of address should not start with 0x55 or OxAA as this may be interpreted as part of preamble, causing address mismatch for the rest of the address. Transmitting and receiving packets: ShockBurst™ transmit sequence: (CPU interface pins: CE, CLKl, DATA) 1. When the application CPU has data to send, set CE high. This activates NRF2401 on-board data processing. 2. The address of the receiving node (RX address) and payload data is clocked into the NRF2401 subsystem. The application protocol or CPU sets the speed < 1Mbps (ex: 10kbps). 3. CPU sets CE low; this activates a ShockBurst™ transmission. 4. ShockBurst™: • RF front end is powered up • RF package is completed (preamble added, CRC calculated) • Data is transmitted at high speed (1 Mbps configured by software). PREAMBLE ADDRESS PAYLOAD CRC Preamble is 8 bits in length and is dependent of the first address bit and is automatically added or removed to the data packet 1st ADDR-BIT is 1: 01010101 or 0: 10101010 8 to 40 bits length, automatically added and removed from received packet Data to be transmitted: size is 256 bits minus the following: (Address + CRC).(32) The CRC is optional (8 or 16 bits length) and automatically added or removed ShockBurst™ receive sequence: (CPU interface pins: CE, DR1, CLK1 and DATA) 1. Correct address and size of payload of incoming RF packages are set when nRF2401 subsystem is configured to ShockBurst™ RX. 2. To activate RX, set CE high. 3. After 200µs settling, NRF2401 subsystem is monitoring the air for incoming communication. 4. When a valid package has been received (correct address and CRC found), nRF2401 subsystem removes the preamble, address and CRC bits. 5. NRF2401 subsystem notifies (interrupts) the CPU by setting the DRl pin high. 6. CPU may set the CE low to disable the RF front end (low current mode). 7. The CPU will clock out just the payload data at a suitable rate (ex. 10 kbps). 8. When all payload data is retrieved NRF2401 subsystem sets DRl low again, and is ready for new incoming data package if CE is kept high during data download. If the CE was set low, a new start up sequence can begin. d. Interruption There are several registers used in this section: to control internal clock: CKCON to control timer 0: TCON TMOD TH0 to enable interruption: IE EXIF EIE EIP Aim of interruption here is to have a time indication, to realize a chronometer function. Using RC oscillator is not recommended in datasheet, because its frequency is said very inaccurate (1 to 5 KHz). Yet, this option should be the best as regards as the power consumption. However, a program correcting this inaccuracy is “heavy” to be implemented in our embedded final product, but still realisable. I have explored option to use timer 2 (16 bits) in capture mode so that software has an accurate base of time that could be read at any time… but this option has a consequence: using a multiplex in/output on pin 4 (P1.0), which is still not very easy to encode. I found the last option more convenient to program. Before I present how this chronometer function is made, let me introduce you some information about timers. The NRF24E1 includes three timer/counters (Timer 0, Timer 1 and Timer 2). Each timer/counter can operate as either a timer with a clock rate based on the CPU clock or as an event counter clocked by the t0 pin (Timer 0), tl pin (Timer 1). or the t2 pin (Timer 2). These pins are alternate function bits of Port 0 and 1 as this: t0 is P0.5. tl is P0.6 and t2 is P1.0. Each timer/counter consists of a 16-bit register that is accessible to software as three registers: Timer i(1/2/3): TLi (8-bit) and THi(8-bit). Timer 0 can operate in four modes, as controlled through TMOD and TCON registers: 13-bit timer/counter (mode 0) 16-bit timer/counter (mode 1) 8-bit counter with auto-reload (mode 2) Two 8-bit counters (mode 3) Another way to have a good timing is to use a simple 8bits counter, for instance timer 0, and set it in auto-reload mode; this is configured by the value of TMOD register programmed as follow TMOD |=0x02. Bit Function in TMOD Register TMOD.7 GATE Timer 1 gate control. TMOD.6 C/T Counter/Timer 1 select. TMOD.5-4 Timer 1 mode select bit 1. TMOD.3 GATE When GATE = 0, Timer 0 will clock only when TR0=1, regardless of int0_n state. TMOD.2 Counter/Timer select. When C/T = 0. Timer 0 is clocked by CPU_clk/4 or CPU_clk/12, depending on the state of T0M (CKCON.3). When C/T = 1. Timer 0 is clocked by t0 pin. TMOD.1-0 10 Mode 2 : 8-bit counter with auto-reload In mode 2, timer is configured as an 8-bit counter, with automatic reload of start value. The LSB register (TL0) is the counter, and MSB register TH0 stores the reload value. However, in mode 2, when TL0 increments from 0xFF, value stored in TH0 is reloaded into TL0. Therefore, initial value must be placed in TH0 register (8 bits). Actually, initial value is calculated so that overflow occurs every 0.1µs (e.g. 0x7A=122) and stored in TH0 register (8bits). The 133 increases from this value (until 255=28-1) take place each 12 cycles clock as configured by T0M bit (at a low level in register CKCON). This can be made by line code: CKCON &= ~0x08 Bit CKCON.7,6 CKCON.5 CKCON.4 CKCON.3 CKCON.2-0 Function CKCON Register Reserved T2M - Timer 2 clock select. TIM-Timer 1 clock select. T0M - Timer 0 clock select. when T0M = 0. Timer 0 uses CPU_clk/l2 (Reserved)MD2, MD1, MD0, I strongly recommend do not touch To be logic with previous choice made in TMOD with the GATE bit, IT0 bit should be set low. To start the timer, TF0 bit must be set high (TCON |=0x10). Bit Function TCON Register TCON.7-6 TF1 and TR1 for timer 1. TCON.5 TF0 Timer 0 overflow flag. Set to 1 when Timer 0 overflows and cleared when the CPU vectors to the interrupt service routine. TCON.4 TR0 Timer 0 run control. Set to 1 to enable counting on Timer 0. TCON.3-2 IE1 and IT1 interruption for timer 1. TCON. 1 IE0: In level-sensitive mode, software cannot write to IE0. TCON.0 IT0=0, nRF24El detects int0_n as a low level (level-sensitive mode). When an overflow occurs, that is to say every 0.1µs (Timer increment is independent of main program, e.g. every 12 CPU clock), TF0 flag bit is automatically set high. It is seen as an interruption on timer 0. Interruption occurs each 0.1µs; where fast changes on global timing variables by software enable to have access to accurate timer data. NRF24E1 supports the following interrupt sources: Interrupt signal Description TF0 Timer 0 interrupt (counter overflow) int2 Internal ADC EOC (end of AD conversion) interrupt int3 Internal SPI_READY interrupt (finished transaction) int4 Internal RADIO.DR1 interrupt (a packet is ready from receiver 1) When an enabled interrupt occurs, CPU vectors to the address of the Interrupt Service Routine (ISR) associated with that interrupt. Then, CPU executes the ISR to completion unless another interrupt of higher priority occurs (there is only one interruption here). Each ISR ends with a return from interrupt instruction. After executing this, CPU returns to the next instruction that would have been executed if the interrupt had not occurred. An ISR can only be interrupted by a higher priority interrupt. That means ISR for a low-level interrupt can be interrupted only by a high-level interrupt. CPU always completes the instruction in progress before servicing an interrupt. If instruction in progress is RETI. or a write access to any of the IP, IE, EIP, or EIE Registers, CPU completes one additional instruction before servicing the interrupt. Interrupt TF0 int2 int3 int4 Description Natural Priority (lowest number gives highest priority) Timer 0 interrupt 2 ADC_EOC interrupt 8 SPI_READY interrupt 9 RADIO.DR1 interrupt 10 Interrupt Vector 0x0B used 0x43 unused 0x4B unused 0x53 unused here EA bit (IE.7: 8th bit in IE register) is a global enable for all interrupts. When EA = 1, each interrupt is enabled/masked by its individual enable bit. When EA = 0, all interrupts are masked. Next table provides a summary of interrupt sources, flags, enables, and priorities. Interrupt TF0 int2 int3 int4 Description Timer 0 interrupt ADC_E(nd)O(f)C(onversion) interrupt SPI_READY interrupt RADIO.DR1 interrupt Flag TCON.5 EXIF.4 EXIF.5 EXIF.6 Enable IE.l=1 EIE.0 EIE.1 EIE.2 Control IP.1 EIP.0 EIP.1 EIP.2 IE must be initialized at 10000010 (0x82), which is made by this command: ‘IE |=0x82’. This table shows why these flags are raised: Bit Function of these Interrupt Flag TCON.5 TF0 Timer 0 overflow flag. Set high automatically when Timer 0 overflows and cleared when the CPU vectors to the interrupt service routine. EXIF.6 IE4 = 1 indicates that a rising edge was detected on the RADIO.DR1 signal. EXIF.5 IE3 = 1 highlights that SPI module is ready for a new command EXIF.4 IE2 = 1 rising edge detected in ADC_EOC signal, (End of conversion) These bits must be cleared by software. Setting them high in software generates an interrupt, if enabled. All flag bits that don’t need to be interrupt-enabled are listed below: Bit Function EIE.2 EX4 EX4 = 0 disables interrupt 4 (RADIO.DR1). EIE.l EX3 EX3 = 0 disables interrupt 3 (SPI_READY). EIE.0 EX2 EX2 = 0 disables interrupt 2 (ADC_EOC). The interruption handling Sending RF packets: In C, interrupt number is given by (0x0B-3)/8, where 0x0B is the interrupt vector address. Here are some lines of interruption code: void Chronometer(void) interrupt 1 { Interruption occurs only when TF0 overflow counter 0 flag bit has rised, i.e. every 100µs. TF0 = 0; to set low the interrupt flag (overflow in timer 0): it enables another interruption. j++; to count how many times interruptions occurred. Then, we enter in a conditional action. If the packet is ready to transmit and an interrupt occurred there is 600µs ago, if((j%6==0)&&(ready==1)){CE=0;} then, we can start data transmission in ShockBurst®. Every 600µs, a packet is expected to be sent, so that Sample Frequency is >1620Hz (that means our waveform of induced voltage on coil is well sampled, see 3§ below). Step detection: Now, we have to detect steps made by walker, if a step has been counted and the time difference between the time on counted step (timeflag) and now (j) is neg: if ( (flag == 1) && (j < timeflag) ) {if( (timeflag-j) == 3000 ) Then if the step was counted 300ms ago, we can come back to 'listen' another step detection {flag = 0;} (flag highlights ability to detect a step or not: while flag is rise, no step is detected. 300ms correspond to the while during over or underflows can occur. If a step has been counted and the time difference between the count and now is pos and there was 300ms ago: if ((flag == 1) && (j>timeflag)) {if((j-timeflag) == 3000) Then, we come back listening: {flag=0;}} So that disability to detect step lasts only 300ms after a previous step was detected. Time basis: To have an accurate basis of time, here are the lines: if (j==10000) //j is an integer (can go up to 65535): 10000*100µs=1s { j=0; //every second we begin again another cycle time++; //seconds are incremented if(time==59) //when we reach a new minute (time is a char: 255 max) {time=0;}}} //we start another one from the beginning This interruption can be used to send periodically (every 600µs) data (address and payload). A study of a typical signal generated by the shoe-generator (voltage on one coil) during a walk reveals that the maximal frequency of the signal is about 820Hz. A good sampling of this signal is assured if the sampling frequency is about twice this max frequency (1640Hz). Actually, sending data every 600µs is enough to receipt the waveform of this signal. This could be easily achieved by including the control of the bit of sending (CE). By this way, if the counter of the number of interruptions is a multiple of 6 (which happens each 600µs) and if the data to send are ready (which is filled for the complete code controlling the ADC and the Serial Peripheral Interface executes itself in less than 500µs), then the order of data sending is given. The whole interrupt code is entirely executed in few hundred of nanoseconds, so that the accuracy of the timing is maintained. Therefore, time is a reliable variable that gives us a good timing indication. e. Display To display data sampled on the receiver part, some input/output port are used. Here are their related registers: Port 0: P0 P0_ALT P0_DIR I do not use Port 1. The device has 1 general purpose input (DIN0) and 10 general purpose bidirectional pins. These are by default configured as GPIO pins controlled by the ports P0 (DIO2 to DIO9) and P1 (DIO0, DIO1, DIN0) of the microcontroller. Most of the GPIO pins can be used for multiple purposes under program control. Alternate functions include two external interrupts, a SPI master port, three enable/count signals for the timers and a PWM output. The NRF24E1 have two IO ports fully bi-directional CMOS and the direction of each pin is controlled by a _DIR and an _ALT register. During period of internal reset (regardless of whether or not the clock is running), all the port pins are configured as inputs. When program execution starts, the DIO ports are still configured as inputs and program will need to set _ALT and/or _DIR register for pins that should be used as outputs. Name Function P0 Port 0, pins DI09 to DI02 P0_DIR For each bit of Port 0; 0: Output, 1: Input P0_ALT Select alternate functions for each pin of P0 Unused here. P0_ALT and P0_DIR control P0 port function in that order of priority. If the alternate function for port P0.n is set (by P0_ALT.n = 1) the pin will be input or output as required by the alternate function (UART, external interrupt, timer inputs or PWM output. Pin P0.0 (DI02) P0.1 (DI03) P0.2 (DI04) P0.3 (DI05) P0.4 (DI06) P0.5 (DI07) P0.6 (DI08) P0.7 (DI09) P0_DIR= 0x81 P0.0 In P0.1 Out P0.2 Out P0.3 Out P0.4 Out P0.5 Out P0.6 Out P0.7 In 7. WITH COMPONENTS Ordering The main component was not provided by the lab’s supplier, e.g. Farnell®. Nordic Semiconductor, which is the company that makes this component, was in holyday during all July. Therefore, we have some delays expected. Finally, I have made some research on Internet and try a company in UK, CLERE Company, that was found in partnership with Nordic…so that, I email them to see if they stock the NRF24E1. The answer was positive, and I ask them for sending some samples, and they answer very fast. What I’ve learned from this issue is, sometimes, you must do it by yourself, without going trough classic hierarchy when it is necessary, in order to have a faster service. Soldering As the main component, the NRF24E1 is really small (6mm squared and 36 pins), I had some problem to sold it. We refer to the CEL Company, located at Tuam, (10km from Galway) to sold it. Once again, important delays have been encountered 8. WITH UNITS They also have a tendency to measure in inches instead in centimetres, to talk about Fahrenheit degrees instead Celsius. They mix all these unities: we can’t know if our interlocutor is going to use one or another unity. I found very useful a ruler in which both of the unities (cm; inches) are quoted. II. BIBLIOGRAPHY Websites: www.farnell.com www.nordicsemi.no www.rare-earth-magnets.com www.forcefieldmagnets.com www.mmcmagnetics.com Books / Publications: Demonstrator Circuit for Wearable Power Generators (Ronan Crowley) Electromagnetic generators for Power Harvesting (Maeve Duffy & Damien Carroll) Design of a Wearable Generator with a Magnetic Core (Nicolae Popa & Maeve Duffy) Demonstration of Wearable Power Generator (Damien Carroll & Maeve Duffy) Energy Harvesting from Passive Human Power (Loreto Mateu) Energy Harvesting from Passive Human Power (Loreto Mateu) Review of Energy Harvesting Techniques and Applications for Microelectronics (Loreto Mateu & Francesc Mol) Human Generated Power for Mobile Electronics (Thad Starner & Joseph A. Paradiso) Theoretical aspects concerning the calculus possibilities of magnetic fluid core coils (Nicolae Popa) Shoe-mounted Piesoelectrics (IEEE MICRO May/June 2001) Datasheets: NRF24E1 from Nordic® NRF2401 from Nordic® 25AA320 from Microchip® EECS0HD104 from Panasonic® MAX1672 from Maxim® PMEG1020EA from Philips® HLMP-D155 from Agilent Technologies® APPENDIX 9: C-LANGUAGE BASES Memory allocation: Registers or bits addresses are defined in h file (given by the constructor), so that we can use their name as they appear in datasheet directly in program code. Operations: Or (|) and And (&) operations are made bit per bit which rules are 1&1=1, 1|1=1, 1&0=0, 1|0=1, 0&1=0, 0|1=1, 0&0=0, 0|0=0 Bit complementary (~) operation: 0 becomes 1 and vice-versa Specific operator %: x%y will return the rest of Euclidian division of x by y Translation of n bits from the left to the right hand side: (bits to translate) << n. Conversely, use >> from the right to the left hand side translation. Allocation is made by only one sign =: for instance, TH0=0x7A; puts the hexadecimal value 0x7A into TH0 register. (TH0 bits will be 01111010=0x7A) i++; is the same than i=i+1(increasing); n--; is the same than n=n-1(decreasing); (here, i and n are variables). Conversion hexadecimal/binary: Every 4 bits gathered made 1 hex value. In hex, we write from 0-9, then from 10 to 15 translated by A to F, A=10, B=11 … F=15. 1011=1*23+0*22+1*21+1*20=1*8+0*4+1*2+1*1=8+2+1=11=B (max, 15: F, min 0: 0) Conversely, 0xE9=1110(E)1001(9)=11101001. Some examples you will find in my program. TMOD |=0x02; implies that TMOD=TMOD | 0x02;, here only 2nd bit of TMOD is set high, others are unchanged. CKCON &= ~0x08; is the same that CKCON=CKCON & (~0x08); and ~0x08 is 0xF7, here we only change (set low) the 4th bit of CKCON. TCON |=0x10; only the 5th bit is set high, others are unchanged. IE |=0x82; only 8th and 2nd bits are set high here, others are unchanged. Sometimes, 2 operations are needed: ADCCON &= ~0x80; set low last bit of ADCCON, ADCCON |= 0x80; and then set high this same last bit. This allows us to make a toggle on CSTARTN pin to start a sample on ADC input. Cleverer: ADCSTATIC &= 0x1C; we keep previous values of 5th, 4th and 3rd bits and set others low. ADCSTATIC |= 0xA3; then, even if we don’t know actual value of 3 bits (5th,4th, and 3rd) we can change other bits which are in the same register without changing these specific 3 bits. This operation is named a masking, because some bits are voluntarily ‘hidden’ for changing. Special actions: Conditional actions: Using if(condition) {action1;} else{action2;} will execute action1 only if condition is verified otherwise action2 will be done. A condition is often a double equality: x == y; if x has the same value than y, therefore this expression is seen as a ‘Boolean 1’, so that condition is verified. If y is 1, condition could be simply x. In a condition, logical signs like & (and) or | (or) are repeated twice. Parentheses are used to show which operations has priority. Condition x different from y is written: x != y. Waiting sequences: do{action;}while(condition); will execute action until condition is not anymore verified. while(condition); will do nothing until condition is verified. Loops: for(i=1;i<N;i++) {action;} will execute N times action; i is initialized at 1(i=1) and is incremented (i++) until i<N is no more verified. Beware with infinite loops: do{…} while(1) or for(;;){…} will generate infinite sequences, which can cause abortion of program or incorrect end of thread when there are not handled carefully. Functions: C file is organized in functions in which local variables are defined (to be used only in this function). In head of file are defined global variables, which can be accessed by any function in C file. Program will start executing main function (in which must be placed all initialisations), defined as follow: void main(void) {…} Interrupt function is special, because it will stop program, execute itself, then return to execute program from where it was stopped. Parameters: Functions can have parameters, so that when they are used with a specific parameter, specific actions will be done. Function can return only one result, which type is defined in function definition: int(eger on 16 bits), char(acter ASCII on 8 bits), long (32 bits) (unsigned or not, depends on memory reservation that the µcontroller can afford)… void is the term used when there is no parameter returned by (or given to) function. Words are predefined in c language: typedef struct will enable building several kinds of variables in only one. ‘txconf’ is, for instance, a character (n) and a vector (buf) of characters (see Appendix 10). Accessing to one or other variable is made by selecting txconf.n or txconf.buf. Then, to access nth variable in txconf.buf vector, txconf.buf[n] will do the job APPENDIX 10: PROGRAM CODE FOR TRANSMITTER pedo2.c 1 /* Author: LE BORGNE Guillaume Training period : summer 2006 2 * After initializing the ADC on desired channels 0-3, radio & timer, the transmitter 3 * reads the ADC and transmits the read data (16bits) : differential value AIN1-AIN0 & AIN2 (& AIN3) 4 * Then it sends the time elapsed since the program has started, thanks to Shockburst method... 5 ***********************************************************************************/ 6 #include <reg24e1.h> //definition of SFR registers 7 #define ADDR_INDEX 5 // Index to address bytes in RFConfig.buf 8 #define ADDR_COUNT 1 // Number of address bytes 9 //definition of the structure of word configuration 10 struct RFConfig 11 { unsigned char n; 12 unsigned char buf[9];}; 13 typedef struct RFConfig RFConfig; 14 //Configuration word for transmitter 15 const RFConfig txconf ={ 9, 16 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x23, 0x6c, 0x04}; 17 // data2 width / data1 width / ADDR2 / ADDR1 /ADDR width/CRC16bits enabled /only 1 channel /Shockburst @16Mhz /Power@-20dB/ Channel 2.402GHz/transmission 18 //avoid "A" & "5" in address 19 20 8its 21 22 23 24 25 26 27 28 unsigned int j=0,timeflag=0; unsigned char time,ready,flag; //an int can go up to 65535 16bits //a char can go up to 255 //interrupt handle each 100us during = 0.1us void Chronometer(void) interrupt 1 {//the interrupt number is given by (0x0B-3)/8, where 0x0B is the interrupt vector address TF0 = 0; //clear the interrupt flag overflow in timer 0 j++; if ( ( (j%6) == 0) && (ready == 1) ) {CE = 0;} //start of data to transmit in ShockBurst every 600us 29 if ( (flag == 1) && (j < timeflag) )//if a step has been counted and the time difference between the count and now is neg 30 31 32 { if( (timeflag-j) == 3000 ) //and there was 300ms ago {flag = 0;}} //come back to 'listen' if ((flag == 1) && (j>timeflag)) //if a step has been counted and the time difference between the count and now is pos 33 34 35 36 37 38 39 40 42 43 44 45 47 48 { if((j-timeflag) == 3000) {flag=0;}} //and there was 300ms ago //come back to 'listen' //so that the global Sample Frequency is >1620Hz (signal of voltage induced well sampled) if (j==10000) { j=0; time++; //sec incrementing if(time==59) {time=0;}}} //we described every sec 41 void Delay100us(volatile unsigned char n) { unsigned char i; while(n--) for(i=0;i<35;i++);} 46 unsigned char SpiReadWrite(unsigned char b) { EXIF &= ~0x20; // Clear SPI interrupt (ie IE3 cleared: flag showing data was sent or received) 49 50 51 52 SPI_DATA = b; // while(IE3!=1); while((EXIF & 0x20) == return SPI_DATA;} // Move byte to send to SPI data register //SPI ready (EXIF & 0x20=IE3) 0x00); // Wait until SPI has finished transmitting //access to in or out put data 53 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 79 80 void Init(void) { unsigned char g; //interrupt init IE |= 0x82; //interrupt from timer0 enabled //init radio PWR_UP = 1; Delay100us(30); SPICLK = 0x00; SPI_CTRL = 0x02; // Turn on Radio // Wait > 3ms // Max SPI clock (XTAL/8) // Connect internal SPI controller to Radio //settings for the transmitter CS = 1; Delay100us(0); for(g=0;g<txconf.n;g++) {SpiReadWrite(txconf.buf[g]);} CS = 0; //enable configuration mode //sending configuration word to the transmitter //disable configuration mode //done for each power up (reset of all data) //init timer 0 in auto reload mode(2), CPU/12 clock //133*7.5e-7=100us therefore an incrementation of 133 from 122= 0111 1010 =0x7A CKCON &= ~0x08; //T0M=0 => CPU/12 clk TMOD |= 0x02; //Gate=0, C/T=0, Mode 2 TH0 = 0x7A; //initial value @ 122 TCON |= 0x10; //set TR0 high : timer 0 starts }//TF0 will set high each time timer 0 will be in overflow and causes an interruption 78 void InitADC(unsigned int c) { if (c==2) {ADCCON = 0x22;} // Analog Input=2, NPD=1, ADCRUN=0, EXTREF=0 81 if (c==3) {ADCCON = 0x23;} EXTREF=0 82 // if (c==4) {ADCCON = 0x24;} EXTREF=0 ... 83 // if (c==7) {ADCCON = 0x27;} 84 85 86 87 88 ADCSTATIC &= 0x1c; ADCSTATIC |= 0x23;} // Analog Input=3, NPD=1, ADCRUN=0, // Analog Input=4, NPD=1, ADCRUN=0, // Analog Input=7, NPD=1, ADCRUN=0, EXTREF=0 // we don't touch to these 3bits // highest frequency (2) and 12bits resolution(3) void InitDiffADC(void) {// Analog Input=0 will be used as inverting input, others as non-inverting inputs, NPD=1, ADCRUN=0, EXTREF=0 89 ADCCON = 0x21; // Analog Input=1, NPD=1, ADCRUN=0, EXTREF=0 90 91 ADCSTATIC &= 0x1c; // we don't touch to these 3bits ADCSTATIC |= 0xA3;} // highest frequency & differential mode enabled (A) and 12bits resolution(3) 92 93 void Conversion(void) //on start, CSTARTN is high initially 94 { ADCCON &= ~0x80; // Start.. (toggle on CSTARTN) 95 ADCCON |= 0x80;} // ..new conversion 96 // IE2 will set automatically high at the end of the conversion 97 98 unsigned char ReadADC(unsigned int w) 99 { unsigned char b; 100 while((EXIF & 0x10) == 0); // Wait until ADC conversion is complete : ADC_EOC signal 101 102 103 EXIF &= ~0x10; if(w==0) {b = ADCDATAH;} if(w==1) {b = ADCDATAL;} ...) 104 return b;} 105 // Clear ADC completion bit // Read ADC data 8bits MSB // Read ADC data 4bits LSB + details(overflow, 112 113 114 115 116 117 118 void main(void) { unsigned char a,k,step; j = 0; timeflag = 0; time = 0; ready = 0; flag = 0; //interrupt counter //sensibility of counting steps //number of second to be send //go for shockburst sending data // say if an increment of step number is possible 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 step=0; Init(); for(;;) { if(ready==0) {Delay100us(1); InitDiffADC(); //interrupt radio timer init // Wait ~100us // differential measurement AIN1 - AIN0 //between only 1 coil ends before rectifier stage Vmax<Vref=1.22V Conversion(); // Loading of data to transmit CE = 1; // transmission mode on Delay100us(0); for(k=0;k<ADDR_COUNT;k++) // Start with the address of the receiver: {SpiReadWrite(txconf.buf[ADDR_INDEX+k]);} a = ReadADC(1); // Read ADC LSB // Step counting if(((a & 0x01)==1)&&(flag==0)) // if there is under or over flow and the possibility of counting steps 135 136 { step = 0x01; flag=1; // detect one step // become insensible detecting a step timeflag=j;} // record of time during 300ms 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 // Continue loading of data to transmit InitADC(3); Conversion(); a = ReadADC(0); SpiReadWrite(a); a = ReadADC(1); k = time; a &= 0xF0; a |= ((k & 0x38)>>2); a |= step; step = 0; SpiReadWrite(a); InitADC(2); Conversion(); a = ReadADC(0); SpiReadWrite(a); a = ReadADC(1); a &= 0xF1; a |= ((k & 0x07)<<1); SpiReadWrite(a); ready = 1;}}} // measure AIN3; diffm disabled // Read ADC MSB // loading // Read ADC LSB //time doesn't have to change // reset 4 bits LSB // change 3 bits // say if there is or not one more step // count only one time the step once detected // loading // measure AIN2; diffm disabled // Read ADC MSB // loading // Read ADC LSB // the other bits are low // change 3 bits of a // loading // address & payload ready APPENDIX 11: PROGRAM CODE FOR RECEIVER receiver2.c 1 2 /* Author: LE BORGNE Guillaume Training period : summer 2006 * After initializing the the port P0 & the radio, the receiver has the read data (48bits) thanks to Shockburst method...*/ 3 #include <reg24e1.h> //definition of SFR registers 4 #define ADDR_INDEX 5 // Index to address bytes in RFConfig.buf 5 #define ADDR_COUNT 1 // Number of address bytes 6 //definition of the structure of word configuration 7 8 9 10 11 12 13 14 struct RFConfig { unsigned char n; unsigned char buf[9];}; typedef struct RFConfig RFConfig; //Configuration word for receiver const RFConfig rxconf ={ 9, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x23, 0x6c, 0x05}; // data2 width / data1 width / ADDR2 / ADDR1 /ADDR width/CRC16bits enabled /only 1 channel /Shockburst @16Mhz /Power@-20dB/ Channel 2.402GHz/reception 15 unsigned int step=0; //an int can go up to 65535 16 17 18 19 20 21 22 23 24 unsigned char sec,m,h; //a char can go up to 256 void Delay100us(volatile unsigned char n) { unsigned char i; while(n--) for(i=0;i<35;i++);} unsigned char SpiReadWrite(unsigned char b) { EXIF &= ~0x20; // Clear SPI interrupt (i.e. IE3 cleared : flag showing data was sent or received) 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 SPI_DATA = b; // while(IE3!=1); while((EXIF & 0x20) == 0x00); return SPI_DATA;} // Move byte to send to SPI data register //SPI ready (EXIF & 0x20=IE3) // Wait until SPI has finished transmitting //access to in or out put data void Init(void) { unsigned char b; //port 0 P0_DIR = 0x81; P0 &=0x81; //pin 0.1 to P0.6 output //init radio PWR_UP = 1; Delay100us(30); SPICLK = 0x00; SPI_CTRL = 0x02; // Turn on Radio // Wait > 3ms // Max SPI clock (XTAL/8) // Connect internal SPI controller to Radio //settings of the receiver CS = 1; Delay100us(0); for(b=0;b<rxconf.n;b++) {SpiReadWrite(rxconf.buf[b]);} CS = 0;} unsigned long ReceivePacket() { unsigned char a,b,c; unsigned long x; CE = 1; //configuration enabled //configuration disabled //receive mode enabled 51 52 53 54 while(DR1 == 0); x = SpiReadWrite(0); a = SpiReadWrite(0); b = SpiReadWrite(0); 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 c = SpiReadWrite(0); CE = 0; //received mode disabled x <<= 8; x = x | a; x <<= 8; // stack x = x | b; x <<= 8; x = x | c; sec = (((x & 0x000E0000)>>13)|(x & 0x0000000E)); sec >>= 1; if (sec==59) { sec=0; m++; //Time increment if (m==60) { m=0; h++;}} if (((x & 0x00010000)>>15) == 2);//Step increment {step=step+2;} return x;} //data received //waiting end of data flux //reading data flux void Receiver(void) { unsigned long y; do { y = ReceivePacket(); //start of checking that can be removed later do { if ((P0>>7)==1) {P0 = (y & 0xFC000000)>>25;} else {P0 = (y & 0x03F00000)>>19;} y = ReceivePacket(); }while(sec<20); do { if( (P0>>7)==1 ) {P0 = (y & 0x0000FC00)>>9;} else {P0 = (y & 0x000003F0)>>3;} y = ReceivePacket(); }while(sec<40); //MSB AIN3 //LSB AIN3 //receipt payload bits //MSB AIN2 //LSB AIN2 //receipt payload bits //end of checking part that can be removed later do { if((P0>>7)==1) {P0 = (sec << 1);} else {P0 = ((step & 0x3F) << 1);} y = ReceivePacket(); }while(sec!=59); }while(1);} //time elapsed displayed // number of steps displayed //receipt payload bits 105 106 107 108 109 110 111 void main(void) { sec = 0; step = 0; m = 0; h = 0; Init(); Receiver();} APPENDIX 12: NRF24E1 H FILE /* -----------------------------------------------* reg24e1.h Header file for the Nordic VLSI nRF24E1 2.4Ghz RF transceiver with embedded 8051 compatible microcontroller. $Revision: 1 $ * ------------------------------------------------ */ #ifndef __REG24E1_H__ #define __REG24E1_H__ /* BYTE Registers */ sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr sfr P0 TCON TMOD TH0 CKCON EXIF IE IP EICON EIE EIP P0_DIR P0_ALT RADIO ADCCON ADCDATAH ADCDATAL ADCSTATIC SPI_DATA SPI_CTRL SPICLK CK_CTRL = = = = = = = = = = = = = = = = = = = = = = 0x80; 0x88; 0x89; 0x8C; 0x8E; 0x91; 0xA8; 0xB8; 0xD8; 0xE8; 0xF8; 0x94; 0x95; 0xA0; 0xA1; 0xA2; 0xA3; 0xA4; 0xB2; 0xB3; 0xB4; 0xB6; /* BIT Registers */ /* TCON */ sbit sbit sbit sbit TF0 TR0 IE0 IT0 = = = = TCON^5; TCON^4; TCON^1; TCON^0; /* IE */ sbit EA = IE^7; /* RADIO */ sbit PWR_UP sbit CE sbit CS sbit DR1 sbit CLK1 sbit DATA #endif = = = = = = RADIO^7; RADIO^6; RADIO^3; RADIO^2; RADIO^1; RADIO^0; APPENDIX 13: SCHEMATICS FOR TRANSMITTER APPENDIX 14: SCHEMATICS FOR RECEIVER