initial WIP
lots of code copied over, things filled in to hopefully get the LED matrix lighting up. untested.
This commit is contained in:
186
firmware/app/driver/adc.c
Normal file
186
firmware/app/driver/adc.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 0x88
|
||||
#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()
|
||||
{
|
||||
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[1].chan = ADC_Channel_1;
|
||||
adc_chan[2].chan = ADC_Channel_2;
|
||||
adc_chan[3].chan = ADC_Channel_3;
|
||||
adc_chan[4].chan = ADC_Channel_4;
|
||||
adc_chan[5].chan = ADC_Channel_8;
|
||||
adc_chan[6].chan = ADC_Channel_13;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t timeout = 0;
|
||||
|
||||
// todo:
|
||||
// lightsense is timing-sensitive. will need to have this as an overridden channel?
|
||||
|
||||
// get next index
|
||||
adc_idx++;
|
||||
if (adc_idx > (sizeof(adc_chan) / sizeof(adc_chan[0]))) {
|
||||
adc_idx = 0;
|
||||
}
|
||||
|
||||
// so long as the channel is enabled
|
||||
while (!(adc_chan[adc_idx].type & CHAN_ENABLED) && (timeout++ < ADC_CHANNELS)) {
|
||||
adc_idx++;
|
||||
}
|
||||
|
||||
// if nothing is enabled, break out
|
||||
if (timeout == ADC_CHANNELS)
|
||||
return;
|
||||
|
||||
chan = &adc_chan[adc_idx];
|
||||
|
||||
// configure sampling
|
||||
switch (chan->type) {
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user