FireBlue/Adc driver

来自Firefly wiki
跳转至: 导航搜索

Introduction

ADC mainly achieves it to convert the analog voltage to digital form ,making the CPU know the value of external voltage.the serial of QN902x have the 8/10/12-bit adc with 2/5 channel and changeable rang,with the standard reference voltage of 1v,and can be linked to the externed reference voltage.

Initialization

System platform initialization

Comparing to the routine before,the adc need to add some initialization on the stage within system initialization,in the function SystemInit,the child mode initialization above need to be changed like this:

/*
 **************************
 * Sub module clock setting
 **************************
 */
  // DC-DC
  dc_dc_enable(true);
  // QN platform initialization
  plf_init(NORMAL_MODE, __XTAL, QN_32K_RCO, NULL, 0);

GPIO configuration

Next step is to configuration GPIO.

    adc_io_config();


P30,P31,P06,P07 is the input channel AIN0,AIN1,AIN2,AIN3 of ADC respectively,P05 is the GPIO trigger pin of ADC.

// pin mux
syscon_SetPMCR0(QN_SYSCON, P00_UART0_TXD_PIN_CTRL
                         | P05_ADCT_PIN_CTRL
#if ADC_EXT_REF_EN==TRUE
                         | P06_AIN2_PIN_CTRL
                         | P07_AIN3_PIN_CTRL
#else
                         | P06_SW_DAT_PIN_CTRL
                         | P07_SW_CLK_PIN_CTRL
#endif
                         | P17_UART0_RXD_PIN_CTRL
                         );
syscon_SetPMCR1(QN_SYSCON, P27_PWM0_PIN_CTRL
                         | P26_PWM1_PIN_CTRL
                         | P31_AIN1_PIN_CTRL
                         | P30_AIN0_PIN_CTRL
                         );

Enable the analog input function of port AIN0,AIN1,AIN2,AIN3.

analog_pin_enable(AIN0, MASK_ENABLE);
analog_pin_enable(AIN1, MASK_ENABLE);
analog_pin_enable(AIN2, MASK_ENABLE);
analog_pin_enable(AIN3, MASK_ENABLE);

Serial initialization

Initializing the serial port it to see the better result of ADC.

#if (__AHB_CLK == 32000UL)
    uart_init(QN_UART0, __USART_CLK, UART_1200);
#else
    uart_init(QN_UART0, __USART_CLK, UART_115200);
#endif
    uart_tx_enable(QN_UART0, MASK_ENABLE);

ADC initialization

ADC initialization,this example,turn off 32k crystals,without the external crystals better。

    // ADC initialization    
#if ADC_WORKING_AT_32K==TRUE
    clk32k_enable(__32K_TYPE);
    adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK32K_16000, ADC_INT_REF, ADC_12BIT);
#else
 
#if ADC_EXT_REF_EN==TRUE
    adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_EXT_REF2, ADC_12BIT);
    //adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_EXT_REF1, ADC_12BIT);
#else    
    adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);
#endif
#endif

现在来分析一下

adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);

这句代码,第一个参数比较复杂,在工程中搜索(查到定义是跳不过去的,因为这是枚举变量),发现如下语句,在ADC采集有两种方式,一种是差分采集,一种是单信号采集,并且采集的时候如果使用内部buffer的话,不能检测到0.2v一下的电压,但是如果基准电压的1.5倍小于VDD-0.2的话,可以扩大量程到0.2V~1.5*VREF。对于例程来说,配置为单信号采集,不采用系统buffer,采样频率为1MHz,采用内部基准电压,ADC的位数为12位。

/// ADC input mode
enum ADC_IN_MOD
{
    ADC_DIFF_WITH_BUF_DRV = 0,      /*!< ADC differential input with buffer, input singal
 0.2 =< VIN(V) <= VDD-0.2, ADC result [-2048, 2047] map to [-VREF, VREF). */
    ADC_DIFF_WITHOUT_BUF_DRV,       /*!< ADC differential input without buffer, input singal
 0 =< VIN(V) <= VDD, and should have enough driving capability, ADC result [-2048, 2047] map to [-VREF, VREF). */
    ADC_SINGLE_WITH_BUF_DRV,        /*!< ADC single-ended input with buffer, input singal
 0.2 =< VIN(V) <= 1.5*VREF <= VDD-0.2, ADC result [x, 2047] map to [0.2, 1.5*VREF). */
    ADC_SINGLE_WITHOUT_BUF_DRV      /*!< ADC single-ended input without buffer, input singal
 0 =< VIN(V) <= VREF <= VDD, and should have enough driving capability, ADC result [0, 2047] map to [0, VREF). */
};
 
