<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 NB Cannot use PIT to trigger periodic DMA due to hardware bug
See here.
Try using PDB instead??
Step1:  Overview
Use linked DMA channels to perform "scan" across multiple ADC input channels.
After each scan, use DMA scatter chain to write the converted ADC values to a
separate output array for each ADC channel.  The length of the output array to
allocate for each ADC channel is determined by the sample_count in the
example below.
See diagram below.
Channel configuration ##
DMA channel $i$ copies conesecutive SC1A configurations to the ADC SC1A
   register.  Each SC1A configuration selects an analog input channel.
Channel $i$ is initially triggered by software trigger
   (i.e., DMA_SSRT = i), starting the ADC conversion for the first ADC
   channel configuration.
Loading of subsequent ADC channel configurations is triggered through
   minor loop linking of DMA channel $ii$ to DMA channel $i$.
DMA channel $ii$ is triggered by ADC conversion complete (i.e., COCO), and
   copies the output result of the ADC to consecutive locations in the result
   array.
Channel $ii$ has minor loop link set to channel $i$, which triggers the
   loading of the next channel SC1A configuration to be loaded immediately
   after the current ADC result has been copied to the result array.
After $n$ triggers of channel $i$, the result array contains $n$ ADC results,
   one result per channel in the SC1A table.
N.B., Only the trigger for the first ADC channel is an explicit
   software trigger.  All remaining triggers occur through minor-loop DMA
   channel linking from channel $ii$ to channel $i$.
After each scan through all ADC channels is complete, the ADC readings are
   scattered using the selected "scatter" DMA channel through a major-loop link
   between DMA channel $ii$ and the "scatter" channel.
<img src="multi-channel_ADC_multi-samples_using_DMA.jpg" style="max-height
Step2:  Configure ADC sample rate, etc.
Step3:  Pseudo-code to set DMA channel $i$ to be triggered by ADC0 conversion complete.
DMAMUX0_CFGi[SOURCE] = DMAMUX_SOURCE_ADC0  // Route ADC0 as DMA channel source.
DMAMUX0_CFGi[TRIG] = 0  // Disable periodic trigger.
DMAMUX0_CFGi[ENBL] = 1  // Enable the DMAMUX configuration for channel.
DMA_ERQ[i] = 1  // DMA request input signals and this enable request flag
                // must be asserted before a channel’s hardware service
                // request is accepted (21.3.3/394).
DMA_SERQ = i  // Can use memory mapped convenience register to set instead.
Set DMA mux source for channel 0 to ADC0
Step4:  Analog channel list
List of channels to sample.
Map channels from Teensy references (e.g., A0, A1, etc.) to the Kinetis analog
   pin numbers using the adc.CHANNEL_TO_SC1A_ADC0 mapping.
Step5:  Allocate and initialize device arrays
SD1A register configuration for each ADC channel in the channel_sc1as list.
Copy channel_sc1as list to device.
ADC result array
Initialize to zero.
Step6:  Configure DMA channel $i$
Step7:  Configure DMA channel $ii$
Step8:  Trigger sample scan across selected ADC channels
<ASSISTANT_TASK:>
Python Code:
import pandas as pd
def get_pdb_divide_params(frequency, F_BUS=int(48e6)):
    mult_factor = np.array([1, 10, 20, 40])
    prescaler = np.arange(8)
    clock_divide = (pd.DataFrame([[i, m, p, m * (1 << p)]
                                  for i, m in enumerate(mult_factor) for p in prescaler],
                                 columns=['mult_', 'mult_factor', 'prescaler', 'combined'])
                    .drop_duplicates(subset=['combined'])
                    .sort_values('combined', ascending=True))
    clock_divide['clock_mod'] = (F_BUS / frequency / clock_divide.combined).astype(int)
    return clock_divide.loc[clock_divide.clock_mod <= 0xffff]
