Ambiguity in MK20DX128 reference manual regarding second PDB channel

whollender

Well-known member
I'm starting to define the structure and program flow of my first Teensy 3 project. The project involves audio input (using the ADC) and audio output (using the FTM). Because the PDB output trigger is connected to the ADC, I was hoping to use it to define my sample rate. The reference manual also mentions a second PDB channel that is connected to the FTM HW synchronization input. This would be ideal for my project so that I can adjust the relative delay of the FTM update and ADC conversion if necessary while they both use the same sample clock, but I noticed that the reference manual for the MK20DX128 is a bit ambiguous about the second channel.

In chapter 3.8.1 (the chip configuration section regarding the PDB) it notes that there are 2 PDB channels: Channel 0 is connected to the ADC, and channel 1 is connected to one of the FTM's hardware synchronization trigger inputs (in sections 3.8.1.1.1 and 3.8.1.2).

However, the PDB chapter (chapter 34) itself only lists addresses for 1 PDB channel, channel 0. The header file for the mk20dx128 included with Teensy3 also only has addresses for channel 0:

Code:
// Chapter 34: Programmable Delay Block (PDB)
#define PDB0_SC                 *(volatile uint32_t *)0x40036000 // Status and Control Register
#define PDB_SC_LDMOD(n)			(((n) & 3) << 18)	// Load Mode Select
#define PDB_SC_PDBEIE			0x00020000		// Sequence Error Interrupt Enable
#define PDB_SC_SWTRIG			0x00010000		// Software Trigger
#define PDB_SC_DMAEN			0x00008000		// DMA Enable
#define PDB_SC_PRESCALER(n)		(((n) & 7) << 12)	// Prescaler Divider Select
#define PDB_SC_TRGSEL(n)		(((n) & 15) << 8)	// Trigger Input Source Select
#define PDB_SC_PDBEN			0x00000080		// PDB Enable
#define PDB_SC_PDBIF			0x00000040		// PDB Interrupt Flag
#define PDB_SC_PDBIE			0x00000020		// PDB Interrupt Enable.
#define PDB_SC_MULT(n)			(((n) & 3) << 2)	// Multiplication Factor
#define PDB_SC_CONT			0x00000002		// Continuous Mode Enable
#define PDB_SC_LDOK			0x00000001		// Load OK
#define PDB0_MOD                *(volatile uint32_t *)0x40036004 // Modulus Register
#define PDB0_CNT                *(volatile uint32_t *)0x40036008 // Counter Register
#define PDB0_IDLY               *(volatile uint32_t *)0x4003600C // Interrupt Delay Register
#define PDB0_CH0C1              *(volatile uint32_t *)0x40036010 // Channel n Control Register 1
#define PDB0_CH0S               *(volatile uint32_t *)0x40036014 // Channel n Status Register
#define PDB0_CH0DLY0            *(volatile uint32_t *)0x40036018 // Channel n Delay 0 Register
#define PDB0_CH0DLY1            *(volatile uint32_t *)0x4003601C // Channel n Delay 1 Register
#define PDB0_POEN               *(volatile uint32_t *)0x40036190 // Pulse-Out n Enable Register
#define PDB0_PO0DLY             *(volatile uint32_t *)0x40036194 // Pulse-Out n Delay Register
#define PDB0_PO1DLY             *(volatile uint32_t *)0x40036198 // Pulse-Out n Delay Register

But the header file that you can download from freescale does reference a second PDB channel (from MK20DZ10.h):

Code:
/* PDB - Register instance definitions */
/* PDB0 */
#define PDB0_SC                                  PDB_SC_REG(PDB0_BASE_PTR)
#define PDB0_MOD                                 PDB_MOD_REG(PDB0_BASE_PTR)
#define PDB0_CNT                                 PDB_CNT_REG(PDB0_BASE_PTR)
#define PDB0_IDLY                                PDB_IDLY_REG(PDB0_BASE_PTR)
#define PDB0_CH0C1                               PDB_C1_REG(PDB0_BASE_PTR,0)
#define PDB0_CH0S                                PDB_S_REG(PDB0_BASE_PTR,0)
#define PDB0_CH0DLY0                             PDB_DLY_REG(PDB0_BASE_PTR,0,0)
#define PDB0_CH0DLY1                             PDB_DLY_REG(PDB0_BASE_PTR,0,1)
#define PDB0_CH1C1                               PDB_C1_REG(PDB0_BASE_PTR,1)
#define PDB0_CH1S                                PDB_S_REG(PDB0_BASE_PTR,1)
#define PDB0_CH1DLY0                             PDB_DLY_REG(PDB0_BASE_PTR,1,0)
#define PDB0_CH1DLY1                             PDB_DLY_REG(PDB0_BASE_PTR,1,1)
#define PDB0_DACINTC0                            PDB_INTC_REG(PDB0_BASE_PTR,0)
#define PDB0_DACINT0                             PDB_INT_REG(PDB0_BASE_PTR,0)
#define PDB0_DACINTC1                            PDB_INTC_REG(PDB0_BASE_PTR,1)
#define PDB0_DACINT1                             PDB_INT_REG(PDB0_BASE_PTR,1)
#define PDB0_PO0EN                               PDB_POEN_REG(PDB0_BASE_PTR)
#define PDB0_PO0DLY                              PDB_PODLY_REG(PDB0_BASE_PTR)

The header file from freescale also defines this structure that appears to set the offsets of all the PDB registers from the PDB base address (0x40036000):

Code:
/** PDB - Peripheral register structure */
typedef struct PDB_MemMap {
  uint32_t SC;                                     /**< Status and Control Register, offset: 0x0 */
  uint32_t MOD;                                    /**< Modulus Register, offset: 0x4 */
  uint32_t CNT;                                    /**< Counter Register, offset: 0x8 */
  uint32_t IDLY;                                   /**< Interrupt Delay Register, offset: 0xC */
  struct {                                         /* offset: 0x10, array step: 0x28 */
    uint32_t C1;                                     /**< Channel n Control Register 1, array offset: 0x10, array step: 0x28 */
    uint32_t S;                                      /**< Channel n Status Register, array offset: 0x14, array step: 0x28 */
    uint32_t DLY[2];                                 /**< Channel n Delay 0 Register..Channel n Delay 1 Register, array offset: 0x18, array step: index*0x28, index2*0x4 */
    uint8_t RESERVED_0[24];
  } CH[2];
  uint8_t RESERVED_0[240];
  struct {                                         /* offset: 0x150, array step: 0x8 */
    uint32_t INTC;                                   /**< DAC Interval Trigger n Control Register, array offset: 0x150, array step: 0x8 */
    uint32_t INT;                                    /**< DAC Interval n Register, array offset: 0x154, array step: 0x8 */
  } DAC[2];
  uint8_t RESERVED_1[48];
  uint32_t POEN;                                   /**< Pulse-Out n Enable Register, offset: 0x190 */
  uint32_t PODLY;                                  /**< Pulse-Out n Delay Register, offset: 0x194 */
} volatile *PDB_MemMapPtr;


// Some code removed

/* PDB - Peripheral instance base addresses */
/** Peripheral PDB0 base pointer */
#define PDB0_BASE_PTR                            ((PDB_MemMapPtr)0x40036000u)

Based on the way the structure is defined, it looks like the registers for the second channel would start at address 0x40036038, so the header file included with Teensy could be updated with the following register addresses, if this second PDB channel actually exists :)

Code:
#define PDB0_CH1C1              *(volatile uint32_t *)0x40036038 // Channel 1 Control Register 1
#define PDB0_CH1S               *(volatile uint32_t *)0x4003603C // Channel 1 Status Register
#define PDB0_CH1DLY0            *(volatile uint32_t *)0x40036040 // Channel 1 Delay 0 Register
#define PDB0_CH1DLY1            *(volatile uint32_t *)0x40036044 // Channel 1 Delay 1 Register

I haven't had a chance to actually try anything with this mythological second PDB channel :), so I can't say for sure if it exists yet, but it might be nice to let Freescale know that their documentation needs some clarification.

I should be able to get some code in place to actually test this out soon, but I thought I'd document some of my frustration trying to get this figured out first :)

Edit: I actually realized that the pins that I connected my output amp to are on FTM1 instead of FTM0, so I'll probably end up using the PDB interrupt instead of the second PDB channel for output synchronization. Because I won't be using the second PDB channel, I won't plan on putting any code together to test it out.
 
Last edited:
Back
Top