Search This Blog

Mar 12, 2012

SiM3U1xx-current-mode-dac

Introduction
  SiM3U1xx and SiM3C1xx include current mode digital-to-analog converter (IDAC) module.  The IDAC takes a digital value as an input and outputs a proportional constant current on a pin.

Function Description

  The IDAC module includes the following features:   10-bit current DAC with output update trigger source options. Support for three full-scale output mode: 0.5, 1.0 and 2.0mA. Four-word FIFO to aid with high-speed waveform generation or DMA interactions.  Ability to update on rising, falling, or both edge for any of the external I/O trigger sources (DACnTx).

The various IDAC features and modes are enabled using the CONTROL register. There are four different modes: on-demand mode, periodic FIFO wrap mode, period FIFO-only mode, and periodic DMA mode.

  The IDAC full scale current output is configured using the OUTMD bit field. There are nominally 2.046, 1.023 and 0.5115 mA.
  Optionally, and on-chip load resistor can be enabled by setting the LOADEN bit. This enables an impedance path to ground which effectively produces a voltage at the output pin. 

IDAC conversions can be triggered “on-demand” with a write to the DATA register, or periodically using one of the internal timer options or external conversion trigger inputs.

We can have a look on below table for quick-reference on IDAC operation. That is very useful for setting up IDAC correctly. 

And here we have a look on IDAC electrical parameters in below table.

Code Implementation

   We used AppBuilder to generate basic code we need, and then added necessary code to make temperature sensor works.
void CLKCTRL_setup_default_mode_clock_gates(void)
{
  SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0,
                                         SI32_CLKCTRL_A_APBCLKG0_PB0 |
                                         SI32_CLKCTRL_A_APBCLKG0_TIMER0 |
                                         SI32_CLKCTRL_A_APBCLKG0_IDAC0);
  SI32_CLKCTRL_A_enable_ahb_to_dma_controller(SI32_CLKCTRL_0);
}
2.       Enable on-chip load resistor; set GAINADJ as 16, this value is well match with theory and measure result;  enable IDAC module; set output full-scale current output as 2mA ; and external trigger source use default setting which is PB3.2 with SiM3U1x7/C1x7 package.(gIDAC0.c)
void IDAC0_enter_default_mode_from_reset(void)
{
  SI32_IDAC_A_enable_load_resistor(SI32_IDAC_0);
  SI32_IDAC_A_set_output_fullscale_adjust(SI32_IDAC_0, 16);
  SI32_IDAC_A_enable_module(SI32_IDAC_0);
  SI32_IDAC_A_select_output_fullscale_2ma(SI32_IDAC_0);
}
3.       Port bank configuration. PB0.13 selected as IDAC0 output pin  need to be skipped in crossbar, P3.2  selected as external trigger source and need to be skipped in crossbar.(gPB.c)
void pb_enter_default_mode_from_reset(void)
{
  SI32_PBCFG_A_unlock_ports(SI32_PBCFG_0);
  // PB0 Setup
  SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_0, 0x2000);
  // PB1 Setup
  SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_1, 0x0008);
  SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_1, 0x0008);
  // Enable Crossbar0 signals & set properties
  SI32_PBCFG_A_enable_crossbar_0(SI32_PBCFG_0);
  // PB2 Setup
  SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_2, 0x0C00);
  SI32_PBSTD_A_set_pins_high_drive_strength(SI32_PBSTD_2, 0x0C00);
  // PB3 Setup
  SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_3, 0x0004);
  // Enable Crossbar1 signals & set properties
  SI32_PBCFG_A_enable_crossbar_1(SI32_PBCFG_0);
}
4.       IDAC On-demand mode.
a.       Set output update trigger  with “the IDAC output updates on write to DATA (On Demand“, data format is single 10 bit and right-justified  as default setting.(myIDAC0.c)
void myIDAC0_enter_demand_mode(void)
{
  SI32_IDAC_A_set_output_update_trigger(SI32_IDAC_0,      SI32_IDAC_A_CONTROL_OUPDT_DACNT15_VALUE);
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x01F40190);
}
b.      Fetch data to IDACn_DATA register every 2 seconds, On-Demand mode, writes to this IDACn_DATA register update the IDAC value immediately, (main.c)