PDB0_IDLY = 0x4003600C  # Interrupt Delay Register
PDB0_SC = 0x40036000  # Status and Control Register
PDB0_MOD = 0x40036004  # Modulus Register
PDB_SC_PDBEIE = 0x00020000  # Sequence Error Interrupt Enable
PDB_SC_SWTRIG = 0x00010000  # Software Trigger
PDB_SC_DMAEN = 0x00008000  # DMA Enable
PDB_SC_PDBEN = 0x00000080  # PDB Enable
PDB_SC_PDBIF = 0x00000040  # PDB Interrupt Flag
PDB_SC_PDBIE = 0x00000020  # PDB Interrupt Enable.
PDB_SC_CONT = 0x00000002  # Continuous Mode Enable
PDB_SC_LDOK = 0x00000001  # Load OK
def PDB_SC_TRGSEL(n): return (((n) & 15) << 8)  # Trigger Input Source Select
def PDB_SC_PRESCALER(n): return (((n) & 7) << 12)  # Prescaler Divider Select
def PDB_SC_MULT(n): return (((n) & 3) << 2)  # Multiplication Factor
def PDB_SC_LDMOD(n): return (((n) & 3) << 18)  # Load Mode Select
# PDB0_IDLY = 1; // the pdb interrupt happens when IDLY is equal to CNT+1
proxy.mem_cpy_host_to_device(PDB0_IDLY, np.uint32(1).tostring())
#             software trigger    enable PDB     continuous
PDB_CONFIG = (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT |  PDB_SC_LDMOD(0))
PDB0_SC_ = (PDB_CONFIG | PDB_SC_PRESCALER(clock_divide.prescaler) |
            PDB_SC_MULT(clock_divide.mult_) | 
            PDB_SC_DMAEN | PDB_SC_LDOK)  # load all new values
proxy.mem_cpy_host_to_device(PDB0_SC, np.uint32(PDB0_SC_).tostring())
clock_divide = get_pdb_divide_params(25).iloc[0]
# PDB0_MOD = (uint16_t)(mod-1);
proxy.mem_cpy_host_to_device(PDB0_MOD, np.uint32(clock_divide.clock_mod).tostring())
PDB0_SC_ = (PDB_CONFIG | PDB_SC_PRESCALER(clock_divide.prescaler) |
            PDB_SC_DMAEN | PDB_SC_MULT(clock_divide.mult_) |
            PDB_SC_SWTRIG)  # start the counter!
proxy.mem_cpy_host_to_device(PDB0_SC, np.uint32(PDB0_SC_).tostring())
PDB0_SC_ = 0
proxy.mem_cpy_host_to_device(PDB0_SC, np.uint32(PDB0_SC_).tostring())
Explanation: NB Cannot use PIT to trigger periodic DMA due to hardware bug
See here.
Try using PDB instead??
End of explanation
import arduino_helpers.hardware.teensy as teensy
from arduino_rpc.protobuf import resolve_field_values
from teensy_minimal_rpc import SerialProxy
import teensy_minimal_rpc.DMA as DMA
import teensy_minimal_rpc.ADC as ADC
import teensy_minimal_rpc.SIM as SIM
import teensy_minimal_rpc.PIT as PIT
# Disconnect from existing proxy (if available)
try:
    del proxy
except NameError:
    pass
