Implemented ADC gem.

Signed-off-by: Yilin Sun <imi415@imi.moe>
This commit is contained in:
Yilin Sun 2023-03-16 18:42:55 +08:00
parent a2eb84f757
commit f6659c7958
3 changed files with 89 additions and 33 deletions

View File

@ -3,9 +3,9 @@
## Implemented features:
```ruby
# Initialization: Takes a hash with configuration.
led_red = Machine::GPIO.new({pin: 38, mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
led_blue = Machine::GPIO.new({pin: 36, mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
led_green = Machine::GPIO.new({pin: 39, mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
led_red = Machine::GPIO.new(38, {mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
led_blue = Machine::GPIO.new(36, {mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
led_green = Machine::GPIO.new(39, {mode: Machine::GPIO::OUTPUT_OD, init: Machine::GPIO::ON})
```
```ruby
@ -16,3 +16,7 @@ led_red.on # Turn off the LED (Level is reversed)
led_red.toggle # Toggle output
```
```ruby
ana_bg = Machine::ADC.new(52) # BandGap
ana_bg.value
```

@ -1 +1 @@
Subproject commit 7f0aca015ec2bcad7d08d912163f135bdb238149
Subproject commit 6be5185d38c88b22a9681f4937d47d568651ab73

View File

@ -18,15 +18,39 @@
/* Gem private */
#include "machine-adc/src/adc.h"
#define ADC_IMPL_CHANNEL_ID(x) (x / 4)
#define ADC_IMPL_CHANNEL_MODE(x) (x % 4) /* */
#define ADC_IMPL_CHANNEL_REQUIRE_A(x) (((x % 4) != 1) ? true : false)
#define ADC_IMPL_CHANNEL_REQUIRE_B(x) (((x % 4) != 0) ? true : false)
#define ADC_IMPL_VREF_UV 3300000
#define ADC_IMPL_FULL_SCALE 65536
#define ADC_IMPL_FULL_SCALE_DIFF (ADC_IMPL_FULL_SCALE / 2)
#define ADC_IMPL_PORT(x) (x / 32)
#define ADC_IMPL_PIN(x) (x % 32)
#define ADC_EXT_CHANNEL_COUNT 10
#define ADC_TEMP_CHANNEL 26
#define ADC_EXT_CHANNEL_COUNT 5
/**
* Channel Allocation
* 0, 1, 2, 3: CH0A, CH0B, CH0DIFF, CH0AB(Not supported)
* 4, 5, 6, 7: CH1A, CH1B, CH1DIFF, CH1AB(Not supported)
* 8, 9, 10, 11: CH2A, CH2B, CH2DIFF, CH2AB(Not supported)
* 12, 13, 14, 15: CH3A, CH3B, CH3DIFF, CH3AB(Not supported)
* 16, 17, 18, 19: CH4A, CH4B, CH4DIFF, CH4AB(Not supported)
* 48: VDDA
* 52: BIAS_VREF_1V
* 106: TEMP_SENSOR
*/
/* CH0A, CH0B, CH1A, CH1B, ... CH4A, CH4B */
const static uint32_t s_adc_pin_table[ADC_EXT_CHANNEL_COUNT] = {
23, 16, 10, 11, 15, 12, 31, 32, 40, 41,
const static uint32_t s_adc_pin_table_a[ADC_EXT_CHANNEL_COUNT] = {
23, 10, 15, 31, 40,
};
const static uint32_t s_adc_pin_table_b[ADC_EXT_CHANNEL_COUNT] = {
16, 11, 12, 32, 41,
};
static bool s_adc_initialized = false;
@ -51,9 +75,11 @@ static void mrb_machine_adc_impl_init_peripheral(void) {
LPADC_Init(ADC0, &adc_cfg);
LPADC_DoOffsetCalibration(ADC0);
LPADC_DoAutoCalibration(ADC0);
NVIC_SetPriority(ADC0_IRQn, 5);
}
int mrb_machine_adc_impl_config(uint32_t pin) {
int mrb_machine_adc_impl_config(uint32_t channel) {
if (!s_adc_initialized) {
mrb_machine_adc_impl_init_peripheral();
@ -68,14 +94,28 @@ int mrb_machine_adc_impl_config(uint32_t pin) {
uint32_t iocon_mode = IOCON_PIO_ASW(1);
/* Configure IOCON only if this is an external channel */
if (pin < ADC_EXT_CHANNEL_COUNT) {
IOCON_PinMuxSet(IOCON, ADC_IMPL_PORT(s_adc_pin_table[pin]), ADC_IMPL_PIN(s_adc_pin_table[pin]), iocon_mode);
}
if (ADC_IMPL_CHANNEL_ID(channel) < ADC_EXT_CHANNEL_COUNT) {
uint32_t iocon_port;
uint32_t iocon_pin;
if (ADC_IMPL_CHANNEL_REQUIRE_A(channel)) {
iocon_port = ADC_IMPL_PORT(s_adc_pin_table_a[ADC_IMPL_CHANNEL_ID(channel)]);
iocon_pin = ADC_IMPL_PIN(s_adc_pin_table_a[ADC_IMPL_CHANNEL_ID(channel)]);
IOCON_PinMuxSet(IOCON, iocon_port, iocon_pin, iocon_mode);
}
if (ADC_IMPL_CHANNEL_REQUIRE_B(channel)) {
iocon_port = ADC_IMPL_PORT(s_adc_pin_table_b[ADC_IMPL_CHANNEL_ID(channel)]);
iocon_pin = ADC_IMPL_PIN(s_adc_pin_table_b[ADC_IMPL_CHANNEL_ID(channel)]);
IOCON_PinMuxSet(IOCON, iocon_port, iocon_pin, iocon_mode);
}
}
return 0;
}
int mrb_machine_adc_impl_read(uint32_t pin) {
int mrb_machine_adc_impl_read(uint32_t channel) {
lpadc_conv_command_config_t conv_cfg;
lpadc_conv_trigger_config_t trig_cfg;
@ -84,41 +124,53 @@ int mrb_machine_adc_impl_read(uint32_t pin) {
conv_cfg.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
if (pin < ADC_EXT_CHANNEL_COUNT) {
/* This is an external channel, Positive and negative pair shares the same channel */
lpadc_sample_channel_mode_t mode;
if ((pin % 2) == 0) {
mode = kLPADC_SampleChannelSingleEndSideA;
} else {
mode = kLPADC_SampleChannelSingleEndSideB;
}
conv_cfg.channelNumber = pin / 2;
conv_cfg.sampleChannelMode = mode;
} else if (pin == ADC_TEMP_CHANNEL) { /* Temperature channel requires differential sampling */
conv_cfg.channelNumber = pin;
conv_cfg.sampleChannelMode = kLPADC_SampleChannelDiffBothSide;
} else { /* Other internal channels */
conv_cfg.channelNumber = pin;
conv_cfg.sampleChannelMode = kLPADC_SampleChannelSingleEndSideA;
}
conv_cfg.channelNumber = ADC_IMPL_CHANNEL_ID(channel);
conv_cfg.sampleChannelMode = ADC_IMPL_CHANNEL_MODE(channel);
trig_cfg.targetCommandId = 15;
trig_cfg.enableHardwareTrigger = false;
trig_cfg.channelAFIFOSelect = 0U;
trig_cfg.channelBFIFOSelect = 0U;
LPADC_SetConvCommandConfig(ADC0, 15, &conv_cfg);
LPADC_SetConvTriggerConfig(ADC0, 0U, &trig_cfg);
/* Wait for 2 data if 2 channels are being sampled. */
if (ADC_IMPL_CHANNEL_MODE(channel) == kLPADC_SampleChannelDualSingleEndBothSide) {
ADC0->FCTRL[0] = ADC_FCTRL_FWMARK(1);
} else {
ADC0->FCTRL[0] = ADC_FCTRL_FWMARK(0);
}
LPADC_EnableInterrupts(ADC0, kLPADC_FIFO0WatermarkInterruptEnable);
EnableIRQ(ADC0_IRQn);
return 0;
LPADC_DoSoftwareTrigger(ADC0, 1U);
if (xSemaphoreTake(s_adc_semaphore, pdMS_TO_TICKS(500)) != pdTRUE) {
return 0;
}
uint8_t result_count = LPADC_GetConvResultCount(ADC0, 0);
lpadc_conv_result_t rslt;
for (uint8_t i = 0; i < result_count; i++) {
LPADC_GetConvResult(ADC0, &rslt, 0);
}
if (ADC_IMPL_CHANNEL_MODE(channel) == kLPADC_SampleChannelDiffBothSide) {
return (int)((int64_t)(int16_t)rslt.convValue * ADC_IMPL_VREF_UV / ADC_IMPL_FULL_SCALE_DIFF);
}
return (int)((int64_t)rslt.convValue * ADC_IMPL_VREF_UV / ADC_IMPL_FULL_SCALE);
}
void ADC0_IRQHandler(void) {
BaseType_t higher_priority_woken = pdFALSE;
DisableIRQ(ADC0_IRQn);
xSemaphoreGiveFromISR(s_adc_semaphore, &higher_priority_woken);
if (higher_priority_woken) {