/// ADC reference voltage
enum ADC_REF
{
    ADC_INT_REF = 0,    /*!< Internal reference, VREF = 1.0V */
    ADC_EXT_REF1,       /*!< External reference1(with buffer and gain=2, input PIN is P0.7):
 VREF = 2*EXT_REF1 (0 < EXT_REF1 < (VDD-1.0)/2). */
    ADC_EXT_REF2        /*!< External reference2(without buffer, input PIN is P0.7): 
VERF = EXT_REF2 (0 < EXT_REF2 < VDD), EXT_REF2 should have driving capability. */
};

ADC collection

The adc collection setting states,adc has many collection modes,before collecting in different requirements ,it needs to set the collection mode.GPIO trigger collection,timer flow-control collection, software trigger collection,extracting filtering collection,sampling comparison.

// Read configuration
adc_read_configuration read_cfg;

GPIO trigger collection

Set the collection trigger source GPIO_P05.

    read_cfg.trig_src = ADC_TRIG_GPIO;
    syscon_SetPMCR2WithMask(QN_SYSCON, SYSCON_MASK_ADCT_PIN_SEL, ADC_GPIO15_TRIG);

Set the PWM output,link the PWM_CH0W to the ADC_TRIG_GPIO, then collect the voltage by IO triggering the ADC.

    // triger by gpio in burst or burst scan mode, connect PWM output to ADC trigger PIN
    pwm_init(PWM_CH0);
    pwm_config(PWM_CH0, PWM_PSCAL_DIV, PWM_COUNT_US(50, PWM_PSCAL_DIV), PWM_COUNT_US(25, PWM_PSCAL_DIV));
    pwm_enable(PWM_CH0, MASK_ENABLE);

The same to above,firstly set the collection mark-bit in ADC,then reset in the callback funciton.

    adc_done = 0;

The adc collection mode is a burst mode,the collection channel is AIN0,collect 512 data and keep them in the arry buf,wait to collect complete.

    // modify here
    read_cfg.mode = BURST_MOD;
    read_cfg.start_ch = AIN0;
    read_cfg.end_ch = AIN0;
    adc_read(&read_cfg, buf, 512, adc_test_cb);
    while (adc_done == 0);

Because ADC uses the external buffer,the variable buf is the global varial said in the file adc_example_.c.

int16_t buf[512];

Print the sampling result by ADC.

    for (int i = 0; i < 512; i++) {
        printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i]));
    }

Average the sampling result by ADC,print it.then the program ends.

    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += buf[511 - 2*i];
    }
    sum = sum / 10;
    printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));

Timer triggering collection

Set the triggering source timer-triggering,set the timer mode triggering once every 50us.

    read_cfg.trig_src = ADC_TRIG_TOVF1;
    // triger by timer1 overflow
    timer_init(QN_TIMER1, NULL);
    timer_pwm_config(QN_TIMER1, TIMER_PSCAL_DIV, TIMER_COUNT_US(100, TIMER_PSCAL_DIV), TIMER_COUNT_US(50, TIMER_PSCAL_DIV));
    timer_enable(QN_TIMER1, MASK_ENABLE);

The same to above,firstly set the collection mark-bit in ADC,then reset in the callback funciton.

    adc_done = 0;

The adc collection mode is a burst mode,the collection channel is AIN0,collect 512 data and keep them in the arry buf,wait to collect complete.

    // modify here
    read_cfg.mode = BURST_MOD;
    read_cfg.start_ch = AIN0;
    read_cfg.end_ch = AIN0;
    adc_read(&read_cfg, buf, 512, adc_test_cb);
    while (adc_done == 0);

Because ADC uses the external buffer,the variable buf is the global varial said in the file adc_example_.c.

int16_t buf[512];

Print the sampling result by ADC.

    for (int i = 0; i < 512; i++) {
        printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i]));
    }

Average the sampling result by ADC,print it.then the program ends.

    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += buf[511 - 2*i];
    }
    sum = sum / 10;
    printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));

Software triggering collection

The settiing triggering-source is the software triggering.software triggering can not control the rate and time of adc.

    read_cfg.trig_src = ADC_TRIG_SOFT;

The same to above,firstly set the collection mark-bit in ADC,then reset in the callback funciton.

    adc_done = 0;

