瀏覽代碼

analogRead & analogWrite on imxrt

main
PaulStoffregen 5 年之前
父節點
當前提交
89e54de809
共有 3 個檔案被更改,包括 438 行新增4 行删除
  1. +128
    -0
      teensy4/analog.c
  2. +302
    -0
      teensy4/pwm.c
  3. +8
    -4
      teensy4/startup.c

+ 128
- 0
teensy4/analog.c 查看文件

@@ -0,0 +1,128 @@
#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"

static uint8_t calibrating;
static uint8_t analog_config_bits = 10;
static uint8_t analog_num_average = 4;


const uint8_t pin_to_channel[] = { // pg 482
7, // 0/A0 AD_B1_02
8, // 1/A1 AD_B1_03
12, // 2/A2 AD_B1_07
11, // 3/A3 AD_B1_06
6, // 4/A4 AD_B1_01
5, // 5/A5 AD_B1_00
15, // 6/A6 AD_B1_10
0, // 7/A7 AD_B1_11
13, // 8/A8 AD_B1_08
14, // 9/A9 AD_B1_09
128, // 10
128, // 11
128, // 12
128, // 13
7, // 14/A0 AD_B1_02
8, // 15/A1 AD_B1_03
12, // 16/A2 AD_B1_07
11, // 17/A3 AD_B1_06
6, // 18/A4 AD_B1_01
5, // 19/A5 AD_B1_00
15, // 20/A6 AD_B1_10
0, // 21/A7 AD_B1_11
13, // 22/A8 AD_B1_08
14, // 23/A9 AD_B1_09
1, // 24/A10 AD_B0_12
2 // 25/A11 AD_B0_13
// 26/A12 AD_B1_14 - only on ADC2
// 27/A13 AD_B1_15 - only on ADC2
};


static void wait_for_cal(void)
{
printf("wait_for_cal\n");
while (ADC1_GC & ADC_GC_CAL) ;
// TODO: check CALF, but what do to about CAL failure?
calibrating = 0;
printf("cal complete\n");
}


int analogRead(uint8_t pin)
{
if (pin > sizeof(pin_to_channel)) return 0;
if (calibrating) wait_for_cal();
uint8_t ch = pin_to_channel[pin];
if (ch > 15) return 0;
ADC1_HC0 = ch;
while (!(ADC1_HS & ADC_HS_COCO0)) ; // wait
return ADC1_R0;
}

void analogReference(uint8_t type)
{
}

void analogReadRes(unsigned int bits)
{

}

void analogReadAveraging(unsigned int num)
{
}

#define MAX_ADC_CLOCK 20000000

void analog_init(void)
{
uint32_t mode, avg=0;

printf("analogInit\n");

CCM_CCGR1 |= CCM_CCGR1_ADC1(CCM_CCGR_ON);

if (analog_config_bits == 8) {
// 8 bit conversion (17 clocks) plus 8 clocks for input settling
mode = ADC_CFG_MODE(0) | ADC_CFG_ADSTS(3);
} else if (analog_config_bits == 10) {
// 10 bit conversion (17 clocks) plus 20 clocks for input settling
mode = ADC_CFG_MODE(1) | ADC_CFG_ADSTS(2) | ADC_CFG_ADLSMP;
} else {
// 12 bit conversion (25 clocks) plus 24 clocks for input settling
mode = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP;
}
if (analog_num_average >= 4) {
if (analog_num_average >= 32) {
mode |= ADC_CFG_AVGS(3);
} else if (analog_num_average >= 16) {
mode |= ADC_CFG_AVGS(2);
} else if (analog_num_average >= 8) {
mode |= ADC_CFG_AVGS(1);
}
avg = ADC_GC_AVGE;
}
#if 1
mode |= ADC_CFG_ADIV(1) | ADC_CFG_ADICLK(3); // async clock
#else
uint32_t clock = F_BUS;
if (clock > MAX_ADC_CLOCK*8) {
mode |= ADC_CFG_ADIV(3) | ADC_CFG_ADICLK(1); // use IPG/16
} else if (clock > MAX_ADC_CLOCK*4) {
mode |= ADC_CFG_ADIV(2) | ADC_CFG_ADICLK(1); // use IPG/8
} else if (clock > MAX_ADC_CLOCK*2) {
mode |= ADC_CFG_ADIV(1) | ADC_CFG_ADICLK(1); // use IPG/4
} else if (clock > MAX_ADC_CLOCK) {
mode |= ADC_CFG_ADIV(0) | ADC_CFG_ADICLK(1); // use IPG/2
} else {
mode |= ADC_CFG_ADIV(0) | ADC_CFG_ADICLK(0); // use IPG
}
#endif

ADC1_CFG = mode | ADC_HC_AIEN | ADC_CFG_ADHSC;
ADC1_GC = avg | ADC_GC_CAL; // begin cal
calibrating = 1;
}



