samv7: add support for ADC conversion triggering with PWM

This commit enhances ADC and PWM drivers with option to trigger ADC
conversion with events generated by PWM comparison units.

The generation of PWM events is handled by comparison units set up from
Kconfig option SAMV7_PWMx_TRIGn. ADC triggering from PWM can be selected
by AFECn_PWMTRIG option.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
This commit is contained in:
Michal Lenc 2023-02-24 16:33:42 +01:00 committed by Xiang Xiao
parent d77b53957c
commit 49ce1c5df7
3 changed files with 391 additions and 7 deletions

View File

@ -387,10 +387,18 @@ config SAMV7_ADC
bool "12-bit ADC Controller (ADC)"
default n
config SAMV7_AFEC_SWTRIG
bool
default n
config SAMV7_AFEC_TIOATRIG
bool
default n
config SAMV7_AFEC_PWMTRIG
bool
default n
menuconfig SAMV7_AFEC0
bool "Analog Front End 0 (AFEC0)"
default n
@ -437,8 +445,15 @@ config SAMV7_AFEC0_TIOATRIG
---help---
Use output of Timer/Counter as a trigger.
config SAMV7_AFEC0_PWMTRIG
bool "PWM trigger"
select SAMV7_AFEC_PWMTRIG
---help---
Use output of PWM as a trigger.
config SAMV7_AFEC0_SWTRIG
bool "Software trigger"
select SAMV7_AFEC_SWTRIG
---help---
Use software trigger.
@ -462,6 +477,17 @@ config SAMV7_AFEC0_TIOACHAN
endif #SAMV7_AFEC0_TIOATRIG
if SAMV7_AFEC0_PWMTRIG
config SAMV7_AFEC0_PWMEVENT
int "PWM Event Line"
default 0
range 0 1
---help---
PWM event line used for AFEC triggering. Can be either 0 or 1.
endif # SAMV7_AFEC0_PWM_TRIG
endif # SAMV7_AFEC0
menuconfig SAMV7_AFEC1
@ -510,8 +536,15 @@ config SAMV7_AFEC1_TIOATRIG
---help---
Use output of Timer/Counter as a trigger.
config SAMV7_AFEC1_PWMTRIG
bool "PWM trigger"
select SAMV7_AFEC_PWMTRIG
---help---
Use output of PWM as a trigger.
config SAMV7_AFEC1_SWTRIG
bool "Software trigger"
select SAMV7_AFEC_SWTRIG
---help---
Use software trigger.
@ -535,6 +568,17 @@ config SAMV7_AFEC1_TIOACHAN
endif #SAMV7_AFEC1_TIOATRIG
if SAMV7_AFEC1_PWMTRIG
config SAMV7_AFEC1_PWMEVENT
int "PWM Event Line"
default 0
range 0 1
---help---
PWM event line used for AFEC triggering. Can be either 0 or 1.
endif # SAMV7_AFEC1_PWM_TRIG
endif # SAMV7_AFEC1
config SAMV7_MCAN0
@ -656,6 +700,94 @@ config SAMV7_PWM0_CH3_COMP
endif
menu "PWM Comparison units"
config SAMV7_PWM0_TRIG0
int "PWM0 Comparison Unit 0 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG1
int "PWM0 Comparison Unit 1 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG2
int "PWM0 Comparison Unit 2 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG3
int "PWM0 Comparison Unit 3 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG4
int "PWM0 Comparison Unit 4 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG5
int "PWM0 Comparison Unit 5 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG6
int "PWM0 Comparison Unit 6 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM0_TRIG7
int "PWM0 Comparison Unit 7 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM
endmenu
config SAMV7_PWM0_EVENT0
bool "Generate trigger on Event Line 0"
default n
---help---
PWM allows trigger generation on event lines. This can be
used for ADC conversion triggering for example. It is
necessary to set a comparison unit in order to generate an
event.
config SAMV7_PWM0_EVENT1
bool "Generate trigger on Event Line 1"
default n
---help---
PWM allows trigger generation on event lines. This can be
used for ADC conversion triggering for example. It is
necessary to set a comparison unit in order to generate an
event.
endif
menuconfig SAMV7_PWM1
@ -715,6 +847,92 @@ config SAMV7_PWM1_CH3_COMP
endif
menu "PWM Comparison units"
config SAMV7_PWM1_TRIG0
int "PWM1 Comparison Unit 0 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG1
int "PWM1 Comparison Unit 1 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG2
int "PWM1 Comparison Unit 2 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG3
int "PWM1 Comparison Unit 3 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG4
int "PWM1 Comparison Unit 4 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG5
int "PWM1 Comparison Unit 5 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG6
int "PWM1 Comparison Unit 6 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
config SAMV7_PWM1_TRIG7
int "PWM1 Comparison Unit 7 value"
range 0 100
default 0
---help---
Timer value on which the comparison unit generates
an event. Value is in percentages.
endmenu
config SAMV7_PWM1_EVENT0
bool "Generate trigger on Event Line 0"
default n
---help---
PWM allows trigger generation on event lines. This can be
used for ADC conversion triggering for example. It is
necessary to set a comparison unit in order to generate an
event.
config SAMV7_PWM1_EVENT1
bool "Generate trigger on Event Line 1"
default n
---help---
PWM allows trigger generation on event lines. This can be
used for ADC conversion triggering for example. It is
necessary to set a comparison unit in order to generate an
event.
endif
config SAMV7_QSPI

