diff --git a/project/src/ina219/ina219drv.c b/project/src/ina219/ina219drv.c new file mode 100644 index 0000000..5daff4a --- /dev/null +++ b/project/src/ina219/ina219drv.c @@ -0,0 +1,430 @@ +/* + * ina219drv.c + * + * Created on: 02/05/2017 + * Author: pvvx + */ +#include +#include "rtl8195a.h" +#include "device.h" +#include "PinNames.h" + +#include "basic_types.h" +#include "diag.h" +#include "osdep_api.h" + +//#include "i2c_api.h" +//#include "i2c_ex_api.h" +#include "pinmap.h" +#include "rtl_lib.h" +#include "ina219/ina219drv.h" +#include "rtl8195a/rtl_libc.h" +#include "driver/i2c_drv.h" +#include "platform_stdlib.h" + +#include "hal_com_reg.h" + +//#define ReadTSF_Lo32() (*((volatile unsigned int *)(WIFI_REG_BASE + REG_TSFTR))) +//#define ReadTSF_Hi32() (*((volatile unsigned int *)(WIFI_REG_BASE + REG_TSFTR1))) + +INA219DRV ina219drv = { + .addr = INA219_ADDRESS>>1, + .config = + INA219_CONFIG_BVOLTAGERANGE_16V | // INA219_CONFIG_BVOLTAGERANGE_32V + INA219_CONFIG_GAIN_8_320MV | // INA219_CONFIG_GAIN_1_40MV | + INA219_CONFIG_BADCRES_12BIT | + INA219_CONFIG_SADCRES_12BIT_2S_1060US | // INA219_CONFIG_SADCRES_12BIT_128S_69MS | + INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS, + .calibration = 8192, // при шунте 0.1 Ом + .buf_idx = 709, // циклический буфер на 710 замеров (по 4 байт -> sizeof(INA219DATA)) + // Если шаг заполнения 1 ms -> буфер на 0.71 сек + // Оптимизация под TCP: (TCP_MSS*2 - 80)/4 = (1460*2 - 80)/4 = 710 + .i2c.status = DRV_I2C_OFF, + .i2c.idx = 1, // I2C1 + .i2c.io_sel = S0, // PC_4, PC_5 + .i2c.mode = DRV_I2C_FS_MODE // DRV_I2C_HS_MODE +}; +/* +void ina219_write(unsigned char reg, unsigned short data) +{ + PINA219DRV p = &ina219drv; + p->buf_i2c.uc[0] = reg; + p->buf_i2c.uc[1] = (unsigned char)((unsigned short)(data >> 8)); + p->buf_i2c.uc[2] = (unsigned char)data; + _i2c_write(&p->i2c, p->addr, (const char *)p->buf_i2c.uc, 3, 1); + UBaseType_t f; + portBASE_TYPE d; +} + +unsigned int ina219_read(unsigned char reg) +{ + PINA219DRV p = &ina219drv; + p->buf_i2c.uc[0] = reg; + _i2c_write(&p->i2c, p->addr, (const char *)p->buf_i2c.uc, 1, 1); + p->buf_i2c.ui = 0; + _i2c_read(&p->i2c, p->addr, (const char *)p->buf_i2c.uc, 2, 1); + return (p->buf_i2c.uc[0] << 8) | p->buf_i2c.uc[1]; +} +*/ + +#define i2c_reg(r) *((volatile uint32 *)(pi2c->base_regs + r)) + +/* Пример непрерывного чтения регистров + * тока и напряжения IN219 по прерыванию таймера */ +void ina_tick_handler(void *par) { + PINA219DRV p = &ina219drv; + i2c_drv_t *pi2c = &p->i2c; + switch(p->status) { + default: + // Disable controller. + i2c_reg(REG_DW_I2C_IC_ENABLE) = 0; + p->status = 1; + break; + case 1: + // Master Target Address + i2c_reg(REG_DW_I2C_IC_TAR) = p->addr; + // Enable controller. + i2c_reg(REG_DW_I2C_IC_ENABLE) = BIT_IC_ENABLE; + p->status = 2; + break; + case 2: + // Заполним FIFO ic I2C командами инициализации INA219 + // Write addr reg. + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = INA219_REG_CONFIG; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = p->config >> 8; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = (p->config & 0x0FF) | BIT_IC_DATA_CMD_STOP; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = INA219_REG_CALIBRATION; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = p->calibration >> 8; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = (p->calibration & 0x0FF) | BIT_IC_DATA_CMD_STOP; + // + p->status = 3; + break; + case 4: + if (i2c_reg(REG_DW_I2C_IC_RAW_INTR_STAT) & BIT_IC_RAW_INTR_STAT_TX_ABRT) { + uint32 tmp = i2c_reg(REG_DW_I2C_IC_CLR_INTR); + p->errs++; + p->status = 0; + break; + } else { + if(p->pbuf) { + PINA219DATA pd = p->pbuf + p->buf_tx; + // Считаем готовые значения из FIFO ic I2C + pd->v.vuc[1] = i2c_reg(REG_DW_I2C_IC_DATA_CMD); + pd->v.vuc[0] = i2c_reg(REG_DW_I2C_IC_DATA_CMD); + pd->i.iuc[1] = i2c_reg(REG_DW_I2C_IC_DATA_CMD); + pd->i.iuc[0] = i2c_reg(REG_DW_I2C_IC_DATA_CMD); + if(p->buf_tx >= p->buf_idx) p->buf_tx = 0; + else p->buf_tx++; + if(p->buf_rx == p->buf_tx) { + p->overrun++; // todo: if(p->overrun++ > 100000) deinit() ? + if(p->buf_rx >= p->buf_idx) p->buf_rx = 0; + else p->buf_rx++; + }; + } else { + (volatile)i2c_reg(REG_DW_I2C_IC_DATA_CMD); + (volatile)i2c_reg(REG_DW_I2C_IC_DATA_CMD); + (volatile)i2c_reg(REG_DW_I2C_IC_DATA_CMD); + (volatile)i2c_reg(REG_DW_I2C_IC_DATA_CMD); + }; + } + case 3: + // Заполним FIFO ic I2C командами чтения + // Write addr reg. + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = INA219_REG_BUSVOLTAGE | BIT_IC_DATA_CMD_STOP; + // Read command. + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP; + // Write addr reg. + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = INA219_REG_SHUNTVOLTAGE | BIT_IC_DATA_CMD_STOP; + // Read command. + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP; + p->status = 4; + break; + } +} + +uint16 ina219_chkdata(uint16 cnt) +{ + PINA219DRV p = &ina219drv; + if(p->init <= 0) return 0; + int len = p->buf_tx - p->buf_rx; + if(len < 0) len += p->buf_idx + 1; + if(cnt > (uint16)len) cnt = (uint16)len; + return cnt; +} + +size_t ina219_getdata(void *pd, uint16 cnt) +{ + PINA219DRV p = &ina219drv; + if(p->init <= 0) return 0; +// cnt = ina219_chkdata(cnt); + unsigned short *pus = (unsigned short *) pd; + taskDISABLE_INTERRUPTS(); + uint16 buf_rx = p->buf_rx; + *pus++ = cnt; // кол-во замеров + *pus++ = p->count + p->overrun; // индекс замера для анализа пропусков на стороне приемника + // если не пропущено, то равен прошлому + кол-во считанных замеров в прошлом блоке + p->count += cnt; // p->overrun = 0; + unsigned char *puc = (unsigned char *) pus; + if(cnt) { + uint16 lend = buf_rx + cnt; + if(lend > p->buf_idx) { + lend -= p->buf_idx + 1; + p->buf_rx = lend; + } else { + p->buf_rx = lend; + lend = 0; + }; + size_t len = (cnt - lend) *sizeof(INA219DATA); + if(len) memcpy(puc, (void *)(p->pbuf + buf_rx), len); + if(lend) memcpy(puc + len, (void *)p->pbuf, lend *sizeof(INA219DATA)); + } + taskENABLE_INTERRUPTS(); + return cnt * sizeof(INA219DATA) + 4; +} +#define mMIN(a, b) ((ab)?a:b) + +#include "web_srv.h" +#include "websock.h" + +int ina219_ws(TCP_SERV_CONN *ts_conn, char cmd) +{ + PINA219DRV p = &ina219drv; + switch(cmd) { + case 'd': // deinit + if(p->init > 0) { + gtimer_stop(&p->timer); + gtimer_deinit(&p->timer); + _i2c_deinit(&p->i2c); + if(p->pbuf) { + free(p->pbuf); + p->pbuf = NULL; + } + p->init = -1; + return 0; + } + return 1; + case 'c': // get count + return ina219_chkdata(p->buf_idx + 1); + case 'i': // init + return p->init; + default: // get_data + if(p->init <= 0) { + p->status = 0; + p->count = 0; + p->overrun = 0; + p->errs = 0; + if(!p->pbuf) { + p->pbuf = zalloc((p->buf_idx + 1) * sizeof(INA219DATA)); + if(!p->pbuf) { + error_printf("Error create buffer!\n"); + return -1; + }; + p->buf_tx = 0; + p->buf_rx = 0; + }; + _i2c_init(&p->i2c); + gtimer_init(&p->timer, INA219_TIMER); + gtimer_start_periodical(&p->timer, 1000, (void*)ina_tick_handler, (uint32_t)&ina219drv); + p->init = 1; +// return 0; + } + case 'g': // get + { + uint32 i = ina219_chkdata(p->buf_idx + 1); + if(i) { + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + i = mMIN((web_conn->msgbufsize / sizeof(INA219DATA)), i); + if(websock_tx_frame(ts_conn, WS_OPCODE_BINARY | WS_FRAGMENT_FIN, web_conn->msgbuf, ina219_getdata(web_conn->msgbuf, i)) != ERR_OK) + return -1; + } + return i; + } + } + return -1; +} + +void ina219_init(void) +{ + PINA219DRV p = &ina219drv; + if(p->init <= 0) { + rtl_printf("Init INA219\n"); + p->status = 0; + p->count = 0; + p->errs = 0; + if(!p->pbuf) { + p->pbuf = zalloc((p->buf_idx + 1) * sizeof(INA219DATA)); + if(!p->pbuf) { + error_printf("Error create buffer!\n"); + return; + }; + p->buf_tx = 0; + p->buf_rx = 0; + }; +// (!) Установки драйвера I2C заданы в структуре ina219drv +// _i2c_setup(&p->i2c, INA219_I2C_PIN_SDA , INA219_I2C_PIN_SCL, DRV_I2C_FS_MODE); // == DRV_I2C_OK? + _i2c_init(&p->i2c); +// _i2c_set_speed(&p->i2c, INA219_I2C_BUS_CLK); + rtl_printf("I2C%d mode = %d, drvStatus = %d\n", p->i2c.idx, p->i2c.mode, p->i2c.status); +// (!) Инициализация INA219 перенесена в прерывание таймера +// ina219_write(INA219_REG_CONFIG, p->config); +// ina219_write(INA219_REG_CALIBRATION, p->calibration); + // Initial a periodical timer + gtimer_init(&p->timer, INA219_TIMER); + // Tick every 0.000532 sec (N*532 μs) +// uint32 tus = (1 << ((p->config >> 3) & 7)); +// tus *= 532; + gtimer_start_periodical(&p->timer, 1000, (void*)ina_tick_handler, (uint32_t)&ina219drv); + rtl_printf("INA219 Timer Period = %u us\n", p->timer.hal_gtimer_adp.TimerLoadValueUs); + p->init = 1; + } +} + +void ina219_deinit(void) +{ + PINA219DRV p = &ina219drv; + i2c_drv_t *pi2c = &p->i2c; + if(p->init > 0) { + rtl_printf("Deinit INA219\n"); + gtimer_stop(&p->timer); + gtimer_deinit(&p->timer); +// (!) Не используется ina219_write / ina219_read для сокращения кода +// ina219_write(INA219_REG_CONFIG, INA219_CONFIG_MODE_POWERDOWN); + // Break controller. + i2c_reg(REG_DW_I2C_IC_ENABLE) |= 2; + vTaskDelay(2); // +WDT + // Disable controller. + i2c_reg(REG_DW_I2C_IC_ENABLE) = 0; + vTaskDelay(2); // +WDT + // Master Target Address + i2c_reg(REG_DW_I2C_IC_TAR) = p->addr; + // Enable controller. + i2c_reg(REG_DW_I2C_IC_ENABLE) = BIT_IC_ENABLE; + // Заполним FIFO ic I2C командой отключения INA219 + vTaskDelay(2); // +WDT + // Write addr reg. INA219 POWER_DOWN + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = INA219_REG_CONFIG; + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = (INA219_CONFIG_MODE_POWERDOWN >> 8); + i2c_reg(REG_DW_I2C_IC_DATA_CMD) = (INA219_CONFIG_MODE_POWERDOWN & 0x0F) | BIT_IC_DATA_CMD_STOP; + vTaskDelay(2); // +WDT + _i2c_deinit(&p->i2c); + if(p->pbuf) { + free(p->pbuf); + p->pbuf = NULL; + } + } + p->init = 0; +} + +LOCAL void ShowIna(void) +{ + PINA219DRV p = &ina219drv; + rtl_printf("INA219: %d, %d\n", p->buf_i2c.us[0], p->buf_i2c.ss[1]); + rtl_printf("INA219: Cnt = %u, Err = %u\n", p->count, p->errs); +} + +LOCAL void fATINA(int argc, char *argv[]) +{ + if(argc > 1) { + if(atoi(argv[1])) { + ina219_init(); + vTaskDelay(10); // +WDT + } + else ina219_deinit(); + } + ShowIna(); +} + + +extern void dump_bytes(uint32 addr, int size); +extern uint32 hextoul(uint8 *s); + +i2c_drv_t ti2c; +/* Sample: + * ati2c i + * ati2c w 40 5 + * ati2c r 40 2 + */ +LOCAL void fATI2C(int argc, char *argv[]) +{ + i2c_drv_t *pi2c = &ti2c; + uint8 buf[32]; + if(argc > 1) { + if(argv[1][0] == 'i') { + if(!pi2c->status) { + uint8 sda = 0; + uint8 scl = 0; + uint8 mode = 0; + uint32 speed = 0; + if(argc > 2) sda = hextoul(argv[2]); + else if(argc > 3) scl = hextoul(argv[3]); + else if(argc > 4) mode = hextoul(argv[4]); + else if(argc > 5) speed = hextoul(argv[5]); + if(!sda) sda = PC_4; + if(!scl) scl = PC_5; + if(!mode) mode = DRV_I2C_FS_MODE; + if(!speed) speed = 400000; + if(_i2c_setup(pi2c, sda, scl, mode) == DRV_I2C_OK + && _i2c_init(pi2c) == DRV_I2C_OK + && _i2c_set_speed(pi2c, speed) == DRV_I2C_OK) { + rtl_printf("I2C%d Init\n", pi2c->idx); + }; + } else { + rtl_printf("Already init!\n"); + return; + }; + } else { + if(pi2c->status) { + if(argv[1][0] == 'd') { + _i2c_ic_off(pi2c); + rtl_printf("I2C%d DeInit\n", pi2c->idx); + return; + }; + int i; + for(i = 0; i + 2 < argc; i++) { + buf[i] = hextoul(argv[i+2]); + }; + if(i) { + if(argv[1][0] == 'w') { + _i2c_write(pi2c, buf[0], &buf[1], i-1, 1); + rtl_printf("I2C%d write[%d]:\n", pi2c->idx, i-1); + dump_bytes(&buf[0], i); + } + else if(argv[1][0] == 'r') { + i = buf[1]; + if(i > sizeof(buf) - 1) i = sizeof(buf) - 1; + _i2c_read(pi2c, buf[0], &buf[1], i, 1); + rtl_printf("I2C%d read[%d]:\n", pi2c->idx, i); + dump_bytes(&buf[0], i+1); + }; + + }; + }; + }; + }; + rtl_printf("I2C%d drvStatus = %d\n", pi2c->idx, pi2c->status); + return; +} + +LOCAL void fATLED(int argc, char *argv[]) +{ + if(argc > 1) { + EGTIM_FCTRL(1); + EGTIM_RSIG_SEL(atoi(argv[1])); + EGTIME_PIN_G0_OPT_SEL(atoi(argv[2])); + EGTIME_PIN_G1_OPT_SEL(atoi(argv[3])); + EGTIME_PIN_G1_OPT_SEL(atoi(argv[4])); + } + else { + EGTIM_FCTRL(0); + } +} + +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_ina219[] = { + {"ATLED", 0, fATLED, ": Test LED"}, + {"ATI2C", 0, fATI2C, ": Test I2C, nit, einit, rite, ead"}, + {"ATINA", 0, fATINA, "=[0/1]: INA219 =1 start, =0 stop"} +}; +