+ 302
- 0
teensy4/pwm.c 查看文件

@@ -0,0 +1,302 @@
#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"


struct pwm_pin_info_struct {
uint8_t type; // 0=no pwm, 1=flexpwm, 2=quad
uint8_t module; // 0-3, 0-3
uint8_t channel; // 0=X, 1=A, 2=B
uint8_t muxval; //
};

uint8_t analog_write_res = 8;

#define M(a, b) ((((a) - 1) << 4) | (b))

const struct pwm_pin_info_struct pwm_pin_info[] = {
{1, M(1, 1), 0, 4}, // FlexPWM1_1_X 0 // AD_B0_03
{1, M(1, 0), 0, 4}, // FlexPWM1_0_X 1 // AD_B0_02
{1, M(4, 2), 1, 1}, // FlexPWM4_2_A 2 // EMC_04
{1, M(4, 2), 2, 1}, // FlexPWM4_2_B 3 // EMC_05
{1, M(2, 0), 1, 1}, // FlexPWM2_0_A 4 // EMC_06
{1, M(2, 0), 2, 1}, // FlexPWM2_0_B 5 // EMC_07
{1, M(1, 3), 2, 6}, // FlexPWM1_3_B 6 // B1_01
{1, M(1, 3), 1, 6}, // FlexPWM1_3_A 7 // B1_00
{1, M(2, 2), 1, 2}, // FlexPWM2_2_A 8 // B0_10
{1, M(2, 2), 1, 2}, // FlexPWM2_2_B 9 // B0_11
{2, M(1, 0), 0, 1}, // QuadTimer1_0 10 // B0_00
{2, M(1, 2), 0, 1}, // QuadTimer1_2 11 // B0_02
{2, M(1, 1), 0, 1}, // QuadTimer1_1 12 // B0_01
{2, M(2, 0), 0, 1}, // QuadTimer2_0 13 // B0_03
{2, M(3, 2), 0, 1}, // QuadTimer3_2 14 // AD_B1_02
{2, M(3, 3), 0, 1}, // QuadTimer3_3 15 // AD_B1_03
{0, M(1, 0), 0, 0},
{0, M(1, 0), 0, 0},
{2, M(3, 1), 0, 1}, // QuadTimer3_1 18 // AD_B1_01
{2, M(3, 0), 0, 1}, // QuadTimer3_0 19 // AD_B1_00
{0, M(1, 0), 0, 0},
{0, M(1, 0), 0, 0},
{1, M(4, 0), 1, 1}, // FlexPWM4_0_A 22 // AD_B1_08
{1, M(4, 1), 1, 1}, // FlexPWM4_1_A 23 // AD_B1_09
{1, M(1, 2), 0, 4}, // FlexPWM1_2_X 24 // AD_B0_12
{1, M(1, 3), 0, 4}, // FlexPWM1_3_X 25 // AD_B0_13
{0, M(1, 0), 0, 0},
{0, M(1, 0), 0, 0},
{1, M(3, 1), 2, 1}, // FlexPWM3_1_B 28 // EMC_32
{1, M(3, 1), 1, 1}, // FlexPWM3_1_A 29 // EMC_31
{1, M(1, 0), 2, 1}, // FlexPWM1_0_B 30 // EMC_24
{1, M(1, 0), 1, 1}, // FlexPWM1_0_A 31 // EMC_23
{0, M(1, 0), 0, 0},
{1, M(2, 1), 1, 1}, // FlexPWM2_1_A 33 // EMC_08
};

