RTL00_WEB_VS/RTLGDB/Project/MLX90614/MLX90614.c

414 lines
12 KiB
C
Raw Permalink Normal View History

2017-11-28 19:31:40 +00:00
/*
* MLX90614drv.c
* https://esp8266.ru/forum/threads/i2c-na-rtl.2450/
* Created on: 02/09/2017
* Author: pvvx
*/
#include <platform_opts.h>
#include "rtl8195a.h"
#include "device.h"
#include "PinNames.h"
#include "basic_types.h"
#include "diag.h"
#include "osdep_api.h"
#include "timer_api.h"
//#include "i2c_api.h"
//#include "i2c_ex_api.h"
#include "pinmap.h"
#include "rtl_lib.h"
#include "rtl8195a/rtl_libc.h"
#include "driver/i2c_drv.h"
#include "platform_stdlib.h"
#include "web_websocket.h"
#include "tcpsrv/tcp_srv_conn.h"
#include "hal_com_reg.h"
#define MLX90614_I2CADDR 0x5A
// RAM
#define MLX90614_RAWIR1 0x04
#define MLX90614_RAWIR2 0x05
#define MLX90614_TA 0x06
#define MLX90614_TOBJ1 0x07
#define MLX90614_TOBJ2 0x08
// EEPROM
#define MLX90614_TOMAX 0x20
#define MLX90614_TOMIN 0x21
#define MLX90614_PWMCTRL 0x22
#define MLX90614_TARANGE 0x23
#define MLX90614_EMISS 0x24
#define MLX90614_CONFIG 0x25
#define MLX90614_ADDR 0x0E
#define MLX90614_ID1 0x3C
#define MLX90614_ID2 0x3D
#define MLX90614_ID3 0x3E
#define MLX90614_ID4 0x3F
#define MLX90614_SLEEP 0xFF
typedef struct _MLX90614_data {
union {
signed short to1; // Tobj1
unsigned char uc[2];
} o;
union {
signed short ta; // Ta
unsigned char uc[2];
} a;
} MLX90614DATA, *PMLX90614DATA;
typedef struct _MLX90614drv {
unsigned char status;
unsigned char addr; // адрес MLX90614 на шине I2C (формат 7 bit)
signed char init;
unsigned char tmp;
unsigned short config; // регистр конфигурации MLX90614
unsigned short timetik; // время опроса таймером (в us)
unsigned short count; // счетчик считанных значений
unsigned short overrun; // счет переполнений буфера
unsigned short errs; // счет ошибок на I2C
unsigned short buf_idx; // объем буфера pbuf[buf_idx+1], максимальный индекс-номер замера
unsigned short buf_rx; // индекс-номер ещё не считанного замера
unsigned short buf_tx; // индекс-номер для записи следующего замера
PMLX90614DATA pbuf;
gtimer_t timer;
union { // буфер
unsigned char uc[4];
unsigned short us[2];
signed short ss[2];
unsigned int ui;
} buf_i2c;
i2c_drv_t i2c;
} MLX90614DRV, *PMLX90614DRV;
#define MLX90614_I2C_PIN_SDA PC_4
#define MLX90614_I2C_PIN_SCL PC_5
#define MLX90614_I2C_BUS_CLK 50000 // The maximum frequency of the MLX90614 SMBus is 100 KHz and the minimum is 10 KHz.
#define MLX90614_TIMER TIMER3 // используемый таймер
MLX90614DRV mlx90614drv = {
.addr = MLX90614_I2CADDR,
.timetik = 10000, // Время опроса таймером: 10000 us
.buf_idx = 709, // циклический буфер на 710 замеров (по 4 байт -> sizeof(MLX90614DATA))
// Если шаг заполнения 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_SS_MODE // DRV_I2C_FS_MODE // DRV_I2C_HS_MODE
};
#define i2c_reg(r) *((volatile uint32 *)(pi2c->base_regs + r))
#define MLX90614_USE_CRC
#ifdef MLX90614_USE_CRC
unsigned char crc8(unsigned char x) {
for (int i = 8; i; i--) {
x = ( x << 1 ) ^ ( x & 128 ? 7 : 0 );
}
return x;
}
#endif
/* Пример непрерывного чтения регистров Tobj1 и Tobj2
* MLX90614 по прерыванию таймера */
void mlx_tick_handler(void *par) {
PMLX90614DRV p = &mlx90614drv;
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 3:
if (i2c_reg(REG_DW_I2C_IC_RAW_INTR_STAT) & BIT_IC_RAW_INTR_STAT_TX_ABRT) {
(volatile uint32)i2c_reg(REG_DW_I2C_IC_CLR_INTR);
p->errs++;
p->status = 0;
break;
} else {
if(p->pbuf) {
PMLX90614DATA pd = p->pbuf + p->buf_tx;
// Считаем готовые значения из FIFO ic I2C
pd->o.uc[0] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
pd->o.uc[1] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#ifdef MLX90614_USE_CRC
// B407B5 -> 0x28 (http://crccalc.com/)
if(crc8(pd->o.uc[1] ^ crc8(pd->o.uc[0] ^ 0x28)) != (uint8) i2c_reg(REG_DW_I2C_IC_DATA_CMD)) {
i2c_reg(REG_DW_I2C_IC_ENABLE) = 2; // ABORT
p->errs++;
p->status = 0;
break;
}
#endif
pd->a.uc[0] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
pd->a.uc[1] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#ifdef MLX90614_USE_CRC
// B406B5 -> 0x3D (http://crccalc.com/)
if(crc8(pd->a.uc[1] ^ crc8(pd->a.uc[0] ^ 0x3D)) != (uint8) i2c_reg(REG_DW_I2C_IC_DATA_CMD)) {
i2c_reg(REG_DW_I2C_IC_ENABLE) = 2; // ABORT
p->errs++;
p->status = 0;
break;
}
#endif
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 {
// удаление из FIFO блока данных
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#ifdef MLX90614_USE_CRC
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#endif
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#ifdef MLX90614_USE_CRC
(volatile uint32)i2c_reg(REG_DW_I2C_IC_DATA_CMD);
#endif
};
}
case 2:
// Заполним FIFO ic I2C командами чтения
// Write Command.
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = MLX90614_TOBJ1;
// Read command & data (3 bytes).
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_RESTART;
#ifdef MLX90614_USE_CRC
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD;
#endif
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP;
// Write Command.
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = MLX90614_TA;
// Read command & data (3 bytes).
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_RESTART;
#ifdef MLX90614_USE_CRC
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD;
#endif
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP;
p->status = 3;
break;
}
}
uint16 mlx90614_chkdata(uint16 cnt)
{
PMLX90614DRV p = &mlx90614drv;
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 mlx90614_getdata(void *pd, uint16 cnt)
{
PMLX90614DRV p = &mlx90614drv;
if(p->init <= 0) return 0;
// cnt = mlx90614_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(MLX90614DATA);
if(len) memcpy(puc, (void *)(p->pbuf + buf_rx), len);
if(lend) memcpy(puc + len, (void *)p->pbuf, lend *sizeof(MLX90614DATA));
}
taskENABLE_INTERRUPTS();
return cnt * sizeof(MLX90614DATA) + 4;
}
//#define mMIN(a, b) ((a<b)?a:b)
#define mMAX(a, b) ((a>b)?a:b)
#include "web_srv.h"
#include "websock.h"
int mlx90614_ws(TCP_SERV_CONN *ts_conn, char cmd)
{
PMLX90614DRV p = &mlx90614drv;
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 mlx90614_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(MLX90614DATA));
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, MLX90614_TIMER);
gtimer_start_periodical(&p->timer, p->timetik, (void*)mlx_tick_handler, (uint32_t)&mlx90614drv);
p->init = 1;
// return 0;
}
case 'g': // get
{
uint32 i = mlx90614_chkdata(p->buf_idx + 1);
if(i) {
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
i = mMIN((web_conn->msgbufsize / sizeof(MLX90614DATA)), i);
if(websock_tx_frame(ts_conn, WS_OPCODE_BINARY | WS_FRAGMENT_FIN, web_conn->msgbuf, mlx90614_getdata(web_conn->msgbuf, i)) != ERR_OK)
return -1;
}
return i;
}
}
return -1;
}
void mlx90614_init(void)
{
PMLX90614DRV p = &mlx90614drv;
if(p->init <= 0) {
rtl_printf("Init MLX90614\n");
p->status = 0;
p->count = 0;
p->errs = 0;
if(!p->pbuf) {
p->pbuf = zalloc((p->buf_idx + 1) * sizeof(MLX90614DATA));
if(!p->pbuf) {
error_printf("Error create buffer!\n");
return;
};
p->buf_tx = 0;
p->buf_rx = 0;
};
// (!) Установки драйвера I2C заданы в структуре mlx90614drv
// _i2c_setup(&p->i2c, MLX90614_I2C_PIN_SDA , MLX90614_I2C_PIN_SCL, DRV_I2C_FS_MODE); // == DRV_I2C_OK?
_i2c_init(&p->i2c);
// _i2c_set_speed(&p->i2c, MLX90614_I2C_BUS_CLK);
rtl_printf("I2C%d mode = %d, drvStatus = %d\n", p->i2c.idx, p->i2c.mode, p->i2c.status);
// (!) Инициализация MLX90614 перенесена в прерывание таймера
// Initial a periodical timer
gtimer_init(&p->timer, MLX90614_TIMER);
// Tick every 0.001 sec (p->timetik)
gtimer_start_periodical(&p->timer, p->timetik, (void*)mlx_tick_handler, (uint32_t)&mlx90614drv);
rtl_printf("MLX90614 Timer Period = %u us\n", p->timetik);
p->init = 1;
}
}
void mlx90614_deinit(void)
{
PMLX90614DRV p = &mlx90614drv;
i2c_drv_t *pi2c = &p->i2c;
if(p->init > 0) {
rtl_printf("Deinit MLX90614\n");
gtimer_stop(&p->timer);
gtimer_deinit(&p->timer);
// (!) Не используется mlx90614_write / mlx90614_read для сокращения кода
// mlx90614_write(MLX90614_REG_CONFIG, MLX90614_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;
/* Exit from Sleep Mode > 250 ms !
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 командой sleep MLX90614
vTaskDelay(2); // +WDT
// MLX90614 Enter Sleep Mode
i2c_reg(REG_DW_I2C_IC_DATA_CMD) = MLX90614_SLEEP | 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 ShowMlx(void)
{
PMLX90614DRV p = &mlx90614drv;
rtl_printf("MLX90614: %d, %d\n", p->buf_i2c.us[0], p->buf_i2c.ss[1]);
rtl_printf("MLX90614: Cnt = %u, Err = %u\n", p->count, p->errs);
}
LOCAL void fATMLX(int argc, char *argv[])
{
if(argc > 1) {
if(atoi(argv[1])) {
mlx90614_init();
vTaskDelay(10); // +WDT
}
else mlx90614_deinit();
}
ShowMlx();
}
MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_mlx90614[] = {
{"ATMLX", 0, fATMLX, "=[0/1]: MLX90614 =1 start, =0 stop"}
};
/*
* Test i2c:
* ati2c i
* ati2c g 5a 1 6 3
* ati2c d
*/