proxy = SerialProxy()
proxy.pin_mode(teensy.LED_BUILTIN, 1)
from IPython.display import display
proxy.update_sim_SCGC6(SIM.R_SCGC6(PDB=True))
sim_scgc6 = SIM.R_SCGC6.FromString(proxy.read_sim_SCGC6().tostring())
display(resolve_field_values(sim_scgc6)[['full_name', 'value']].T)
# proxy.update_pit_registers(PIT.Registers(MCR=PIT.R_MCR(MDIS=False)))
# pit_registers = PIT.Registers.FromString(proxy.read_pit_registers().tostring())
# display(resolve_field_values(pit_registers)[['full_name', 'value']].T)
import numpy as np
# CORE_PIN13_PORTSET = CORE_PIN13_BITMASK;
# CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK;
#define CORE_PIN13_PORTCLEAR	GPIOC_PCOR
#define CORE_PIN13_PORTSET	GPIOC_PSOR
#define GPIOC_PCOR		(*(volatile uint32_t *)0x400FF088) // Port Clear Output Register
#define GPIOC_PSOR		(*(volatile uint32_t *)0x400FF084) // Port Set Output Register
CORE_PIN13_BIT = 5
GPIOC_PCOR = 0x400FF088  # Port Clear Output Register
GPIOC_PSOR = 0x400FF084  # Port Set Output Register
proxy.mem_cpy_host_to_device(GPIOC_PSOR, np.uint32(1 << CORE_PIN13_BIT).tostring())
proxy.update_dma_mux_chcfg(0, DMA.MUX_CHCFG(ENBL=1, TRIG=0, SOURCE=48))
proxy.update_dma_registers(DMA.Registers(SERQ=0))
proxy.update_dma_registers(DMA.Registers(CERQ=0))
resolve_field_values(DMA.MUX_CHCFG.FromString(proxy.read_dma_mux_chcfg(0).tostring()))[['full_name', 'value']]
print proxy.update_pit_timer_config(0, PIT.TimerConfig(LDVAL=int(48e6)))
print proxy.update_pit_timer_config(0, PIT.TimerConfig(TCTRL=PIT.R_TCTRL(TEN=True)))
pit0 = PIT.TimerConfig.FromString(proxy.read_pit_timer_config(0).tostring())
display(resolve_field_values(pit0)[['full_name', 'value']].T)
PIT_LDVAL0 = 0x40037100  # Timer Load Value Register
PIT_CVAL0  = 0x40037104  # Current Timer Value Register
PIT_TCTRL0 = 0x40037108  # Timer Control Register
proxy.mem_cpy_host_to_device(PIT_TCTRL0, np.uint32(1).tostring())
proxy.mem_cpy_device_to_host(PIT_TCTRL0, 4).view('uint32')[0]
proxy.digital_write(teensy.LED_BUILTIN, 0)
proxy.update_dma_registers(DMA.Registers(SSRT=0))
proxy.free_all()
toggle_pin_addr = proxy.mem_alloc(4)
proxy.mem_cpy_host_to_device(toggle_pin_addr, np.uint32(1 << CORE_PIN13_BIT).tostring())
tcds_addr = proxy.mem_aligned_alloc(32, 2 * 32)
hw_tcds_addr = 0x40009000
tcd_addrs = [tcds_addr + 32 * i for i in xrange(2)]
# Create Transfer Control Descriptor configuration for first chunk, encoded
# as a Protocol Buffer message.
tcd0_msg = DMA.TCD(CITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ITER=1),
                   BITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ITER=1),
                   ATTR=DMA.R_TCD_ATTR(SSIZE=DMA.R_TCD_ATTR._32_BIT,
                                       DSIZE=DMA.R_TCD_ATTR._32_BIT),
                   NBYTES_MLNO=4,
                   SADDR=int(toggle_pin_addr),
                   SOFF=0,
                   SLAST=0,
                   DADDR=int(GPIOC_PSOR),
                   DOFF=0,
#                    DLASTSGA=0,
#                    CSR=DMA.R_TCD_CSR(START=0, DONE=False, ESG=False))
# proxy.update_dma_TCD(0, tcd0_msg)
                   DLASTSGA=int(tcd_addrs[1]),
                   CSR=DMA.R_TCD_CSR(START=0, DONE=False, ESG=True))
# # Convert Protocol Buffer encoded TCD to bytes structure.
tcd0 = proxy.tcd_msg_to_struct(tcd0_msg)
# Create binary TCD struct for each TCD protobuf message and copy to device
# memory.
for i in xrange(2):
    tcd_i = tcd0.copy()
    tcd_i['DADDR'] = [GPIOC_PSOR, GPIOC_PCOR][i]
    tcd_i['DLASTSGA'] = tcd_addrs[(i + 1) % len(tcd_addrs)]
    tcd_i['CSR'] |= (1 << 4)
    proxy.mem_cpy_host_to_device(tcd_addrs[i], tcd_i.tostring())