View File

@ -94,6 +94,7 @@ struct samv7_dev_s
uint8_t resolution; /* ADC resolution (SAMV7_AFECn_RES) */
uint8_t trigger; /* ADC trigger (software, timer...) */
uint8_t timer_channel; /* Timer channel to trigger ADC */
uint8_t event_line; /* PWM event line to trigger ADC */
uint32_t frequency; /* Frequency of the timer */
int irq; /* ADC IRQ number */
int pid; /* ADC PID number */
@ -177,12 +178,15 @@ static struct samv7_dev_s g_adcpriv0 =
.intf = 0,
.initialized = 0,
.resolution = CONFIG_SAMV7_AFEC0_RES,
#ifdef CONFIG_SAMV7_AFEC0_SWTRIG
.trigger = 0,
#else
#if defined (CONFIG_SAMV7_AFEC0_PWMTRIG)
.trigger = 2,
.event_line = CONFIG_SAMV7_AFEC0_PWMEVENT,
#elif defined (CONFIG_SAMV7_AFEC0_TIOATRIG)
.trigger = 1,
.timer_channel = CONFIG_SAMV7_AFEC0_TIOACHAN,
.frequency = CONFIG_SAMV7_AFEC0_TIOAFREQ,
#else
.trigger = 0,
#endif
.base = SAM_AFEC0_BASE,
};
@ -217,12 +221,15 @@ static struct samv7_dev_s g_adcpriv1 =
.intf = 1,
.initialized = 0,
.resolution = CONFIG_SAMV7_AFEC1_RES,
#ifdef CONFIG_SAMV7_AFEC1_SWTRIG
.trigger = 0,
#else
#if defined (CONFIG_SAMV7_AFEC1_PWMTRIG)
.trigger = 2,
.event_line = CONFIG_SAMV7_AFEC0_PWMEVENT,
#elif defined (CONFIG_SAMV7_AFEC1_TIOATRIG)
.trigger = 1,
.timer_channel = CONFIG_SAMV7_AFEC1_TIOACHAN,
.frequency = CONFIG_SAMV7_AFEC1_TIOAFREQ,
#else
.trigger = 0,
#endif
.base = SAM_AFEC1_BASE,
};
@ -624,7 +631,9 @@ static int sam_afec_trigger(struct samv7_dev_s *priv)
regval &= ~AFEC_MR_TRGSEL_MASK;
afec_putreg(priv, SAM_AFEC_MR_OFFSET, regval);
}
#elif CONFIG_SAMV7_AFEC_TIOATRIG
#endif
#ifdef CONFIG_SAMV7_AFEC_TIOATRIG
if (priv->trigger == 1)
{
ainfo("Setup timer/counter trigger\n");
@ -655,6 +664,31 @@ static int sam_afec_trigger(struct samv7_dev_s *priv)
afec_putreg(priv, SAM_AFEC_MR_OFFSET, regval);
}
#endif
#ifdef CONFIG_SAMV7_AFEC_PWMTRIG
if (priv->trigger == 2)
{
regval = afec_getreg(priv, SAM_AFEC_MR_OFFSET);
regval &= ~AFEC_MR_TRGSEL_MASK;
if (priv->event_line == 1)
{
/* PWM Event Line 1 is used for AFEC triggering */
regval |= AFEC_MR_TRGSEL_PWM1;
}
else
{
/* PWM Event Line 0 is used for AFEC triggering */
regval |= AFEC_MR_TRGSEL_PWM0;
}
regval |= AFEC_MR_TRGEN;
afec_putreg(priv, SAM_AFEC_MR_OFFSET, regval);
}
#endif
return ret;

View File