void flexpwmWrite(IMXRT_FLEXPWM_t *p, unsigned int submodule, uint8_t channel, uint16_t val)
{
uint16_t mask = 1 << submodule;
uint32_t modulo = p->SM[submodule].VAL1;
uint32_t cval = ((uint32_t)val * (modulo + 1)) >> analog_write_res;
if (cval > modulo) cval = modulo; // TODO: is this check correct?

//printf("flexpwmWrite, p=%08lX, sm=%d, ch=%c, cval=%ld\n",
//(uint32_t)p, submodule, channel == 0 ? 'X' : (channel == 1 ? 'A' : 'B'), cval);
p->MCTRL |= FLEXPWM_MCTRL_CLDOK(mask);
switch (channel) {
case 0: // X
p->SM[submodule].VAL0 = cval;
p->OUTEN |= FLEXPWM_OUTEN_PWMX_EN(mask);
//printf(" write channel X\n");
break;
case 1: // A
p->SM[submodule].VAL3 = cval;
p->OUTEN |= FLEXPWM_OUTEN_PWMA_EN(mask);
//printf(" write channel A\n");
break;
case 2: // B
p->SM[submodule].VAL5 = cval;
p->OUTEN |= FLEXPWM_OUTEN_PWMB_EN(mask);
//printf(" write channel B\n");
}
p->MCTRL |= FLEXPWM_MCTRL_LDOK(mask);
}

void flexpwmFrequency(IMXRT_FLEXPWM_t *p, unsigned int submodule, uint8_t channel, float frequency)
{
uint16_t mask = 1 << submodule;
uint32_t olddiv = p->SM[submodule].VAL1;
uint32_t newdiv = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5);
uint32_t prescale = 0;
//printf(" div=%lu\n", newdiv);
while (newdiv > 65535 && prescale < 7) {
newdiv = newdiv >> 1;
prescale = prescale + 1;
}
if (newdiv > 65535) {
newdiv = 65535;
} else if (newdiv < 2) {
newdiv = 2;
}
//printf(" div=%lu, scale=%lu\n", newdiv, prescale);
p->MCTRL |= FLEXPWM_MCTRL_CLDOK(mask);
p->SM[submodule].CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(prescale);
p->SM[submodule].VAL1 = newdiv - 1;
p->SM[submodule].VAL0 = (p->SM[submodule].VAL0 * newdiv) / olddiv;
p->SM[submodule].VAL3 = (p->SM[submodule].VAL3 * newdiv) / olddiv;
p->SM[submodule].VAL5 = (p->SM[submodule].VAL5 * newdiv) / olddiv;
p->MCTRL |= FLEXPWM_MCTRL_LDOK(mask);
}

void quadtimerWrite(IMXRT_TMR_t *p, unsigned int submodule, uint16_t val)
{
uint32_t modulo = 65537 - p->CH[submodule].LOAD + p->CH[submodule].CMPLD1;
uint32_t high = ((uint32_t)val * (modulo - 1)) >> analog_write_res;
if (high >= modulo) high = modulo - 1; // TODO: is this check correct?

//printf(" modulo=%lu\n", modulo);
//printf(" high=%lu\n", high);
uint32_t low = modulo - high; // TODO: low must never be 0 or 1 - can it be??
//printf(" low=%lu\n", low);

p->CH[submodule].LOAD = 65537 - low;
p->CH[submodule].CMPLD1 = high;
}

