RTL00_WEB_VS/RTLGDB/Project/MLX90614/MLX90614.c
ADElectronics 764b020238 update
2017-11-28 22:31:40 +03:00

413 lines
12 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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
*/