# Load initial TCD in scatter chain to DMA channel chosen to handle scattering.
proxy.mem_cpy_host_to_device(hw_tcds_addr, tcd0.tostring())
proxy.update_dma_registers(DMA.Registers(SSRT=0))
dma_channel_scatter = 0
dma_channel_i = 1
dma_channel_ii = 2
Explanation: Overview
Use linked DMA channels to perform "scan" across multiple ADC input channels.
After each scan, use DMA scatter chain to write the converted ADC values to a
separate output array for each ADC channel.  The length of the output array to
allocate for each ADC channel is determined by the sample_count in the
example below.
See diagram below.
Channel configuration ##
DMA channel $i$ copies conesecutive SC1A configurations to the ADC SC1A
   register.  Each SC1A configuration selects an analog input channel.
Channel $i$ is initially triggered by software trigger
   (i.e., DMA_SSRT = i), starting the ADC conversion for the first ADC
   channel configuration.
Loading of subsequent ADC channel configurations is triggered through
   minor loop linking of DMA channel $ii$ to DMA channel $i$.
DMA channel $ii$ is triggered by ADC conversion complete (i.e., COCO), and
   copies the output result of the ADC to consecutive locations in the result
   array.
Channel $ii$ has minor loop link set to channel $i$, which triggers the
   loading of the next channel SC1A configuration to be loaded immediately
   after the current ADC result has been copied to the result array.
After $n$ triggers of channel $i$, the result array contains $n$ ADC results,
   one result per channel in the SC1A table.
N.B., Only the trigger for the first ADC channel is an explicit
   software trigger.  All remaining triggers occur through minor-loop DMA
   channel linking from channel $ii$ to channel $i$.
After each scan through all ADC channels is complete, the ADC readings are
   scattered using the selected "scatter" DMA channel through a major-loop link
   between DMA channel $ii$ and the "scatter" channel.
<img src="multi-channel_ADC_multi-samples_using_DMA.jpg" style="max-height: 600px" />
Device
Connect to device
End of explanation
# Set ADC parameters
proxy.setAveraging(16, teensy.ADC_0)
proxy.setResolution(16, teensy.ADC_0)
proxy.setConversionSpeed(teensy.ADC_MED_SPEED, teensy.ADC_0)
proxy.setSamplingSpeed(teensy.ADC_MED_SPEED, teensy.ADC_0)
proxy.update_adc_registers(
    teensy.ADC_0,
    ADC.Registers(CFG2=ADC.R_CFG2(MUXSEL=ADC.R_CFG2.B)))
Explanation: Configure ADC sample rate, etc.
End of explanation
DMAMUX_SOURCE_ADC0 = 40  # from `kinetis.h`
DMAMUX_SOURCE_ADC1 = 41  # from `kinetis.h`
#    DMAMUX0_CFGi[SOURCE] = DMAMUX_SOURCE_ADC0  // Route ADC0 as DMA channel source.
#    DMAMUX0_CFGi[TRIG] = 0  // Disable periodic trigger.
#    DMAMUX0_CFGi[ENBL] = 1  // Enable the DMAMUX configuration for channel.
proxy.update_dma_mux_chcfg(dma_channel_ii,
                           DMA.MUX_CHCFG(SOURCE=DMAMUX_SOURCE_ADC0,
                                         TRIG=False,
                                         ENBL=True))
# DMA request input signals and this enable request flag
# must be asserted before a channel’s hardware service
# request is accepted (21.3.3/394).
#    DMA_SERQ = i
proxy.update_dma_registers(DMA.Registers(SERQ=dma_channel_ii))
proxy.enableDMA(teensy.ADC_0)
proxy.DMA_registers().loc['']
dmamux = DMA.MUX_CHCFG.FromString(proxy.read_dma_mux_chcfg(dma_channel_ii).tostring())
resolve_field_values(dmamux)[['full_name', 'value']]
adc0 = ADC.Registers.FromString(proxy.read_adc_registers(teensy.ADC_0).tostring())
resolve_field_values(adc0)[['full_name', 'value']].loc[['CFG2', 'SC1A', 'SC3']]
Explanation: Pseudo-code to set DMA channel $i$ to be triggered by ADC0 conversion complete.
DMAMUX0_CFGi[SOURCE] = DMAMUX_SOURCE_ADC0  // Route ADC0 as DMA channel source.
DMAMUX0_CFGi[TRIG] = 0  // Disable periodic trigger.
DMAMUX0_CFGi[ENBL] = 1  // Enable the DMAMUX configuration for channel.
DMA_ERQ[i] = 1  // DMA request input signals and this enable request flag
                // must be asserted before a channel’s hardware service
                // request is accepted (21.3.3/394).