void IDAC0_demand_mode_fetch_data()
{
  uint32_t i, ms_ticks = 500;
  while (1) {
    for (i = 0; i < 1024; i += 100) {
      while (ms_ticks > get_msTicks()) {
      }
      ms_ticks = get_msTicks() + 2000;
      blink_led();
      printf("Output current is %d uA\n", fullscale_current * i / 1024);
      SI32_IDAC_A_write_data(SI32_IDAC_0, i);
    }
  }
}
5.       IDAC FIFO wrap mode
a.       Set data format as two 10 bit samples and right-justified; set the IDAC output updates on the falling edge of PB3.2; enabled FIFO wrap and reset FIFO before we access it.(myIDAC0.c)
void myIDAC0_enter_fifo_wrap_mode(void)
{
  SI32_IDAC_A_select_2x10bit_input_format(SI32_IDAC_0);
  SI32_IDAC_A_set_output_update_trigger(SI32_IDAC_0,  SI32_IDAC_A_CONTROL_OUPDT_DACNT13_VALUE);
  SI32_IDAC_A_enable_buffer_wrap(SI32_IDAC_0);
  SI32_IDAC_A_reset_buffer(SI32_IDAC_0);
}
b.      In this mode, the IDAC will continuously pull from the four-sample FIFO in a circular fashion. We only need to send data once to FIFO. Since we use two 10 bit samples format, for 4 words FIFO, we only need write two values to data register. (main.c)
void IDAC0_fifo_wrap_mode_fetch_data()
{
  // we set two 10 bit format, so we only need write data register twice
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x01F40190);
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x02BC0258);
  printf("P3.2 falling edge will trigger IDAC0 fifo wrap mode output\n");
}
6.       IDAC FIFO-only mode
a.        Set data format as single 10 bit sample and right-justified; set the IDAC output updates on the falling edge of PB3.2; Reset FIFO before we access it; enable FIFO went empty interrupt and 2nd level interrupt handler function.(myIDAC0.c)
void myIDAC0_enter_fifo_only_mode(void)
{
  SI32_IDAC_A_set_output_update_trigger(SI32_IDAC_0, SI32_IDAC_A_CONTROL_OUPDT_DACNT13_VALUE);
  SI32_IDAC_A_reset_buffer(SI32_IDAC_0);
  SI32_IDAC_A_enable_buffer_went_empty_interrupt(SI32_IDAC_0);
  NVIC_ClearPendingIRQ(IDAC0_IRQn);
  NVIC_EnableIRQ(IDAC0_IRQn);
}
void IDAC0_empty_handler(void)
{
  int i;
  SI32_IDAC_A_clear_buffer_went_empty_interrupt(SI32_IDAC_0);
    for (i = 0; i < 4; i++) {
      idac_output_current = (idac_output_current + 100) & 0x3FF;
      SI32_IDAC_A_write_data(SI32_IDAC_0,idac_output_current);
    }
}
b.      In this mode, firmware will initiate a new write to the FIFO in went empty interrupt handler function. We still need to full fill FIFO when initialize IDAC module. (main.c)
void IDAC0_fifo_only_mode_fetch_data()
{
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x0190);
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x01F4);
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x0258);
  SI32_IDAC_A_write_data(SI32_IDAC_0, 0x02BC);
  printf("P3.2 falling edge will trigger IDAC0 fifo only mode output\n");
}
7.       IDAC DMA mode
a.       Set data format as single 10 bit sample and right-justified; set the IDAC ouput updates on the falling edge of PB3.2; Reset FIFO before we access it; enable FIFO interrupt and 2nd level interrupt handler function (myIDAC0.c)
void myIDAC0_enter_dma_mode(void)
{
  SI32_IDAC_A_set_output_update_trigger(SI32_IDAC_0, SI32_IDAC_A_CONTROL_OUPDT_DACNT13_VALUE);
  SI32_IDAC_A_reset_buffer(SI32_IDAC_0);
  NVIC_ClearPendingIRQ(IDAC0_IRQn);
  NVIC_EnableIRQ(IDAC0_IRQn);
  idac_dma_mode_flag = 1;
}
void IDAC0_empty_handler(void)
{
  int i;
  SI32_IDAC_A_clear_buffer_went_empty_interrupt(SI32_IDAC_0);
  idac_dma_complete = 1;
  SI32_IDAC_A_disable_buffer_went_empty_interrupt(SI32_IDAC_0);
}
b.      Setup DMA base address; set DMA crossbar channel 3 as IDAC0 data request service; Enable the DMA channel; Setup DMA description configure, such as DMA count, DMA input data and destination endpoint. Enable DMA module; Start IDAC DMA transfer; Enable IDAC went empty interrupt. (myIDAC0.c)
void myIDAC0_initialize_dma_transfer(void)
{
  uint32_t ch;
  SI32_IDAC_A_reset_buffer(SI32_IDAC_0);
  printf("Initialize IDAC0 DMA transfer\n");
  SI32_DMACTRL_A_write_baseptr(SI32_DMACTRL_0, (uint32_t)desc_pri);
  ch = SI32_DMAXBAR_A_select_channel_peripheral(SI32_DMAXBAR_0, SI32_DMAXBAR_CHAN3_IDAC0);

  SI32_DMACTRL_A_enable_channel(SI32_DMACTRL_0, ch);
  SI32_DMADESC_A_configure(&desc_pri[3], idac_input_data, SI32_IDAC_0_TX_ENDPOINT, IDAC_DMA_COUNT, SI32_DMADESC_A_CONFIG_WORD_TX);
  SI32_DMACTRL_A_enable_module(SI32_DMACTRL_0);

  idac_dma_complete = 0;
  SI32_IDAC_A_start_dma_operation(SI32_IDAC_0);
  SI32_IDAC_A_clear_buffer_went_empty_interrupt(SI32_IDAC_0);
  SI32_IDAC_A_enable_buffer_went_empty_interrupt(SI32_IDAC_0);
}
c.       In this mode, we only need call DMA initialize function.(main.c)
void IDAC0_dma_mode_fetch_data()
{
  myIDAC0_initialize_dma_transfer();
  while (idac_dma_complete == 0) {
  }
  printf("IDAC dma mode transfer done!\n");
}

Function validation

1.        Use HP 34401A Multimeter, select DC current measurement function. Connect   wires to MCU CARD IDAC0(PB0.13) pin(in "ANALOG" area) and GND. 
2.        Connect a wire to PB3.2 pin with series-connected 1K resistor, we will connect another ends of the wire to generate falling edge event.   
3.       Download the code to a SiM3U1xx device on a SiM3U1xx MCU Card
4.       Run the code and use HP 34401A Multimeter observe the IDAC0 output constant current. For On-Demand mode, we can see output current value update in console windows in IDE every 2 seconds; For other modes, we need generate P3.2 falling edge event by pulling down voltage level, we can observe that output constant current change at each falling edge. 

References

1.       SiM3U1xx/SiM3C1xx Reference Manual: downloadable from the Silicon Labs web site at http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support Documents/TechnicalDocs/SiM3U1xx_SiM3C1xx_RM.pdf&src=DocumentationWebPart

2.       SiM3U1xx Data Sheet: downloadable from the Silicon Labs web site at http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support Documents/TechnicalDocs/SiM3U1xx.pdf&src=DocumentationWebPart


No comments: