RTL00_WEB/project/src/adc_ws/adc_ws.c
2018-03-04 18:23:47 +03:00

291 lines
7.8 KiB
C
Raw 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.

/*
* ADC + Websocket
*
* Created on: 18 июн. 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 "FreeRTOS.h"
#include "diag.h"
//------------------------------------------------------------------------------
#include "objects.h"
#include "PinNames.h"
#include "hal_adc.h"
#include "analogin_api.h"
#include "timer_api.h"
#include "web_srv.h"
#include "websock.h"
#include "web_websocket.h"
#include "driver/adc_drv.h"
#include "rtl8195a/rtl_libc.h"
//------------------------------------------------------------------------------
//#define ADC_USE_TIMER TIMER3 // если назначено, то для чтения ADC используется таймер
// иначе ADC int
//------------------------------------------------------------------------------
#ifndef CONFIG_MBED_ENABLED
extern void *pvPortZalloc(size_t xWantedSize);
extern void vPortFree(void *pv);
extern void *pvPortMalloc(size_t xWantedSize);
#undef malloc
#define malloc pvPortMalloc
#undef zalloc
#define zalloc pvPortZalloc
#undef free
#define free vPortFree
#endif
//------------------------------------------------------------------------------
typedef struct _adc_data {
uint16_t us0;
uint16_t us1;
} ADC_DATA, *PADC_DATA;
typedef struct _adc_drv {
signed char init; // флаг
unsigned char dcmf; // ADC_DECIMATION_FILTER
unsigned char xclk; // ADC_SAMPLE_XCLK
unsigned char audio; // Audio mode
unsigned short count; // счетчик считанных значений
unsigned short overrun; // счет переполнений буфера
unsigned short buf_idx; // объем буфера pbuf[buf_idx+1], максимальный индекс-номер замера
unsigned short buf_rx; // индекс-номер ещё не считанного замера
unsigned short buf_tx; // индекс-номер для записи следующего замера
PADC_DATA pbuf;
#ifdef ADC_USE_TIMER
gtimer_t timer;
#endif
} ADC_DRV, *PADC_DRV;
#define mMIN(a, b) ((a<b)?a:b)
//#define mMAX(a, b) ((a>b)?a:b)
ADC_DRV adc_drv = {
.dcmf = ADC_DECIMATION_FILTER,
.xclk = ADC_SAMPLE_XCLK,
// .audio = 0,
.buf_idx = 709 // (1460*2 - 80)/(sizeof(ADC_DATA)/2) // циклический буфер на ~1420 замеров (см. sizeof(ADC_DATA))
// Если шаг заполнения 1 ms -> буфер на 1.4 сек
// Оптимизация под TCP: (TCP_MSS*2 - 80)/2 = (1460*2 - 80)/2 = 1420
};
void adc_int_handler(void *par) {
union {
uint16_t w[4];
uint32_t d[2];
}buf;
PADC_DRV p = par; // &adc_drv
// uint32_t adc_isr = HAL_ADC_READ32(REG_ADC_INTR_STS);
buf.d[0] = HAL_ADC_READ32(REG_ADC_FIFO_READ);
buf.d[1] = HAL_ADC_READ32(REG_ADC_FIFO_READ);
if(p->pbuf) {
PADC_DATA pd = p->pbuf + p->buf_tx;
pd->us0 = buf.w[3];
pd->us1 = buf.w[2];
if(p->buf_tx >= p->buf_idx) p->buf_tx = 0;
else p->buf_tx++;
if(p->buf_rx == p->buf_tx) {
p->overrun++;
if(p->overrun == 0) p->init = 2; // overrun
if(p->buf_rx >= p->buf_idx) p->buf_rx = 0;
else p->buf_rx++;
};
};
/* Clear ADC Status */
// HAL_ADC_WRITE32(REG_ADC_INTR_STS, adc_isr);
(void)HAL_ADC_READ32(REG_ADC_INTR_STS);
}
void adc_int_handler_audio(void *par) {
union {
uint16_t w[4];
uint32_t d[2];
}buf;
PADC_DRV p = par; // &adc_drv
buf.d[0] = HAL_ADC_READ32(REG_ADC_FIFO_READ);
buf.d[1] = HAL_ADC_READ32(REG_ADC_FIFO_READ);
if(p->pbuf) {
PADC_DATA pd = p->pbuf + p->buf_tx;
pd->us0 = buf.w[0];
pd->us1 = buf.w[1];
if(p->buf_tx >= p->buf_idx) p->buf_tx = 0;
else p->buf_tx++;
if(p->buf_rx == p->buf_tx) {
p->overrun++;
if(p->overrun == 0) p->init = 2; // overrun
if(p->buf_rx >= p->buf_idx) p->buf_rx = 0;
else p->buf_rx++;
};
pd = p->pbuf + p->buf_tx;
pd->us0 = buf.w[2];
pd->us1 = buf.w[3];
if(p->buf_tx >= p->buf_idx) p->buf_tx = 0;
else p->buf_tx++;
if(p->buf_rx == p->buf_tx) {
p->overrun++;
if(p->overrun == 0) p->init = 2; // overrun
if(p->buf_rx >= p->buf_idx) p->buf_rx = 0;
else p->buf_rx++;
};
};
/* Clear ADC Status */
(void)HAL_ADC_READ32(REG_ADC_INTR_STS);
}
size_t adc_getdata(void *pd, uint16_t cnt)
{
PADC_DRV p = &adc_drv;
if(p->init <= 0) return 0;
uint16_t *pus = (uint16_t *) pd;
taskDISABLE_INTERRUPTS();
uint16_t buf_rx = p->buf_rx;
*pus++ = cnt; // кол-во замеров
*pus++ = p->count + p->overrun; // индекс замера для анализа пропусков на стороне приемника
// если не пропущено, то равен прошлому + кол-во считанных замеров в прошлом блоке
p->count += cnt; // p->overrun = 0;
uint8_t *puc = (uint8_t *) pus;
if(cnt) {
uint16_t 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(ADC_DATA);
if(len) memcpy(puc, (void *)(p->pbuf + buf_rx), len);
if(lend) memcpy(puc + len, (void *)p->pbuf, lend *sizeof(ADC_DATA));
}
taskENABLE_INTERRUPTS();
return cnt * sizeof(ADC_DATA) + 4;
}
uint16_t adc_chkdata(uint16_t cnt)
{
PADC_DRV p = &adc_drv;
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_t)len) cnt = (uint16_t)len;
return cnt;
}
int adc_ws(TCP_SERV_CONN *ts_conn, char cmd)
{
PADC_DRV p = &adc_drv;
switch(cmd) {
case '*': // get_data
if(p->init <= 0) {
p->count = 0;
p->overrun = 0;
// p->errs = 0;
if(!p->pbuf) {
p->pbuf = zalloc((p->buf_idx + 1) * sizeof(ADC_DATA));
if(!p->pbuf) {
error_printf("Error create buffer!\n");
return -1;
};
p->buf_tx = 0;
p->buf_rx = 0;
};
ADCInit(p->audio, p->xclk, p->dcmf);
#ifdef ADC_USE_TIMER
// Initial a periodical timer
gtimer_init(&p->timer, ADC_USE_TIMER);
gtimer_start_periodical(&p->timer, 1000, (void*)adc_int_handler, (uint32_t)p);
rtl_printf("ADC Timer Period = %u us\n", &p->timer.hal_gtimer_adp.TimerLoadValueUs);
#else
if(p->audio)
ADCIrqInit(adc_int_handler_audio,(uint32)p, BIT_ADC_FIFO_FULL_EN | BIT_ADC_FIFO_RD_REQ_EN);
else
ADCIrqInit(adc_int_handler,(uint32)p, BIT_ADC_FIFO_FULL_EN | BIT_ADC_FIFO_RD_REQ_EN);
#endif
ADCEnable();
p->init = 1;
}
case 'g': // get_data
{
uint32_t i = adc_chkdata(p->buf_idx + 1);
if(i) {
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
i = mMIN((web_conn->msgbufsize / sizeof(ADC_DATA)), i);
if(websock_tx_frame(ts_conn, WS_OPCODE_BINARY | WS_FRAGMENT_FIN, web_conn->msgbuf, adc_getdata(web_conn->msgbuf, i)) != ERR_OK)
return -1;
}
return i;
}
case 'z': // deinit
if(p->init > 0) {
#ifdef ADC_USE_TIMER
gtimer_stop(&p->timer);
gtimer_deinit(&p->timer);
ADCDisable();
#else
ADCDisable();
ADCIrqDeInit();
#endif
ADCDeInit();
if(p->pbuf) {
free(p->pbuf);
p->pbuf = NULL;
}
p->init = -1;
return 0;
}
return 1;
case '?': // get count
return adc_chkdata(p->buf_idx + 1);
case 'i': // info init
return p->init;
case '1': // set ADC_DECIMATION_1
p->dcmf = 1;
return 0;
case '2': // set ADC_DECIMATION_2
p->dcmf = 2;
return 0;
case '4': // set ADC_DECIMATION_4
p->dcmf = 3;
return 0;
case '8': // set ADC_DECIMATION_8
p->dcmf = 4;
return 0;
case 'a': // set ADC_SAMPLE_XCLK x1
p->xclk = 0;
return 0;
case 'b': // set ADC_SAMPLE_XCLK x2
p->xclk = 1;
return 0;
case 'c': // set ADC_SAMPLE_XCLK x4
p->xclk = 2;
return 0;
case 'd': // set ADC_SAMPLE_XCLK x8
p->xclk = 3;
return 0;
case 'x': // set Audio mode
p->audio = 1;
return 0;
case 'y': // set 4 channel mode
p->audio = 0;
return 0;
}
return -1;
}