DMA_SERQ = i  // Can use memory mapped convenience register to set instead.
Set DMA mux source for channel 0 to ADC0
End of explanation
import re
import numpy as np
import pandas as pd
import arduino_helpers.hardware.teensy.adc as adc
# The number of samples to record for each ADC channel.
sample_count = 10
teensy_analog_channels = ['A0', 'A1', 'A0', 'A3', 'A0']
sc1a_pins = pd.Series(dict([(v, adc.CHANNEL_TO_SC1A_ADC0[getattr(teensy, v)])
                            for v in dir(teensy) if re.search(r'^A\d+', v)]))
channel_sc1as = np.array(sc1a_pins[teensy_analog_channels].tolist(), dtype='uint32')
Explanation: Analog channel list
List of channels to sample.
Map channels from Teensy references (e.g., A0, A1, etc.) to the Kinetis analog
   pin numbers using the adc.CHANNEL_TO_SC1A_ADC0 mapping.
End of explanation
proxy.free_all()
N = np.dtype('uint16').itemsize * channel_sc1as.size
# Allocate source array
adc_result_addr = proxy.mem_alloc(N)
# Fill result array with zeros
proxy.mem_fill_uint8(adc_result_addr, 0, N)
# Copy channel SC1A configurations to device memory
adc_sda1s_addr = proxy.mem_aligned_alloc_and_set(4, channel_sc1as.view('uint8'))
# Allocate source array
samples_addr = proxy.mem_alloc(sample_count * N)
tcds_addr = proxy.mem_aligned_alloc(32, sample_count * 32)
hw_tcds_addr = 0x40009000
tcd_addrs = [tcds_addr + 32 * i for i in xrange(sample_count)]
hw_tcd_addrs = [hw_tcds_addr + 32 * i for i in xrange(sample_count)]
# Fill result array with zeros
proxy.mem_fill_uint8(samples_addr, 0, sample_count * N)
# Create Transfer Control Descriptor configuration for first chunk, encoded
# as a Protocol Buffer message.
tcd0_msg = DMA.TCD(CITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ITER=1),
                   BITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ITER=1),
                   ATTR=DMA.R_TCD_ATTR(SSIZE=DMA.R_TCD_ATTR._16_BIT,
                                       DSIZE=DMA.R_TCD_ATTR._16_BIT),
                   NBYTES_MLNO=channel_sc1as.size * 2,
                   SADDR=int(adc_result_addr),
                   SOFF=2,
                   SLAST=-channel_sc1as.size * 2,
                   DADDR=int(samples_addr),
                   DOFF=2 * sample_count,
                   DLASTSGA=int(tcd_addrs[1]),
                   CSR=DMA.R_TCD_CSR(START=0, DONE=False, ESG=True))
# Convert Protocol Buffer encoded TCD to bytes structure.
tcd0 = proxy.tcd_msg_to_struct(tcd0_msg)
# Create binary TCD struct for each TCD protobuf message and copy to device
# memory.
for i in xrange(sample_count):
    tcd_i = tcd0.copy()
    tcd_i['SADDR'] = adc_result_addr
    tcd_i['DADDR'] = samples_addr + 2 * i
    tcd_i['DLASTSGA'] = tcd_addrs[(i + 1) % len(tcd_addrs)]
    tcd_i['CSR'] |= (1 << 4)
    proxy.mem_cpy_host_to_device(tcd_addrs[i], tcd_i.tostring())
# Load initial TCD in scatter chain to DMA channel chosen to handle scattering.
proxy.mem_cpy_host_to_device(hw_tcd_addrs[dma_channel_scatter],
                             tcd0.tostring())