The adc collection mode is a burst mode,the collection channel is AIN0,collect 512 data and keep them in the arry buf,wait to collect complete.

    // modify here
    read_cfg.mode = BURST_MOD;
    read_cfg.start_ch = AIN0;
    read_cfg.end_ch = AIN0;
    adc_read(&read_cfg, buf, 512, adc_test_cb);
    while (adc_done == 0);

Because ADC uses the external buffer,the variable buf is the global varial said in the file adc_example_.c.

int16_t buf[512];

Print the sampling result by ADC.

    for (int i = 0; i < 512; i++) {
        printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i]));
    }

Average the sampling result by ADC,print it.then the program ends.

    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += buf[511 - 2*i];
    }
    sum = sum / 10;
    printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));

Extracting filter collection

Extracting filter collection apply to sample for the high rate signal.Sampling for high rate signal,due to the large data,itᄀᆵs hard to prcocess data in real time,and need the DDC technology,convert the high-speed signal sampled to the low-speed baseband sinal in order to get into next step of signal processing.The ADC data collection of 64 of sampling frequence for signal is shown in the next figure.

#if ADC_DECIMATION_EN == TRUE
    adc_decimation_enable(DECI_RATE_64, MASK_ENABLE);
#endif

Print the sampling result by ADC.

    for (int i = 0; i < 512; i++) {
        printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i]));
    }

Average the sampling result by ADC,print it.then the program ends.

    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += buf[511 - 2*i];
    }
    sum = sum / 10;
    printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));

ADC comparator

Take the ADC as comparator.

#if ADC_COMPARATOR_EN == TRUE
    //adc_compare_init(DECI_DATA, 2500, -2000, adc_WCMP_cb);
    adc_compare_init(ADC_DATA, 600, -600, adc_WCMP_cb);
#endif

Print the result collceted by ADC and compare those results.

int m = 0;
int n = 0;
for (int i = 0; i < 512; i++) {
   if (buf[i] > 600) {
        m++;
   }
   else if (buf[i] < -600) {
       n++;
   }
}
printf("m = %d\t n = %d\r\n", m, n);

Print the sampling result by ADC.

    for (int i = 0; i < 512; i++) {
        printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i]));
    }

Average the sampling result by ADC,print it.then the program ends.

    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += buf[511 - 2*i];
    }
    sum = sum / 10;
    printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));

Inner temperature sensor

The QN902x has the built-in temperature sensor,read the voltage value of temperature sensor by ADC and count the temperature value this moment.
Open the temperature sensor.

temp_sensor_enable(MASK_ENABLE);

Initialize the ADC.take the comparison mode with the cache mode to collect,sampling frequence value is 1 Mhz,reference voltage is 1v to the inner reference,collected by 12-bit ADC.

    int16_t tempv;
    adc_init(ADC_DIFF_WITH_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);

Setting is the software mode,triggering mode is the burst mode,sampling channel is the inner TEMP channel,begain to read the reset flag-bit.

    adc_done = 0;
    read_cfg.trig_src = ADC_TRIG_SOFT;
    read_cfg.mode = BURST_MOD;
    read_cfg.start_ch = TEMP;
    read_cfg.end_ch = TEMP;
    adc_read(&read_cfg, &tempv, 1, adc_test_cb);
    while (adc_done == 0);

Print the temperature value counted.

    printf("temperature: %0.1f\r\n", (float)(TEMPERATURE_X10(tempv)/10.0));

Inner battery tester

The QN902x has the built-in battery tester,read the voltage value of battery by ADC and count the electric quantity left this moment.
Open the battery tester

    battery_monitor_enable(MASK_ENABLE);

Initialize the ADC.take the single signal with the cache mode to collect,sampling frequence value is 1 Mhz,reference voltage is 1v to the inner reference,collected by 12-bit ADC.

    int16_t battv;
    adc_init(ADC_SINGLE_WITH_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);

Setting is the software mode,triggering mode is the burst mode,sampling channel is the inner BATT channel,begain to read the reset flag-bit.

    adc_done = 0;
    read_cfg.trig_src = ADC_TRIG_SOFT;
    read_cfg.mode = BURST_MOD;
    read_cfg.start_ch = BATT;
    read_cfg.end_ch = BATT;
    adc_read(&read_cfg, &battv, 1, adc_test_cb);
    while (adc_done == 0);

Print the temperature value counted.

    printf("battery voltage: %d\r\n", 4*ADC_RESULT_mV(battv));