Code for Interfacing with Texas Instruments' AFE4404 IC for photoplethysmography applications

Dependents:   ece4180_project

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?

UserRevisionLine numberNew 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 }