print 'ADC results:', proxy.mem_cpy_device_to_host(adc_result_addr, N).view('uint16')
print 'Analog pins:', proxy.mem_cpy_device_to_host(adc_sda1s_addr, len(channel_sc1as) *
                                                   channel_sc1as.dtype.itemsize).view('uint32')
Explanation: Allocate and initialize device arrays
SD1A register configuration for each ADC channel in the channel_sc1as list.
Copy channel_sc1as list to device.
ADC result array
Initialize to zero.
End of explanation
ADC0_SC1A = 0x4003B000  # ADC status and control registers 1
sda1_tcd_msg = DMA.TCD(CITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ELINK=False, ITER=channel_sc1as.size),
                       BITER_ELINKNO=DMA.R_TCD_ITER_ELINKNO(ELINK=False, ITER=channel_sc1as.size),
                       ATTR=DMA.R_TCD_ATTR(SSIZE=DMA.R_TCD_ATTR._32_BIT,
                                           DSIZE=DMA.R_TCD_ATTR._32_BIT),
                       NBYTES_MLNO=4,
                       SADDR=int(adc_sda1s_addr),
                       SOFF=4,
                       SLAST=-channel_sc1as.size * 4,
                       DADDR=int(ADC0_SC1A),
                       DOFF=0,
                       DLASTSGA=0,
                       CSR=DMA.R_TCD_CSR(START=0, DONE=False))
proxy.update_dma_TCD(dma_channel_i, sda1_tcd_msg)
Explanation: Configure DMA channel $i$
End of explanation
ADC0_RA = 0x4003B010  # ADC data result register
ADC0_RB = 0x4003B014  # ADC data result register
tcd_msg = DMA.TCD(CITER_ELINKYES=DMA.R_TCD_ITER_ELINKYES(ELINK=True, LINKCH=1, ITER=channel_sc1as.size),
                  BITER_ELINKYES=DMA.R_TCD_ITER_ELINKYES(ELINK=True, LINKCH=1, ITER=channel_sc1as.size),
                  ATTR=DMA.R_TCD_ATTR(SSIZE=DMA.R_TCD_ATTR._16_BIT,
                                      DSIZE=DMA.R_TCD_ATTR._16_BIT),
                  NBYTES_MLNO=2,
                  SADDR=ADC0_RA,
                  SOFF=0,
                  SLAST=0,
                  DADDR=int(adc_result_addr),
                  DOFF=2,
                  DLASTSGA=-channel_sc1as.size * 2,
                  CSR=DMA.R_TCD_CSR(START=0, DONE=False,
                                    MAJORELINK=True,
                                    MAJORLINKCH=dma_channel_scatter))
proxy.update_dma_TCD(dma_channel_ii, tcd_msg)
Explanation: Configure DMA channel $ii$
End of explanation
# Clear output array to zero.
proxy.mem_fill_uint8(adc_result_addr, 0, N)
proxy.mem_fill_uint8(samples_addr, 0, sample_count * N)
# Software trigger channel $i$ to copy *first* SC1A configuration, which
# starts ADC conversion for the first channel.
#
# Conversions for subsequent ADC channels are triggered through minor-loop
# linking from DMA channel $ii$ to DMA channel $i$ (*not* through explicit
# software trigger).
print 'ADC results:'
for i in xrange(sample_count):
    proxy.update_dma_registers(DMA.Registers(SSRT=dma_channel_i))
    # Display converted ADC values (one value per channel in `channel_sd1as` list).
    print '  Iteration %s:' % i, proxy.mem_cpy_device_to_host(adc_result_addr, N).view('uint16')
print ''
print 'Samples by channel:'
# Trigger once per chunk
# for i in xrange(sample_count):
# proxy.update_dma_registers(DMA.Registers(SSRT=0))
device_dst_data = proxy.mem_cpy_device_to_host(samples_addr, sample_count * N)
pd.DataFrame(device_dst_data.view('uint16').reshape(-1, sample_count).T,
             columns=teensy_analog_channels)
Explanation: Trigger sample scan across selected ADC channels
End of explanation
<END_TASK>