Code for Interfacing with Texas Instruments' AFE4404 IC for photoplethysmography applications
AFE_4404.cpp@0:e9068fdddb58, 2017-05-04 (annotated)
- Committer:
- dotunhunter
- Date:
- Thu May 04 11:56:07 2017 +0000
- Revision:
- 0:e9068fdddb58
Initial commit; Code for interfacing with Texas Instruments' AFE4404 photoplethysmography IC.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
dotunhunter | 0:e9068fdddb58 | 1 | #include "mbed.h" |
dotunhunter | 0:e9068fdddb58 | 2 | #include "AFE_4404.h" |
dotunhunter | 0:e9068fdddb58 | 3 | |
dotunhunter | 0:e9068fdddb58 | 4 | |
dotunhunter | 0:e9068fdddb58 | 5 | char LED = 0x2A; // LED2 on AFE4404 |
dotunhunter | 0:e9068fdddb58 | 6 | int32_t data; |
dotunhunter | 0:e9068fdddb58 | 7 | volatile bool dataAvailable = false; |
dotunhunter | 0:e9068fdddb58 | 8 | |
dotunhunter | 0:e9068fdddb58 | 9 | |
dotunhunter | 0:e9068fdddb58 | 10 | AFE_4404::AFE_4404(PinName rxSupplyEn, PinName txSupplyEn, PinName resetz, |
dotunhunter | 0:e9068fdddb58 | 11 | PinName powerEn, PinName drdy, PinName clk, PinName sda, PinName scl): |
dotunhunter | 0:e9068fdddb58 | 12 | |
dotunhunter | 0:e9068fdddb58 | 13 | _rxSupplyEn(rxSupplyEn), _txSupplyEn(txSupplyEn), _resetz(resetz), |
dotunhunter | 0:e9068fdddb58 | 14 | _powerEn(powerEn), _drdy(drdy), _clk(clk), _i2c(sda, scl) { |
dotunhunter | 0:e9068fdddb58 | 15 | // inputs: |
dotunhunter | 0:e9068fdddb58 | 16 | // rxSupplyEn, txSuppyEn, and powerEn: digital outputs (active low) used to |
dotunhunter | 0:e9068fdddb58 | 17 | // turn on the power supplies in the manner described in the datasheet |
dotunhunter | 0:e9068fdddb58 | 18 | // resetz: used to reset the AFE after power up (active low) |
dotunhunter | 0:e9068fdddb58 | 19 | // drdy: interrupt from AFE when data is ready |
dotunhunter | 0:e9068fdddb58 | 20 | // clk: pwm output at ~ 4.1MHz |
dotunhunter | 0:e9068fdddb58 | 21 | // sda, scl: I2C data and clock, respectively; |
dotunhunter | 0:e9068fdddb58 | 22 | |
dotunhunter | 0:e9068fdddb58 | 23 | // shift by 1 for 8-bit representation of 7-bit address |
dotunhunter | 0:e9068fdddb58 | 24 | _address = (0x58 << 1); |
dotunhunter | 0:e9068fdddb58 | 25 | } |
dotunhunter | 0:e9068fdddb58 | 26 | |
dotunhunter | 0:e9068fdddb58 | 27 | void AFE_4404::initPorts(void) { |
dotunhunter | 0:e9068fdddb58 | 28 | |
dotunhunter | 0:e9068fdddb58 | 29 | // turn off power supplies |
dotunhunter | 0:e9068fdddb58 | 30 | _rxSupplyEn = 0; |
dotunhunter | 0:e9068fdddb58 | 31 | _txSupplyEn = 0; |
dotunhunter | 0:e9068fdddb58 | 32 | _powerEn = 0; |
dotunhunter | 0:e9068fdddb58 | 33 | |
dotunhunter | 0:e9068fdddb58 | 34 | // resetz is active low, so leave on before power supply init |
dotunhunter | 0:e9068fdddb58 | 35 | _resetz = 1; |
dotunhunter | 0:e9068fdddb58 | 36 | |
dotunhunter | 0:e9068fdddb58 | 37 | // set the clock output to zero before power-up sequence |
dotunhunter | 0:e9068fdddb58 | 38 | // this convoluted method was required because of the way the the PWM |
dotunhunter | 0:e9068fdddb58 | 39 | // output is set up (faster that possible with the MBED APIs) |
dotunhunter | 0:e9068fdddb58 | 40 | _clk.period(10); |
dotunhunter | 0:e9068fdddb58 | 41 | _clk.write(0); |
dotunhunter | 0:e9068fdddb58 | 42 | |
dotunhunter | 0:e9068fdddb58 | 43 | disableIRQ(); |
dotunhunter | 0:e9068fdddb58 | 44 | } |
dotunhunter | 0:e9068fdddb58 | 45 | |
dotunhunter | 0:e9068fdddb58 | 46 | void AFE_4404::initPowerSupply(void) { |
dotunhunter | 0:e9068fdddb58 | 47 | |
dotunhunter | 0:e9068fdddb58 | 48 | wait_ms(100); |
dotunhunter | 0:e9068fdddb58 | 49 | |
dotunhunter | 0:e9068fdddb58 | 50 | _powerEn = 1; |
dotunhunter | 0:e9068fdddb58 | 51 | wait_ms(100); |
dotunhunter | 0:e9068fdddb58 | 52 | |
dotunhunter | 0:e9068fdddb58 | 53 | _rxSupplyEn = 1; |
dotunhunter | 0:e9068fdddb58 | 54 | wait_ms(10); |
dotunhunter | 0:e9068fdddb58 | 55 | |
dotunhunter | 0:e9068fdddb58 | 56 | _txSupplyEn = 1; |
dotunhunter | 0:e9068fdddb58 | 57 | wait_ms(20); |
dotunhunter | 0:e9068fdddb58 | 58 | |
dotunhunter | 0:e9068fdddb58 | 59 | _resetz = 0; |
dotunhunter | 0:e9068fdddb58 | 60 | wait_us(35); |
dotunhunter | 0:e9068fdddb58 | 61 | |
dotunhunter | 0:e9068fdddb58 | 62 | _resetz = 1; |
dotunhunter | 0:e9068fdddb58 | 63 | |
dotunhunter | 0:e9068fdddb58 | 64 | initClock(); |
dotunhunter | 0:e9068fdddb58 | 65 | wait_ms(2); |
dotunhunter | 0:e9068fdddb58 | 66 | } |
dotunhunter | 0:e9068fdddb58 | 67 | |
dotunhunter | 0:e9068fdddb58 | 68 | uint32_t AFE_4404::readData(uint8_t reg, bool adc = true) { |
dotunhunter | 0:e9068fdddb58 | 69 | |
dotunhunter | 0:e9068fdddb58 | 70 | if (!adc) { |
dotunhunter | 0:e9068fdddb58 | 71 | enableReadMode(); |
dotunhunter | 0:e9068fdddb58 | 72 | } |
dotunhunter | 0:e9068fdddb58 | 73 | |
dotunhunter | 0:e9068fdddb58 | 74 | _writeBuffer[0] = reg; // initialize write buffer with AFE register address |
dotunhunter | 0:e9068fdddb58 | 75 | |
dotunhunter | 0:e9068fdddb58 | 76 | // initialize read buffers to 0. probably unnecessary |
dotunhunter | 0:e9068fdddb58 | 77 | _readBuffer[0] = 0x00; |
dotunhunter | 0:e9068fdddb58 | 78 | _readBuffer[1] = 0x00; |
dotunhunter | 0:e9068fdddb58 | 79 | _readBuffer[2] = 0x00; |
dotunhunter | 0:e9068fdddb58 | 80 | |
dotunhunter | 0:e9068fdddb58 | 81 | // write the register to AFE and use repeated start mode as specified in |
dotunhunter | 0:e9068fdddb58 | 82 | // the datasheet |
dotunhunter | 0:e9068fdddb58 | 83 | _i2c.write(_address, _writeBuffer, 1, true); |
dotunhunter | 0:e9068fdddb58 | 84 | // read 3 bytes of data from register MSB first |
dotunhunter | 0:e9068fdddb58 | 85 | _i2c.read(_address, _readBuffer, 3); |
dotunhunter | 0:e9068fdddb58 | 86 | |
dotunhunter | 0:e9068fdddb58 | 87 | _tempData = 0; |
dotunhunter | 0:e9068fdddb58 | 88 | _tempData = (_readBuffer[0] << (BITS_PER_BYTE * 2)) | \ |
dotunhunter | 0:e9068fdddb58 | 89 | (_readBuffer[1] << BITS_PER_BYTE) | _readBuffer[2]; |
dotunhunter | 0:e9068fdddb58 | 90 | |
dotunhunter | 0:e9068fdddb58 | 91 | if (adc && (SIGN_MASK & _tempData)) { |
dotunhunter | 0:e9068fdddb58 | 92 | _tempData |= SIGN_EXT; |
dotunhunter | 0:e9068fdddb58 | 93 | } |
dotunhunter | 0:e9068fdddb58 | 94 | |
dotunhunter | 0:e9068fdddb58 | 95 | return _tempData; |
dotunhunter | 0:e9068fdddb58 | 96 | |
dotunhunter | 0:e9068fdddb58 | 97 | } |
dotunhunter | 0:e9068fdddb58 | 98 | |
dotunhunter | 0:e9068fdddb58 | 99 | void AFE_4404::writeData(uint8_t reg, uint32_t data) { |
dotunhunter | 0:e9068fdddb58 | 100 | |
dotunhunter | 0:e9068fdddb58 | 101 | enableWriteMode(); |
dotunhunter | 0:e9068fdddb58 | 102 | |
dotunhunter | 0:e9068fdddb58 | 103 | _writeBuffer[0] = reg; |
dotunhunter | 0:e9068fdddb58 | 104 | |
dotunhunter | 0:e9068fdddb58 | 105 | // store the lower 3 bytes of data in _writeBuffer (MSB first) |
dotunhunter | 0:e9068fdddb58 | 106 | for (int i = 2, j = 1; i >= 0; i--, j++) { |
dotunhunter | 0:e9068fdddb58 | 107 | _writeBuffer[j] = (data >> (BITS_PER_BYTE * i)) & LOWER_BYTE_MASK; |
dotunhunter | 0:e9068fdddb58 | 108 | } |
dotunhunter | 0:e9068fdddb58 | 109 | |
dotunhunter | 0:e9068fdddb58 | 110 | // write 4 bytes |
dotunhunter | 0:e9068fdddb58 | 111 | // 1 for the register address and 3 for the lower 3 bytes of data |
dotunhunter | 0:e9068fdddb58 | 112 | _i2c.write(_address, _writeBuffer, 4); |
dotunhunter | 0:e9068fdddb58 | 113 | |
dotunhunter | 0:e9068fdddb58 | 114 | } |
dotunhunter | 0:e9068fdddb58 | 115 | |
dotunhunter | 0:e9068fdddb58 | 116 | struct Register { |
dotunhunter | 0:e9068fdddb58 | 117 | uint8_t addr; |
dotunhunter | 0:e9068fdddb58 | 118 | uint32_t val; |
dotunhunter | 0:e9068fdddb58 | 119 | }; |
dotunhunter | 0:e9068fdddb58 | 120 | |
dotunhunter | 0:e9068fdddb58 | 121 | void AFE_4404::initRegisters(void) { |
dotunhunter | 0:e9068fdddb58 | 122 | |
dotunhunter | 0:e9068fdddb58 | 123 | unsigned char i; |
dotunhunter | 0:e9068fdddb58 | 124 | struct Register reg[NUM_REGISTERS]; |
dotunhunter | 0:e9068fdddb58 | 125 | reg[0].addr = 0x01; reg[0].val = 0x000050; |
dotunhunter | 0:e9068fdddb58 | 126 | reg[1].addr = 0x02; reg[1].val = 0x00018F; |
dotunhunter | 0:e9068fdddb58 | 127 | reg[2].addr = 0x03; reg[2].val = 0x000320; |
dotunhunter | 0:e9068fdddb58 | 128 | reg[3].addr = 0x04; reg[3].val = 0x0004AF; |
dotunhunter | 0:e9068fdddb58 | 129 | reg[4].addr = 0x05; reg[4].val = 0x0001E0; |
dotunhunter | 0:e9068fdddb58 | 130 | reg[5].addr = 0x06; reg[5].val = 0x00031F; |
dotunhunter | 0:e9068fdddb58 | 131 | reg[6].addr = 0x07; reg[6].val = 0x000370; |
dotunhunter | 0:e9068fdddb58 | 132 | reg[7].addr = 0x08; reg[7].val = 0x0004AF; |
dotunhunter | 0:e9068fdddb58 | 133 | reg[8].addr = 0x09; reg[8].val = 0x000000; |
dotunhunter | 0:e9068fdddb58 | 134 | reg[9].addr = 0x0A; reg[9].val = 0x00018F; |
dotunhunter | 0:e9068fdddb58 | 135 | reg[10].addr = 0x0B; reg[10].val = 0x0004FF; |
dotunhunter | 0:e9068fdddb58 | 136 | reg[11].addr = 0x0C; reg[11].val = 0x00063E; |
dotunhunter | 0:e9068fdddb58 | 137 | reg[12].addr = 0x0D; reg[12].val = 0x000198; |
dotunhunter | 0:e9068fdddb58 | 138 | reg[13].addr = 0x0E; reg[13].val = 0x0005BB; |
dotunhunter | 0:e9068fdddb58 | 139 | reg[14].addr = 0x0F; reg[14].val = 0x0005C4; |
dotunhunter | 0:e9068fdddb58 | 140 | reg[15].addr = 0x10; reg[15].val = 0x0009E7; |
dotunhunter | 0:e9068fdddb58 | 141 | reg[16].addr = 0x11; reg[16].val = 0x0009F0; |
dotunhunter | 0:e9068fdddb58 | 142 | reg[17].addr = 0x12; reg[17].val = 0x000E13; |
dotunhunter | 0:e9068fdddb58 | 143 | reg[18].addr = 0x13; reg[18].val = 0x000E1C; |
dotunhunter | 0:e9068fdddb58 | 144 | reg[19].addr = 0x14; reg[19].val = 0x00123F; |
dotunhunter | 0:e9068fdddb58 | 145 | reg[20].addr = 0x15; reg[20].val = 0x000191; |
dotunhunter | 0:e9068fdddb58 | 146 | reg[21].addr = 0x16; reg[21].val = 0x000197; |
dotunhunter | 0:e9068fdddb58 | 147 | reg[22].addr = 0x17; reg[22].val = 0x0005BD; |
dotunhunter | 0:e9068fdddb58 | 148 | reg[23].addr = 0x18; reg[23].val = 0x0005C3; |
dotunhunter | 0:e9068fdddb58 | 149 | reg[24].addr = 0x19; reg[24].val = 0x0009E9; |
dotunhunter | 0:e9068fdddb58 | 150 | reg[25].addr = 0x1A; reg[25].val = 0x0009EF; |
dotunhunter | 0:e9068fdddb58 | 151 | reg[26].addr = 0x1B; reg[26].val = 0x000E15; |
dotunhunter | 0:e9068fdddb58 | 152 | reg[27].addr = 0x1C; reg[27].val = 0x000E1B; |
dotunhunter | 0:e9068fdddb58 | 153 | reg[28].addr = 0x1D; reg[28].val = 0x009C3F; |
dotunhunter | 0:e9068fdddb58 | 154 | reg[29].addr = 0x1E; reg[29].val = 0x000103; |
dotunhunter | 0:e9068fdddb58 | 155 | reg[30].addr = 0x20; reg[30].val = 0x008003; |
dotunhunter | 0:e9068fdddb58 | 156 | reg[31].addr = 0x21; reg[31].val = 0x000003; |
dotunhunter | 0:e9068fdddb58 | 157 | reg[32].addr = 0x22; reg[32].val = 0x000400; |
dotunhunter | 0:e9068fdddb58 | 158 | reg[33].addr = 0x23; reg[33].val = 0x000000; |
dotunhunter | 0:e9068fdddb58 | 159 | reg[34].addr = 0x32; reg[34].val = 0x00155F; |
dotunhunter | 0:e9068fdddb58 | 160 | reg[35].addr = 0x33; reg[35].val = 0x00991F; |
dotunhunter | 0:e9068fdddb58 | 161 | reg[36].addr = 0x36; reg[36].val = 0x000190; |
dotunhunter | 0:e9068fdddb58 | 162 | reg[37].addr = 0x37; reg[37].val = 0x00031F; |
dotunhunter | 0:e9068fdddb58 | 163 | |
dotunhunter | 0:e9068fdddb58 | 164 | for (i = 0; i < NUM_REGISTERS; i++) |
dotunhunter | 0:e9068fdddb58 | 165 | writeData(reg[i].addr, reg[i].val); |
dotunhunter | 0:e9068fdddb58 | 166 | |
dotunhunter | 0:e9068fdddb58 | 167 | } |
dotunhunter | 0:e9068fdddb58 | 168 | |
dotunhunter | 0:e9068fdddb58 | 169 | void AFE_4404::initClock(void) { |
dotunhunter | 0:e9068fdddb58 | 170 | |
dotunhunter | 0:e9068fdddb58 | 171 | LPC_PWM1->TCR = (1 << 1); // Reset counter, disable PWM |
dotunhunter | 0:e9068fdddb58 | 172 | LPC_SC->PCLKSEL0 &= ~(0x3 << 12); |
dotunhunter | 0:e9068fdddb58 | 173 | LPC_SC->PCLKSEL0 |= (1 << 12); // Set peripheral clock divider to /1, i.e. system clock |
dotunhunter | 0:e9068fdddb58 | 174 | LPC_PWM1->MR0 = 22; // Match Register 0 is shared period counter for all PWM1 |
dotunhunter | 0:e9068fdddb58 | 175 | LPC_PWM1->MR6 = 11; // Pin 21 is PWM output 6, so Match Register 6 |
dotunhunter | 0:e9068fdddb58 | 176 | LPC_PWM1->LER |= 1; // Start updating at next period start |
dotunhunter | 0:e9068fdddb58 | 177 | LPC_PWM1->TCR = (1 << 0) || (1 << 3); // Enable counter and PWM |
dotunhunter | 0:e9068fdddb58 | 178 | } |
dotunhunter | 0:e9068fdddb58 | 179 | |
dotunhunter | 0:e9068fdddb58 | 180 | void AFE_4404::powerUpSequence(void) { |
dotunhunter | 0:e9068fdddb58 | 181 | |
dotunhunter | 0:e9068fdddb58 | 182 | initPorts(); |
dotunhunter | 0:e9068fdddb58 | 183 | initPowerSupply(); |
dotunhunter | 0:e9068fdddb58 | 184 | initRegisters(); |
dotunhunter | 0:e9068fdddb58 | 185 | initClock(); |
dotunhunter | 0:e9068fdddb58 | 186 | _drdy.rise(this, &AFE_4404::getData); |
dotunhunter | 0:e9068fdddb58 | 187 | enableIRQ(); |
dotunhunter | 0:e9068fdddb58 | 188 | |
dotunhunter | 0:e9068fdddb58 | 189 | } |
dotunhunter | 0:e9068fdddb58 | 190 | |
dotunhunter | 0:e9068fdddb58 | 191 | void AFE_4404::getData(void) { |
dotunhunter | 0:e9068fdddb58 | 192 | |
dotunhunter | 0:e9068fdddb58 | 193 | disableIRQ(); |
dotunhunter | 0:e9068fdddb58 | 194 | data = static_cast<int32_t> (readData(LED, true)); |
dotunhunter | 0:e9068fdddb58 | 195 | dataAvailable = true; |
dotunhunter | 0:e9068fdddb58 | 196 | enableIRQ(); |
dotunhunter | 0:e9068fdddb58 | 197 | } |