void quadtimerFrequency(IMXRT_TMR_t *p, unsigned int submodule, float frequency)
{
uint32_t newdiv = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5);
uint32_t prescale = 0;
//printf(" div=%lu\n", newdiv);
while (newdiv > 65534 && prescale < 7) {
newdiv = newdiv >> 1;
prescale = prescale + 1;
}
if (newdiv > 65534) {
newdiv = 65534;
} else if (newdiv < 2) {
newdiv = 2;
}
//printf(" div=%lu, scale=%lu\n", newdiv, prescale);
uint32_t oldhigh = p->CH[submodule].CMPLD1;
uint32_t oldlow = 65537 - p->CH[submodule].LOAD;
uint32_t high = (oldhigh * newdiv) / (oldhigh + oldlow);
// TODO: low must never be less than 2 - can it happen with this?
uint32_t low = newdiv - high;
//printf(" high=%lu, low=%lu\n", high, low);
p->CH[submodule].LOAD = 65537 - low;
p->CH[submodule].CMPLD1 = high;
p->CH[submodule].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8 + prescale) |
TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(6);
}

void analogWrite(uint8_t pin, int val)
{
const struct pwm_pin_info_struct *info;

if (pin >= CORE_NUM_DIGITAL) return;
//printf("analogWrite, pin %d, val %d\n", pin, val);
info = pwm_pin_info + pin;
if (info->type == 1) {
// FlexPWM pin
IMXRT_FLEXPWM_t *flexpwm;
switch ((info->module >> 4) & 3) {
case 0: flexpwm = &IMXRT_FLEXPWM1; break;
case 1: flexpwm = &IMXRT_FLEXPWM2; break;
case 2: flexpwm = &IMXRT_FLEXPWM3; break;
case 3: flexpwm = &IMXRT_FLEXPWM4;
}
flexpwmWrite(flexpwm, info->module & 0x03, info->channel, val);
} else if (info->type == 2) {
// QuadTimer pin
IMXRT_TMR_t *qtimer;
switch ((info->module >> 4) & 3) {
case 0: qtimer = &IMXRT_TMR1; break;
case 1: qtimer = &IMXRT_TMR2; break;
case 2: qtimer = &IMXRT_TMR3; break;
case 3: qtimer = &IMXRT_TMR4;
}
quadtimerWrite(qtimer, info->module & 0x03, val);
} else {
return;
}
*(portConfigRegister(pin)) = info->muxval;
// TODO: pad config register
}

void analogWriteFrequency(uint8_t pin, float frequency)
{
const struct pwm_pin_info_struct *info;

if (pin >= CORE_NUM_DIGITAL) return;
//printf("analogWriteFrequency, pin %d, freq %d\n", pin, (int)frequency);
info = pwm_pin_info + pin;
if (info->type == 1) {
// FlexPWM pin
IMXRT_FLEXPWM_t *flexpwm;
switch ((info->module >> 4) & 3) {
case 0: flexpwm = &IMXRT_FLEXPWM1; break;
case 1: flexpwm = &IMXRT_FLEXPWM2; break;
case 2: flexpwm = &IMXRT_FLEXPWM3; break;
case 3: flexpwm = &IMXRT_FLEXPWM4;
}
flexpwmFrequency(flexpwm, info->module & 0x03, info->channel, frequency);
} else if (info->type == 2) {
// QuadTimer pin
IMXRT_TMR_t *qtimer;
switch ((info->module >> 4) & 3) {
case 0: qtimer = &IMXRT_TMR1; break;
case 1: qtimer = &IMXRT_TMR2; break;
case 2: qtimer = &IMXRT_TMR3; break;
case 3: qtimer = &IMXRT_TMR4;
}
quadtimerFrequency(qtimer, info->module & 0x03, frequency);
}
}

void flexpwm_init(IMXRT_FLEXPWM_t *p)
{
int i;

p->FCTRL0 = FLEXPWM_FCTRL0_FLVL(15); // logic high = fault
p->FSTS0 = 0x000F; // clear fault status
p->FFILT0 = 0;
p->MCTRL |= FLEXPWM_MCTRL_CLDOK(15);
for (i=0; i < 4; i++) {
p->SM[i].CTRL2 = FLEXPWM_SMCTRL2_INDEP | FLEXPWM_SMCTRL2_WAITEN
| FLEXPWM_SMCTRL2_DBGEN;
p->SM[i].CTRL = FLEXPWM_SMCTRL_FULL;
p->SM[i].OCTRL = 0;
p->SM[i].DTCNT0 = 0;
p->SM[i].INIT = 0;
p->SM[i].VAL0 = 0;
p->SM[i].VAL1 = 33464;
p->SM[i].VAL2 = 0;
p->SM[i].VAL3 = 0;
p->SM[i].VAL4 = 0;
p->SM[i].VAL5 = 0;
}
p->MCTRL |= FLEXPWM_MCTRL_LDOK(15);
p->MCTRL |= FLEXPWM_MCTRL_RUN(15);
}

