cc3200: Add period set method to the Timer class.
This commit is contained in:
parent
417205623a
commit
84d11b5e53
|
@ -69,14 +69,15 @@
|
||||||
///
|
///
|
||||||
/// tim1 = pyb.Timer(2, mode=Timer.EVENT_COUNT) # initialize it capture mode
|
/// tim1 = pyb.Timer(2, mode=Timer.EVENT_COUNT) # initialize it capture mode
|
||||||
/// tim2 = pyb.Timer(1, mode=Timer.PWM) # initialize it in PWM mode
|
/// tim2 = pyb.Timer(1, mode=Timer.PWM) # initialize it in PWM mode
|
||||||
/// tim_ch = tim1.channel(Timer.A, freq=1, polarity=Timer.POSITIVE) # start the PWM on channel B with a 50% duty cycle
|
/// tim_ch = tim1.channel(Timer.A, freq=1, polarity=Timer.POSITIVE) # start the event counter with a frequency of 1Hz and triggered by positive edges
|
||||||
/// tim_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=50) # start the event counter with a frequency of 1Hz and triggered by positive edges
|
/// tim_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=50) # start the PWM on channel B with a 50% duty cycle
|
||||||
/// tim_ch.time() # get the current time in usec (can also be set)
|
/// tim_ch.time() # get the current time in usec (can also be set)
|
||||||
/// tim_ch.freq(20) # set the frequency (can also get)
|
/// tim_ch.freq(20) # set the frequency (can also get)
|
||||||
/// tim_ch.duty_cycle(30) # set the duty cycle to 30% (can also get)
|
/// tim_ch.duty_cycle(30) # set the duty cycle to 30% (can also get)
|
||||||
/// tim_ch.duty_cycle(30, Timer.NEGATIVE) # set the duty cycle to 30% and change the polarity to negative
|
/// tim_ch.duty_cycle(30, Timer.NEGATIVE) # set the duty cycle to 30% and change the polarity to negative
|
||||||
/// tim_ch.event_count() # get the number of captured events
|
/// tim_ch.event_count() # get the number of captured events
|
||||||
/// tim_ch.event_time() # get the the time of the last captured event
|
/// tim_ch.event_time() # get the the time of the last captured event
|
||||||
|
/// tim_ch.period(2000000) # change the period to 2 seconds
|
||||||
///
|
///
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
|
@ -104,6 +105,7 @@ typedef struct _pyb_timer_channel_obj_t {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
struct _pyb_timer_obj_t *timer;
|
struct _pyb_timer_obj_t *timer;
|
||||||
uint32_t frequency;
|
uint32_t frequency;
|
||||||
|
uint32_t period;
|
||||||
uint16_t channel;
|
uint16_t channel;
|
||||||
uint8_t polarity;
|
uint8_t polarity;
|
||||||
uint8_t duty_cycle;
|
uint8_t duty_cycle;
|
||||||
|
@ -208,20 +210,23 @@ STATIC void timer_disable (pyb_timer_obj_t *tim) {
|
||||||
STATIC uint32_t compute_prescaler_period_and_match_value(pyb_timer_channel_obj_t *ch, uint32_t *period_out, uint32_t *match_out) {
|
STATIC uint32_t compute_prescaler_period_and_match_value(pyb_timer_channel_obj_t *ch, uint32_t *period_out, uint32_t *match_out) {
|
||||||
uint32_t maxcount = (ch->channel == (TIMER_A | TIMER_B)) ? 0xFFFFFFFF : 0xFFFF;
|
uint32_t maxcount = (ch->channel == (TIMER_A | TIMER_B)) ? 0xFFFFFFFF : 0xFFFF;
|
||||||
uint32_t prescaler;
|
uint32_t prescaler;
|
||||||
uint32_t period = PYBTIMER_SRC_FREQ_HZ / ch->frequency;
|
uint32_t period_c = (ch->frequency > 0) ? PYBTIMER_SRC_FREQ_HZ / ch->frequency : ((PYBTIMER_SRC_FREQ_HZ / 1000000) * ch->period);
|
||||||
|
|
||||||
period = MAX(1, period) - 1;
|
period_c = MAX(1, period_c) - 1;
|
||||||
prescaler = period >> 16;
|
if (period_c == 0) {
|
||||||
*period_out = period;
|
goto error;
|
||||||
|
}
|
||||||
|
prescaler = period_c >> 16;
|
||||||
|
*period_out = period_c;
|
||||||
if (prescaler > 0xFF && maxcount == 0xFFFF) {
|
if (prescaler > 0xFF && maxcount == 0xFFFF) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
// check limit values for the duty cycle
|
// check limit values for the duty cycle
|
||||||
if (ch->duty_cycle == 0) {
|
if (ch->duty_cycle == 0) {
|
||||||
*match_out = period - 1;
|
*match_out = period_c - 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*match_out = period - ((period * ch->duty_cycle) / 100);
|
*match_out = period_c - ((period_c * ch->duty_cycle) / 100);
|
||||||
}
|
}
|
||||||
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM && (*match_out > 0xFFFF)) {
|
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM && (*match_out > 0xFFFF)) {
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -240,15 +245,15 @@ STATIC void timer_init (pyb_timer_obj_t *tim) {
|
||||||
|
|
||||||
STATIC void timer_channel_init (pyb_timer_channel_obj_t *ch) {
|
STATIC void timer_channel_init (pyb_timer_channel_obj_t *ch) {
|
||||||
// calculate the period, the prescaler and the match value
|
// calculate the period, the prescaler and the match value
|
||||||
uint32_t period;
|
uint32_t period_c;
|
||||||
uint32_t match;
|
uint32_t match;
|
||||||
uint32_t prescaler = compute_prescaler_period_and_match_value(ch, &period, &match);
|
uint32_t prescaler = compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||||
|
|
||||||
// set the prescaler
|
// set the prescaler
|
||||||
MAP_TimerPrescaleSet(ch->timer->timer, ch->channel, (prescaler < 0xFF) ? prescaler : 0);
|
MAP_TimerPrescaleSet(ch->timer->timer, ch->channel, (prescaler < 0xFF) ? prescaler : 0);
|
||||||
|
|
||||||
// set the load value
|
// set the load value
|
||||||
MAP_TimerLoadSet(ch->timer->timer, ch->channel, period);
|
MAP_TimerLoadSet(ch->timer->timer, ch->channel, period_c);
|
||||||
|
|
||||||
// configure the pwm if we are in such mode
|
// configure the pwm if we are in such mode
|
||||||
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM) {
|
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM) {
|
||||||
|
@ -412,14 +417,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
|
||||||
///
|
///
|
||||||
/// Keyword arguments:
|
/// Keyword arguments:
|
||||||
///
|
///
|
||||||
/// - `freq` - specifies the frequency in Hz
|
/// - `freq` - specifies the frequency in Hz.
|
||||||
///
|
/// - `period` - specifies the period in microseconds.
|
||||||
/// - `polarity` - in PWM specifies the polarity of the pulse. In capture mode specifies the edge to capture.
|
/// - `polarity` - in PWM specifies the polarity of the pulse. In capture mode specifies the edge to capture.
|
||||||
/// in order to capture on both negative and positive edges, make it = Timer.POSITIVE | Timer.NEGATIVE.
|
/// in order to capture on both negative and positive edges, make it = Timer.POSITIVE | Timer.NEGATIVE.
|
||||||
|
/// - `duty_cycle` - sets the duty cycle value
|
||||||
///
|
///
|
||||||
STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PYBTIMER_POLARITY_POS} },
|
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PYBTIMER_POLARITY_POS} },
|
||||||
{ MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
{ MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
};
|
};
|
||||||
|
@ -454,12 +461,16 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp
|
||||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
// check the frequency
|
// throw an exception if both frequency and period are given
|
||||||
if (args[0].u_int <= 0) {
|
if (args[0].u_int != 0 && args[1].u_int != 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
// check that the polarity is not both in pwm mode
|
// check that at least one of them has a valid value
|
||||||
if ((tim->config & TIMER_A) == TIMER_CFG_A_PWM && args[1].u_int == (PYBTIMER_POLARITY_POS | PYBTIMER_POLARITY_NEG)) {
|
if (args[0].u_int <= 0 && args[1].u_int <= 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
// check that the polarity is not 'both' in pwm mode
|
||||||
|
if ((tim->config & TIMER_A) == TIMER_CFG_A_PWM && args[2].u_int == (PYBTIMER_POLARITY_POS | PYBTIMER_POLARITY_NEG)) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +482,9 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp
|
||||||
|
|
||||||
// get the frequency the polarity and the duty cycle
|
// get the frequency the polarity and the duty cycle
|
||||||
ch->frequency = args[0].u_int;
|
ch->frequency = args[0].u_int;
|
||||||
ch->polarity = args[1].u_int;
|
ch->period = args[1].u_int;
|
||||||
ch->duty_cycle = MIN(100, MAX(0, args[2].u_int));
|
ch->polarity = args[2].u_int;
|
||||||
|
ch->duty_cycle = MIN(100, MAX(0, args[3].u_int));
|
||||||
|
|
||||||
timer_channel_init(ch);
|
timer_channel_init(ch);
|
||||||
|
|
||||||
|
@ -611,39 +623,65 @@ STATIC mp_obj_t pyb_timer_channel_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
||||||
return mp_obj_new_int(ch->frequency);
|
return mp_obj_new_int(ch->frequency);
|
||||||
} else {
|
} else {
|
||||||
// set
|
// set
|
||||||
ch->frequency = mp_obj_get_int(args[1]);
|
int32_t _frequency = mp_obj_get_int(args[1]);
|
||||||
|
if (_frequency <= 0) {
|
||||||
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||||
|
}
|
||||||
|
ch->frequency = _frequency;
|
||||||
|
ch->period = 1000000 / _frequency;
|
||||||
timer_channel_init(ch);
|
timer_channel_init(ch);
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_freq_obj, 1, 2, pyb_timer_channel_freq);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_freq_obj, 1, 2, pyb_timer_channel_freq);
|
||||||
|
|
||||||
|
/// \method period([value])
|
||||||
|
/// get or set the period of the timer channel in microseconds
|
||||||
|
STATIC mp_obj_t pyb_timer_channel_period(mp_uint_t n_args, const mp_obj_t *args) {
|
||||||
|
pyb_timer_channel_obj_t *ch = args[0];
|
||||||
|
if (n_args == 1) {
|
||||||
|
// get
|
||||||
|
return mp_obj_new_int(ch->period);
|
||||||
|
} else {
|
||||||
|
// set
|
||||||
|
int32_t _period = mp_obj_get_int(args[1]);
|
||||||
|
if (_period <= 0) {
|
||||||
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||||
|
}
|
||||||
|
ch->period = _period;
|
||||||
|
ch->frequency = 1000000 / _period;
|
||||||
|
timer_channel_init(ch);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_period_obj, 1, 2, pyb_timer_channel_period);
|
||||||
|
|
||||||
/// \method time([value])
|
/// \method time([value])
|
||||||
/// get or set the value of the timer channel in microseconds
|
/// get or set the value of the timer channel in microseconds
|
||||||
STATIC mp_obj_t pyb_timer_channel_time(mp_uint_t n_args, const mp_obj_t *args) {
|
STATIC mp_obj_t pyb_timer_channel_time(mp_uint_t n_args, const mp_obj_t *args) {
|
||||||
pyb_timer_channel_obj_t *ch = args[0];
|
pyb_timer_channel_obj_t *ch = args[0];
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
// calculate the period, the prescaler and the match value
|
// calculate the period, the prescaler and the match value
|
||||||
uint32_t period;
|
uint32_t period_c;
|
||||||
uint32_t match;
|
uint32_t match;
|
||||||
(void)compute_prescaler_period_and_match_value(ch, &period, &match);
|
(void)compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||||
if (n_args == 1) {
|
if (n_args == 1) {
|
||||||
// get
|
// get
|
||||||
value = (ch->channel == TIMER_B) ? HWREG(ch->timer->timer + TIMER_O_TBV) : HWREG(ch->timer->timer + TIMER_O_TAV);
|
value = (ch->channel == TIMER_B) ? HWREG(ch->timer->timer + TIMER_O_TBV) : HWREG(ch->timer->timer + TIMER_O_TAV);
|
||||||
// return the current timer value in microseconds
|
// return the current timer value in microseconds
|
||||||
// substract value to period since we are always operating in count-down mode
|
// substract value to period since we are always operating in count-down mode
|
||||||
uint32_t time_t = (1000 * (period - value)) / period;
|
uint32_t time_t = (1000 * (period_c - value)) / period_c;
|
||||||
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// set
|
// set
|
||||||
value = (mp_obj_get_int(args[1]) * ((ch->frequency * period) / 1000)) / 1000;
|
value = (mp_obj_get_int(args[1]) * ((ch->frequency * period_c) / 1000)) / 1000;
|
||||||
if ((value > 0xFFFF) && (ch->timer->config & TIMER_CFG_SPLIT_PAIR)) {
|
if ((value > 0xFFFF) && (ch->timer->config & TIMER_CFG_SPLIT_PAIR)) {
|
||||||
// this exceeds the maximum value of a 16-bit timer
|
// this exceeds the maximum value of a 16-bit timer
|
||||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||||
}
|
}
|
||||||
// write period minus value since we are always operating in count-down mode
|
// write period minus value since we are always operating in count-down mode
|
||||||
TimerValueSet (ch->timer->timer, ch->channel, (period - value));
|
TimerValueSet (ch->timer->timer, ch->channel, (period_c - value));
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,12 +700,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_count_obj, pyb_timer_ch
|
||||||
STATIC mp_obj_t pyb_timer_channel_event_time(mp_obj_t self_in) {
|
STATIC mp_obj_t pyb_timer_channel_event_time(mp_obj_t self_in) {
|
||||||
pyb_timer_channel_obj_t *ch = self_in;
|
pyb_timer_channel_obj_t *ch = self_in;
|
||||||
// calculate the period, the prescaler and the match value
|
// calculate the period, the prescaler and the match value
|
||||||
uint32_t period;
|
uint32_t period_c;
|
||||||
uint32_t match;
|
uint32_t match;
|
||||||
(void)compute_prescaler_period_and_match_value(ch, &period, &match);
|
(void)compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||||
uint32_t value = MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel);
|
uint32_t value = MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel);
|
||||||
// substract value to period since we are always operating in count-down mode
|
// substract value to period since we are always operating in count-down mode
|
||||||
uint32_t time_t = (1000 * (period - value)) / period;
|
uint32_t time_t = (1000 * (period_c - value)) / period_c;
|
||||||
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_time_obj, pyb_timer_channel_event_time);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_time_obj, pyb_timer_channel_event_time);
|
||||||
|
@ -683,10 +721,10 @@ STATIC mp_obj_t pyb_timer_channel_duty_cycle(mp_uint_t n_args, const mp_obj_t *a
|
||||||
else {
|
else {
|
||||||
// duty cycle must be converted from percentage to ticks
|
// duty cycle must be converted from percentage to ticks
|
||||||
// calculate the period, the prescaler and the match value
|
// calculate the period, the prescaler and the match value
|
||||||
uint32_t period;
|
uint32_t period_c;
|
||||||
uint32_t match;
|
uint32_t match;
|
||||||
ch->duty_cycle = MIN(100, MAX(0, mp_obj_get_int(args[1])));
|
ch->duty_cycle = MIN(100, MAX(0, mp_obj_get_int(args[1])));
|
||||||
compute_prescaler_period_and_match_value(ch, &period, &match);
|
compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||||
if (n_args == 3) {
|
if (n_args == 3) {
|
||||||
// set the new polarity if requested
|
// set the new polarity if requested
|
||||||
ch->polarity = mp_obj_get_int(args[2]);
|
ch->polarity = mp_obj_get_int(args[2]);
|
||||||
|
@ -801,10 +839,10 @@ STATIC mp_obj_t pyb_timer_channel_callback (mp_uint_t n_args, const mp_obj_t *po
|
||||||
ch->duty_cycle = MIN(100, c_value);
|
ch->duty_cycle = MIN(100, c_value);
|
||||||
|
|
||||||
// reload the timer
|
// reload the timer
|
||||||
uint32_t period;
|
uint32_t period_c;
|
||||||
uint32_t match;
|
uint32_t match;
|
||||||
compute_prescaler_period_and_match_value(ch, &period, &match);
|
compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||||
MAP_TimerLoadSet(ch->timer->timer, ch->channel, period);
|
MAP_TimerLoadSet(ch->timer->timer, ch->channel, period_c);
|
||||||
// set the appropiate match value
|
// set the appropiate match value
|
||||||
MAP_TimerMatchSet(ch->timer->timer, ch->channel, (_config == TIMER_CFG_A_PWM) ? match : c_value);
|
MAP_TimerMatchSet(ch->timer->timer, ch->channel, (_config == TIMER_CFG_A_PWM) ? match : c_value);
|
||||||
|
|
||||||
|
@ -821,6 +859,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_callback_obj, 1, pyb_timer_c
|
||||||
STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = {
|
STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = {
|
||||||
// instance methods
|
// instance methods
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_timer_channel_freq_obj },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_timer_channel_freq_obj },
|
||||||
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_channel_period_obj },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&pyb_timer_channel_time_obj },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&pyb_timer_channel_time_obj },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_count), (mp_obj_t)&pyb_timer_channel_event_count_obj },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_count), (mp_obj_t)&pyb_timer_channel_event_count_obj },
|
||||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_time), (mp_obj_t)&pyb_timer_channel_event_time_obj },
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_time), (mp_obj_t)&pyb_timer_channel_event_time_obj },
|
||||||
|
|
|
@ -336,6 +336,7 @@ Q(TimerChannel)
|
||||||
Q(init)
|
Q(init)
|
||||||
Q(deinit)
|
Q(deinit)
|
||||||
Q(freq)
|
Q(freq)
|
||||||
|
Q(period)
|
||||||
Q(mode)
|
Q(mode)
|
||||||
Q(width)
|
Q(width)
|
||||||
Q(channel)
|
Q(channel)
|
||||||
|
|
Loading…
Reference in New Issue