213 lines
5.2 KiB
C
213 lines
5.2 KiB
C
/*
|
|
* adc.c
|
|
*
|
|
* todo:
|
|
* - use DMA
|
|
*/
|
|
|
|
#include "ch32x035_conf.h"
|
|
|
|
|
|
#define ADC_CHANNELS 8
|
|
|
|
#define CHAN_ENABLED 0x80
|
|
|
|
#define CHAN_MASK 0x7f
|
|
#define CHAN_IS_NORMAL 0
|
|
#define CHAN_IS_TOUCH 1
|
|
#define CHAN_IS_LIGHTSENSE 2
|
|
|
|
#define TOUCH_DEFAULT_CHARGE 0x4c
|
|
#define TOUCH_DEFAULT_THRESH 0x800
|
|
|
|
|
|
typedef struct AdcChan {
|
|
uint8_t chan; // ADC channel number
|
|
uint8_t type; // ADC channel type (use above defines)
|
|
uint8_t timing; // sample rate for normal channels, charge values for touch-type channels
|
|
uint8_t idx; // rawval index
|
|
uint16_t avg; // averaged output
|
|
uint16_t thresh; // touch threshold
|
|
uint16_t rawval[8]; // raw counts
|
|
} AdcChan;
|
|
|
|
|
|
static uint8_t adc_idx = 0;
|
|
static AdcChan adc_chan[ADC_CHANNELS] = {0};
|
|
|
|
|
|
|
|
void adc_init()
|
|
{
|
|
NVIC_InitTypeDef nvic;
|
|
ADC_InitTypeDef adc = {0};
|
|
|
|
// configure pin struct
|
|
for (uint8_t i = 0; i < ADC_CHANNELS; i++) {
|
|
adc_chan[i].type = CHAN_ENABLED | CHAN_IS_TOUCH;
|
|
adc_chan[i].timing = TOUCH_DEFAULT_CHARGE;
|
|
adc_chan[i].thresh = TOUCH_DEFAULT_THRESH;
|
|
}
|
|
|
|
adc_chan[0].chan = ADC_Channel_0;
|
|
adc_chan[0].timing = 0x4f;
|
|
adc_chan[0].thresh = 3380;
|
|
|
|
adc_chan[1].chan = ADC_Channel_1;
|
|
adc_chan[1].thresh = 3580;
|
|
|
|
adc_chan[2].chan = ADC_Channel_2;
|
|
adc_chan[2].thresh = 3380;
|
|
|
|
adc_chan[3].chan = ADC_Channel_3;
|
|
adc_chan[3].timing = 0x54;
|
|
adc_chan[3].thresh = 3450;
|
|
|
|
adc_chan[4].chan = ADC_Channel_4;
|
|
adc_chan[4].timing = 0x44;
|
|
adc_chan[4].thresh = 3000;
|
|
|
|
adc_chan[5].chan = ADC_Channel_8;
|
|
adc_chan[5].thresh = 3380;
|
|
|
|
adc_chan[6].chan = ADC_Channel_13;
|
|
adc_chan[6].timing = 0x4a;
|
|
adc_chan[6].thresh = 3550;
|
|
|
|
adc_chan[7].chan = ADC_Channel_9;
|
|
adc_chan[7].type = CHAN_IS_LIGHTSENSE;
|
|
adc_chan[7].timing = ADC_SampleTime_7Cycles;
|
|
|
|
|
|
// configure actual peripheral
|
|
ADC_DeInit(ADC1);
|
|
|
|
ADC_CLKConfig(ADC1, ADC_CLK_Div6);
|
|
|
|
adc.ADC_Mode = ADC_Mode_Independent;
|
|
adc.ADC_ScanConvMode = DISABLE;
|
|
adc.ADC_ContinuousConvMode = DISABLE;
|
|
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
|
adc.ADC_DataAlign = ADC_DataAlign_Right;
|
|
adc.ADC_NbrOfChannel = 1;
|
|
ADC_Init(ADC1, &adc);
|
|
|
|
ADC_Cmd(ADC1, ENABLE);
|
|
|
|
// configure ADC interrupt
|
|
nvic.NVIC_IRQChannel = ADC1_IRQn;
|
|
nvic.NVIC_IRQChannelPreemptionPriority = 0;
|
|
nvic.NVIC_IRQChannelSubPriority = 4;
|
|
nvic.NVIC_IRQChannelCmd = ENABLE;
|
|
|
|
// enable interrupt
|
|
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
|
|
ADC1->STATR = 0; // clear flags
|
|
NVIC_Init(&nvic);
|
|
}
|
|
|
|
int8_t adc_get_tkey(uint8_t key_idx)
|
|
{
|
|
if (key_idx > ADC_CHANNELS)
|
|
return 0;
|
|
|
|
if (adc_chan[key_idx].avg < adc_chan[key_idx].thresh) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void adc_next()
|
|
{
|
|
AdcChan *chan;
|
|
volatile uint8_t timeout = 0;
|
|
|
|
// todo:
|
|
// lightsense is timing-sensitive. will need to have this as an overridden channel?
|
|
|
|
// get next index
|
|
// so long as the channel is enabled
|
|
do {
|
|
adc_idx++;
|
|
adc_idx %= (sizeof(adc_chan) / sizeof(adc_chan[0]));
|
|
if (adc_chan[adc_idx].type & CHAN_ENABLED)
|
|
break;
|
|
} while (timeout++ < ADC_CHANNELS);
|
|
|
|
// if nothing is enabled, break out
|
|
if (timeout == ADC_CHANNELS)
|
|
return;
|
|
|
|
chan = &adc_chan[adc_idx];
|
|
|
|
// configure sampling
|
|
switch (chan->type & ~CHAN_ENABLED) {
|
|
case CHAN_IS_NORMAL: {
|
|
TKey1->CTLR1 &= ~ADC_TKENABLE; // disable TouchKey
|
|
|
|
// we don't have any
|
|
break;
|
|
}
|
|
case CHAN_IS_TOUCH: {
|
|
TKey1->CTLR1 |= ADC_TKENABLE; // enable TouchKey
|
|
|
|
ADC_RegularChannelConfig(ADC1, chan->chan, 1, ADC_SampleTime_11Cycles);
|
|
TKey1->IDATAR1 = chan->timing & 0xf0; // R32_TKEY1_CHGOFFSET Charging Time
|
|
TKey1->RDATAR = chan->timing & 0x0f; // R32_TKEY1_ACT_DCG Discharging Time
|
|
// per the RM, setting RDATAR (ACT_DCG) starts the conversion
|
|
|
|
break;
|
|
}
|
|
case CHAN_IS_LIGHTSENSE: {
|
|
TKey1->CTLR1 &= ~ADC_TKENABLE; // disable TouchKey
|
|
|
|
// todo: use thresh as a "timeout" value maybe?
|
|
// this way the other channels can be sampled, and each time we pass by
|
|
// the lightsensor, if it isn't time to sample it, it is skipped.
|
|
ADC_RegularChannelConfig(ADC1, chan->chan, 1, chan->timing & 0xf);
|
|
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void adc_isr()
|
|
{
|
|
AdcChan *chan;
|
|
uint32_t v = 0;
|
|
uint8_t amax, ashift;
|
|
|
|
chan = &adc_chan[adc_idx];
|
|
|
|
// conversion done
|
|
if (ADC1->STATR & ADC_EOC) {
|
|
chan->rawval[chan->idx] = ADC1->RDATAR;
|
|
|
|
// how many averages should we do?
|
|
switch (chan->type & CHAN_MASK) {
|
|
case CHAN_IS_TOUCH: {
|
|
amax = 4;
|
|
ashift = 2;
|
|
break;
|
|
}
|
|
default: {
|
|
amax = 8;
|
|
ashift = 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
chan->idx++;
|
|
if (chan->idx >= amax) {
|
|
chan->idx = 0;
|
|
for (int i = 0; i < amax; i++) {
|
|
v += chan->rawval[i];
|
|
}
|
|
chan->avg = v >> ashift;
|
|
}
|
|
}
|
|
|
|
// flags cleared by software
|
|
ADC1->STATR = 0;
|
|
} |