@ -59,8 +59,10 @@
#endif
#define CHANNEL_OFFSET 0x20
#define COMP_OFFSET 0x10
#define CLK_FREQ BOARD_MCK_FREQUENCY
#define PWM_RES 65535
#define COMP_UNITS_NUM 8
/****************************************************************************
* Pre-processor Definitions
@ -77,10 +79,18 @@ struct sam_pwm_channel_s
gpio_pinset_t pin_l; /* PWM L output pin */
};
struct sam_pwm_comparison_s
{
int comp_vals[COMP_UNITS_NUM];
int event0;
int event1;
};
struct sam_pwm_s
{
const struct pwm_ops_s *ops; /* PWM operations */
const struct sam_pwm_channel_s *channels;
const struct sam_pwm_comparison_s *comparison;
uint8_t channels_num; /* Number of channels */
uintptr_t base; /* Base address of peripheral register */
};
@ -158,10 +168,32 @@ static struct sam_pwm_channel_s g_pwm0_channels[] =
#endif
};
static struct sam_pwm_comparison_s g_pwm0_comparison =
{
.comp_vals =
{
CONFIG_SAMV7_PWM0_TRIG0, CONFIG_SAMV7_PWM0_TRIG1,
CONFIG_SAMV7_PWM0_TRIG2, CONFIG_SAMV7_PWM0_TRIG3,
CONFIG_SAMV7_PWM0_TRIG4, CONFIG_SAMV7_PWM0_TRIG5,
CONFIG_SAMV7_PWM0_TRIG6, CONFIG_SAMV7_PWM0_TRIG7
},
#ifdef CONFIG_SAMV7_PWM0_EVENT0
.event0 = 1,
#else
.event0 = 0,
#endif
#ifdef CONFIG_SAMV7_PWM0_EVENT1
.event1 = 1,
#else
.event1 = 0,
#endif
};
static struct sam_pwm_s g_pwm0 =
{
.ops = &g_pwmops,
.channels = g_pwm0_channels,
.comparison = &g_pwm0_comparison,
.channels_num = PWM0_NCHANNELS,
.base = SAM_PWM0_BASE,
};
@ -217,10 +249,32 @@ static struct sam_pwm_channel_s g_pwm1_channels[] =
#endif
}; /* CONFIG_SAMV7_PWM1 */
static struct sam_pwm_comparison_s g_pwm1_comparison =
{
.comp_vals =
{
CONFIG_SAMV7_PWM1_TRIG0, CONFIG_SAMV7_PWM1_TRIG1,
CONFIG_SAMV7_PWM1_TRIG2, CONFIG_SAMV7_PWM1_TRIG3,
CONFIG_SAMV7_PWM1_TRIG4, CONFIG_SAMV7_PWM1_TRIG5,
CONFIG_SAMV7_PWM1_TRIG6, CONFIG_SAMV7_PWM1_TRIG7
},
#ifdef CONFIG_SAMV7_PWM0_EVENT0
.event0 = 1,
#else
.event0 = 0,
#endif
#ifdef CONFIG_SAMV7_PWM0_EVENT1
.event1 = 1,
#else
.event1 = 0,
#endif
};
static struct sam_pwm_s g_pwm1 =
{
.ops = &g_pwmops,
.channels = g_pwm1_channels,
.comparison = &g_pwm1_comparison,
.channels_num = PWM1_NCHANNELS,
.base = SAM_PWM1_BASE,
};
@ -241,6 +295,7 @@ static void pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel,
ub16_t duty);
static void pwm_set_freq(struct pwm_lowerhalf_s *dev, uint8_t channel,
uint32_t frequency);
static void pwm_set_comparison(struct pwm_lowerhalf_s *dev);
/****************************************************************************
* Private Functions
@ -370,6 +425,81 @@ static void pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel,
pwm_putreg(priv, SAMV7_PWM_ENA, regval);
}
/****************************************************************************
* Name: pwm_set_comparison
*
* Description:
* Set comparison units.
*
* Input Parameters:
* dev - A reference to the lower half PWM driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static void pwm_set_comparison(struct pwm_lowerhalf_s *dev)
{
struct sam_pwm_s *priv = (struct sam_pwm_s *)dev;
uint16_t period;
uint16_t width;
uint16_t comp_value;
uint8_t comp_en;
int i;
/* Get the period value */
period = pwm_getreg(priv, SAMV7_PWM_CPRDX);
/* Compute PWM width (count value to set PWM low) */
comp_en = 0;
for (i = 0; i < COMP_UNITS_NUM; i++)
{
if (priv->comparison->comp_vals[i] == 0)
{
continue;
}
comp_value = b16divi(uitoub16(priv->comparison->comp_vals[i]), 100);
width = b16toi(comp_value * period + b16HALF);
/* Generate event line */
if (pwm_getreg(priv, SAMV7_PWM_CMPMX + COMP_OFFSET * i) & CMPM_CEN)
{
/* Use update register if comparision unit is used */
pwm_putreg(priv, SAMV7_PWM_CMPVUPDX + COMP_OFFSET * i, width);
pwm_putreg(priv, SAMV7_PWM_CMPMUPDX + COMP_OFFSET * i, CMPM_CEN);
}
else
{
pwm_putreg(priv, SAMV7_PWM_CMPVX + COMP_OFFSET * i, width);
pwm_putreg(priv, SAMV7_PWM_CMPMX + COMP_OFFSET * i, CMPM_CEN);
}
comp_en += 1 << i;
}
if (priv->comparison->event0)
{
/* Enable output on Event Line 0 */
pwm_putreg(priv, SAMV7_PWM_ELMR1, comp_en);
}
if (priv->comparison->event1)
{
/* Enable output on Event Line 1 */
pwm_putreg(priv, SAMV7_PWM_ELMR2, comp_en);
}
}
/****************************************************************************
* Name: pwm_setup
*
@ -552,6 +682,8 @@ static int pwm_start(struct pwm_lowerhalf_s *dev,
pwm_set_output(dev, priv->channels[0].channel, info->duty);
#endif
pwm_set_comparison(dev);
return OK;
}