/* * AD7280A Lithium Ion Battery Monitoring System * * Copyright 2011 Analog Devices Inc. * * Licensed under the GPL-2. */ #include #include "ad7280a.h" #include "spi2.h" #include "delay.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ ad7280_state ad7280; /* Private function prototypes -----------------------------------------------*/ static void ad7280_crc8_build_table(u8 *crc_tab); /* Private functions ---------------------------------------------------------*/ static void ad7280a_gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //Enables GPIOB clock RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); //Enables GPIOD clock //GPIOD8, D10 initialization settings GPIO_InitStructure.GPIO_Pin = AD_CHIP_PD|AD_CHIP_CONV; //PD8, PD10 function output GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Multiplexing function GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //Pull-up GPIO_Init(GPIOD, &GPIO_InitStructure); //Initialize //GPIOB12 initialization settings GPIO_InitStructure.GPIO_Pin = AD_CHIP_CS; //PB12 function output GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Multiplexing function GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull output GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //Pull-up GPIO_Init(GPIOB, &GPIO_InitStructure); //Initialize //GPIOD9 initialization settings GPIO_InitStructure.GPIO_Pin = AD_CHIP_ALERT; //PD9 function output GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //Multiplexing function GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //Pull-up GPIO_Init(GPIOD, &GPIO_InitStructure); //Initialize AD7280A_CS_HIGH; AD7280A_PD_HIGH; } void ad7280_init(void) { OS_ERR err; s32 data = -1; ad7280a_gpio_init(); memset(&ad7280, 0x00, sizeof(ad7280)); OSTimeDlyHMSM(0, 0, 2, 0, OS_OPT_TIME_HMSM_STRICT, &err); while (1) { AD7280A_CS_HIGH; //SPI2 FLASH is not selected SPI2_Init(); //Initialize the SPI2 SPI2_SetSpeed(SPI_BaudRatePrescaler_64); //Set to 42M clock high-speed mode memset(&ad7280, 0x00, sizeof(ad7280_state)); ad7280_crc8_build_table(ad7280.crc_tab); data = ad7280_chain_setup(); if (data == 3) { printf("\r\nInitialized AD7280A (n = %d)", data); break; } else { // printf("\r\nInitialize fail AD7280A (n = %d)", data); } OSTimeDlyHMSM(0, 0, 2, 0, OS_OPT_TIME_HMSM_STRICT, &err); } ad7280.init = 1; ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_AUX_ADC_OVERVOLTAGE, true, 0x00); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_ALERT , true, 0x47); // 10100100 // ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_PD_TIMER , true, 0x18); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB1_TIMER , true, 0x08); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB2_TIMER , true, 0x08); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB3_TIMER , true, 0x08); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB4_TIMER , true, 0x08); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB5_TIMER , true, 0x08); // 10100100 ad7280_write_register(AD7280A_DEVADDR_MASTER, AD7280A_CB6_TIMER , true, 0x08); // 10100100 printf("AD7280A Chain Setup (MAX Device = %d)\r\n", data); } void set_pd_status(u8 status) { ad7280.pd_status = status; if (ad7280.pd_status == true) GPIO_SetBits (AD_CHIP_IO_GPIO, AD_CHIP_PD); else GPIO_ResetBits(AD_CHIP_IO_GPIO, AD_CHIP_PD); } u8 get_pd_status(void) { return ad7280.pd_status; } static void ad7280_crc8_build_table(u8 *crc_tab) { u8 bit, crc; u16 cnt, i; for (cnt = 0; cnt < 256; cnt++) { crc = cnt; for (i = 0; i < 8; i++) { bit = crc & HIGHBIT; crc <<= 1; if (bit) crc ^= POLYNOM; } crc_tab[cnt] = crc; } } static u8 ad7280_calc_crc8(u8 *crc_tab, u32 val) { u8 crc; crc = crc_tab[val >> 16 & 0xFF]; crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; return crc ^ (val & 0xFF); } static s32 ad7280_check_crc(ad7280_state *st, u32 val) { u8 crc = ad7280_calc_crc8(st->crc_tab, val >> 10); // printf("\r\n CRC1 = 0x%X", crc); // printf("\r\n CRC2 = 0x%X", ((val >> 2) & 0xFF)); if (crc != ((val >> 2) & 0xFF)) return -EIO; return 0; } void Print_CRC_TABLE(void) { u16 i, j; for (i = 0; i < 16; i++) { printf("\r\n"); for (j = 0; j < 16; j++) { printf("0x%02X, ", ad7280.crc_tab[(i * 16) + j]); } } } /* After initiating a conversion sequence we need to wait until the * conversion is done. The delay is typically in the range of 15..30 us * however depending an the number of devices in the daisy chain and the * number of averages taken, conversion delays and acquisition time options * it may take up to 250us, in this case we better sleep instead of busy * wait. */ static void ad7280_delay(u16 ms) { delay_ms(ms); } s32 __ad7280_read32(u32 *val) { s32 result = -1; u16 rx_tmp[2] = {0, 0}; u32 rx_buf; /* Select the FLASH: Chip Select low */ AD7280A_CS_LOW; if (SPI2_ReadWriteByte(DUMMY_BYTE, &rx_tmp[0], 1000) != 0) goto __ad7280_read32_end; if (SPI2_ReadWriteByte(DUMMY_BYTE, &rx_tmp[1], 1000) != 0) goto __ad7280_read32_end; rx_buf = 0; rx_buf |= (u32)rx_tmp[0] << 16; rx_buf |= (u32)rx_tmp[1] << 0; /* Deselect the FLASH: Chip Select high */ *val = rx_buf; result = 0; __ad7280_read32_end: AD7280A_CS_HIGH; return result; } s32 spi_write(u32 reg) { s32 result = -1; u16 rx_tmp[2] = {0, 0}; u32 rx_buf; AD7280A_CS_LOW; if (SPI2_ReadWriteByte((u16)(reg >> 16), &rx_tmp[0], 200) != 0) goto spi_write_end; if (SPI2_ReadWriteByte((u16)(reg >> 0), &rx_tmp[1], 200) != 0) goto spi_write_end; rx_buf = 0; rx_buf |= (u32)rx_tmp[0] << 16; rx_buf |= (u32)rx_tmp[1] << 0; result = 0; spi_write_end: AD7280A_CS_HIGH; return result; } void PrintWriteCycle(u32 reg) { P32BitWriteCycle Write; Write = (P32BitWriteCycle)® printf("\r\n -------32Bit Write Cycle-------"); printf("\r\n DeviceAddr : 0x%X", Write->DeviceAddr ); printf("\r\n RegisterAddr : 0x%X", Write->RegisterAddr); printf("\r\n RegisterData : 0x%X", Write->RegisterData); printf("\r\n AddrAllParts : 0x%X", Write->AddrAllParts); printf("\r\n Reserved : 0x%X", Write->Reserved ); printf("\r\n Bit8CRC : 0x%X", Write->Bit8CRC ); printf("\r\n BitPattern : 0x%X", Write->BitPattern ); printf("\r\n -------------------------------"); } void PrintReadRegisterCycle(u32 reg) { P32BitReadRegisterDataCycle Read; Read = (P32BitReadRegisterDataCycle)® printf("\r\n -------32Bit Read Register Cycle-------"); printf("\r\n DeviceAddr : 0x%X", Read->DeviceAddr ); printf("\r\n RegisterAddr : 0x%X", Read->RegisterAddr); printf("\r\n RegisterData : 0x%X", Read->RegisterData); printf("\r\n Reserved : 0x%X", Read->Reserved ); printf("\r\n WriteAck : 0x%X", Read->WriteAck ); printf("\r\n Bit8CRC : 0x%X", Read->Bit8CRC ); printf("\r\n BitPattern : 0x%X", Read->BitPattern ); printf("\r\n ---------------------------------------"); } u8 ad7280_devaddr_reverse(u8 devaddr) { u8 result = 0; result |= ((devaddr >> 4)& 0x01) << 0; result |= ((devaddr >> 3)& 0x01) << 1; result |= ((devaddr >> 2)& 0x01) << 2; result |= ((devaddr >> 1)& 0x01) << 3; result |= ((devaddr >> 0)& 0x01) << 4; return result; } u32 ad7280_write(ad7280_state *st, u32 devaddr, u32 addr, u8 all, u32 val) { u32 reg = (ad7280_devaddr_reverse(devaddr) << 27 | addr << 21 | (val & 0xFF) << 13 | all << 12); reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2; return spi_write(reg); } s8 ad7280_write_register(u8 devaddr, u8 addr, u8 all, u8 data) { /* 0x01A6151A * Register Addr ; 001101 (0x0D), Control MSB * Data ; 00110000 (0x30) * Bit 12-13 ; Read conversion results ; 11 = no-read operation * Write All */ /* turns off the read operation on all parts */ if (all == true) ad7280_write(&ad7280, devaddr, addr, true , data); else ad7280_write(&ad7280, devaddr, addr, false, data); return 0; } s8 ad7280_read_register(u8 devaddr, u8 addr) { u32 tmp; ad7280_state *st; st = &ad7280; /* 0x01A6151A * Register Addr ; 001101 (0x0D), Control MSB * Data ; 00110000 (0x30) * Bit 12-13 ; Read conversion results ; 11 = no-read operation * Write All */ /* turns off the read operation on all parts */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, true, AD7280A_CTRL_HB_CONV_RES_READ_NO | st->ctrl_hb); /* turns on the read operation on the addressed part */ ad7280_write(st, devaddr, AD7280A_READ, false, addr << 2); /* 0x01AC86F2 * Register Addr ; 001101 (0x0D), Control MSB * Data ; 01100100 (0x64) * Bit 9-10 ; Conversion averaging ; 10 = average by 4 * Bit 12-13 ; Read conversion results ; 10 = six cell voltages only * Bit 14-15 ; Select conversion inputs ; 01 = six cell voltages and AUX1, AUX3, and AUX5 */ ad7280_write(st, devaddr, AD7280A_CONTROL_HB, false, AD7280A_CTRL_HB_CONV_INPUT_6CELL | AD7280A_CTRL_HB_CONV_RES_READ_6CELL | AD7280A_CTRL_HB_CONV_AVG_8 | st->ctrl_hb); ad7280_delay(1); __ad7280_read32(&tmp); if (ad7280_check_crc(st, tmp)) return -EIO; if ((ad7280_devaddr_reverse((u8)(tmp >> 27)) != devaddr) || (((tmp >> 21) & 0x3F) != addr)) return -EFAULT; return (tmp >> 13) & 0xFF; } s32 ad7280_read_all_channels(u16 cnt, u32 *array) { int i; u32 tmp, sum = 0; ad7280_state *st; st = &ad7280; /* 0x038011CA * Register Addr ; 011100 (0x1C), Read * Data ; 00000000 (0x00), Address ; 00 * Write All */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, true, AD7280A_CELL_VOLTAGE_1 << 2); /* 0x038011CA * Register Addr ; 001101 (0x0D), Control MSB * Data ; 00000000 (0x00) * Write All */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, true, AD7280A_CTRL_HB_CONV_INPUT_6CELL | st->ctrl_hb); /* 0x01C2B6E2 * Register Addr ; 001110 (0x1D), CNVST Control * Data ; 00010101 (0x02) * Bit 0 - 1 ; Allow single CNVST pulse. Additional CNVST pulses are gated. * Write All */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CNVST_CONTROL, true, 0x02); AD7280A_CONV; for (i = 0; i < cnt; i++) { __ad7280_read32(&tmp); if (ad7280_check_crc(st, tmp)) return -EIO; if (array) array[i] = tmp; /* only sum cell voltages */ if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) sum += ((tmp >> 11) & 0xFFF); ad7280_delay(1); } return sum; } s32 ad7280_chain_setup(void) { u32 val, n; ad7280_state *st; st = &ad7280; ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, true, AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | AD7280A_CTRL_LB_LOCK_DEV_ADDR | AD7280A_CTRL_LB_MUST_SET | st->ctrl_lb); ad7280_delay(10); for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, true, AD7280A_CTRL_HB_CONV_RES_READ_NO | st->ctrl_hb); ad7280_write(st, n, AD7280A_READ, false, AD7280A_CONTROL_LB << 2); ad7280_write(st, n, AD7280A_CONTROL_HB, false, AD7280A_CTRL_HB_CONV_INPUT_6CELL | AD7280A_CTRL_HB_CONV_RES_READ_6CELL | AD7280A_CTRL_HB_CONV_AVG_8 | st->ctrl_hb); ad7280_delay(10); __ad7280_read32(&val); if (val == 0) return n; if (ad7280_check_crc(st, val)) return -EIO; if (n != ad7280_devaddr_reverse((u8)(val >> 27))) { return -EIO; } } return -EFAULT; } // The self-test conversion result typically varies between Code 970 and Code 990. s32 ad7280_self_test(unsigned devaddr) { u32 val; ad7280_state *st; st = &ad7280; /* 0x01BF0782 * Register Addr ; 001101 (0x0D), Control MSB * Data ; 11111000 (0xF8) * Bit 11 ; Conversion start format ; 1 = rising edge of CS * Bit 12-13 ; Read conversion results ; 11 = no-read operation * Bit 14-15 ; Select conversion inputs ; 11 = ADC self-test */ ad7280_write(st, devaddr, AD7280A_CONTROL_HB, false, AD7280A_CTRL_HB_CONV_START_CS | AD7280A_CTRL_HB_CONV_RES_READ_NO | AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST | st->ctrl_hb); ad7280_delay(1); /* 0x01A6151A * Register Addr ; 001101 (0x0D), Control MSB * Data ; 00110000 (0x30) * Bit 12-13 ; Read conversion results ; 11 = no-read operation * Write All */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, true, AD7280A_CTRL_HB_CONV_RES_READ_NO | st->ctrl_hb); /* 0x038607DA * Register Addr ; 011100 (0x1C), Read * Data ; 00110000 (0x30), Address ; 0C * [D7:D2] 6-bit binary address for the register to be read * [D1:D0] Reserved; set to 00 */ ad7280_write(st, devaddr, AD7280A_READ, false, AD7280A_SELF_TEST << 2); /* 0x01AC86F2 * Register Addr ; 001101 (0x0D), Control MSB * Data ; 01100100 (0x64) * Bit 9-10 ; Conversion averaging ; 10 = average by 4 * Bit 12-13 ; Read conversion results ; 10 = six cell voltages only * Bit 14-15 ; Select conversion inputs ; 01 = six cell voltages and AUX1, AUX3, and AUX5 */ ad7280_write(st, devaddr, AD7280A_CONTROL_HB, false, AD7280A_CTRL_HB_CONV_INPUT_6CELL | AD7280A_CTRL_HB_CONV_RES_READ_6CELL | AD7280A_CTRL_HB_CONV_AVG_8 | st->ctrl_hb); ad7280_delay(1); __ad7280_read32(&val); if (ad7280_check_crc(st, val)) return -EIO; if (ad7280_devaddr_reverse((u8)(val >> 27)) != devaddr) return -EFAULT; return (val >> 11) & 0xFFF; } s32 ad7280_sw_reset(void) { ad7280_state *st; st = &ad7280; /* 0x01D274D2 * Register Addr ; 001110 (0x0E), Control LSB * Data ; 10010011 (0x93) * Bit 0 ; Daisy-chain register readback * Bit 1 ; Increment device address * Bit 4 ; Reserved; set to 1 * Bit 7 ; Software reset */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, true, AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | AD7280A_CTRL_LB_INC_DEV_ADDR | AD7280A_CTRL_LB_MUST_SET | AD7280A_CTRL_LB_SWRST | st->ctrl_lb); ad7280_delay(10); /* 0x01C2B6E2 * Register Addr ; 001110 (0x0E), Control LSB * Data ; 00010101 (0x15) * Bit 0 ; Daisy-chain register readback * Bit 2 ; Lock device address * Bit 4 ; Reserved; set to 1 */ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, true, AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | AD7280A_CTRL_LB_LOCK_DEV_ADDR | AD7280A_CTRL_LB_MUST_SET | st->ctrl_lb); ad7280_delay(10); return 0; } void SetCellbalanceBuff(u8 dev_addr, u8 value) { ad7280.cb_mask[dev_addr] = value & 0xFC; } u8 GetCellbalanceBuff(u8 dev_addr) { return ad7280.cb_mask[dev_addr]; } u16 SetCellBalance(u16 cno, u8 onoff) { u8 addr, id; u8 cdata; addr = cno / 5; id = cno % 5; cdata = ad7280.cb_mask[addr]; if (onoff == true) { cdata = cdata | (1 << cno); } else { cdata = cdata & ~(1 << cno); } ad7280.cb_mask[addr] = cdata; }