void quadtimer_init(IMXRT_TMR_t *p)
{
int i;

for (i=0; i < 4; i++) {
p->CH[i].CTRL = 0; // stop timer
p->CH[i].CNTR = 0;
p->CH[i].SCTRL = TMR_SCTRL_OEN | TMR_SCTRL_OPS | TMR_SCTRL_VAL | TMR_SCTRL_FORCE;
p->CH[i].CSCTRL = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_ALT_LOAD;
// COMP must be less than LOAD - otherwise output is always low
p->CH[i].LOAD = 24000; // low time (65537 - x) -
p->CH[i].COMP1 = 0; // high time (0 = always low, max = LOAD-1)
p->CH[i].CMPLD1 = 0;
p->CH[i].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) |
TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(6);
}
}

void pwm_init(void)
{
//printf("pwm init\n");
CCM_CCGR4 |= CCM_CCGR4_PWM1(CCM_CCGR_ON) | CCM_CCGR4_PWM2(CCM_CCGR_ON) |
CCM_CCGR4_PWM3(CCM_CCGR_ON) | CCM_CCGR4_PWM4(CCM_CCGR_ON);
CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON) | CCM_CCGR6_QTIMER2(CCM_CCGR_ON) |
CCM_CCGR6_QTIMER3(CCM_CCGR_ON) | CCM_CCGR6_QTIMER4(CCM_CCGR_ON);
flexpwm_init(&IMXRT_FLEXPWM1);
flexpwm_init(&IMXRT_FLEXPWM2);
flexpwm_init(&IMXRT_FLEXPWM3);
flexpwm_init(&IMXRT_FLEXPWM4);
quadtimer_init(&IMXRT_TMR1);
quadtimer_init(&IMXRT_TMR2);
quadtimer_init(&IMXRT_TMR3);
}



void xbar_connect(unsigned int input, unsigned int output)
{
if (input >= 88) return;
if (output >= 132) return;
#if 1
volatile uint16_t *xbar = &XBARA1_SEL0 + (output / 2);
uint16_t val = *xbar;
if (!(output & 1)) {
val = (val & 0xFF00) | input;
} else {
val = (val & 0x00FF) | (input << 8);
}
*xbar = val;
#else
// does not work, seems 8 bit access is not allowed
volatile uint8_t *xbar = (volatile uint8_t *)XBARA1_SEL0;
xbar[output] = input;
#endif
}

uint32_t analogWriteRes(uint32_t bits)
{
return 0;
}



+ 8
- 4
teensy4/startup.c 查看文件

@@ -25,6 +25,8 @@ extern void systick_isr(void);
void configure_cache(void);
void unused_interrupt_vector(void);
void usb_pll_start();
extern void analog_init(void);
extern void pwm_init(void);


__attribute__((section(".startup")))
@@ -36,10 +38,10 @@ void ResetHandler(void)
//__asm__ volatile("mov sp, %0" : : "r" (0x20010000) : );

// pin 13 - if startup crashes, use this to turn on the LED early for troubleshooting
//IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 5;
//IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(7);
//GPIO2_GDIR |= (1<<3);
//GPIO2_DR_SET = (1<<3);
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 5;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(7);
GPIO2_GDIR |= (1<<3);
GPIO2_DR_SET = (1<<3);

memory_copy(&_stext, &_stextload, &_etext);
memory_copy(&_sdata, &_sdataload, &_edata);
@@ -91,6 +93,8 @@ void ResetHandler(void)

// TODO: wait at least 20ms before starting USB
usb_init();
analog_init();
pwm_init();

// TODO: wait tat least 300ms before calling setup
printf("before setup\n");

Loading…
取消
儲存