Hi Mark,
Many thanks for your effort. It has been very helpful in encouraging me to abandon the MCUExpresso API and hard code this part of the firmware. I selected your third approach, the most low level because it is the easiest to follow in terms of the register data needed to set everything up and stepping through the code it’s easy to see where one should be looking for changes in the registers when each line has the memory address written in the macro.
There is still a mistake in what’s copied below. It puts out 10kHz (at 120 MHz system clock and no FTM pre-scaling) with 50% duty which equates to the first CnV value in the buffer that the DMA should read. Not sure where the mistake(s) are presently as the end of the working day has crept up on me. If you see anything odd please do point it out. I’ll be back at this on Monday. I suspect that the DMA is either not set up quite right (I’m pointing it at the wrong place etc.) or perhaps the FTM is not requesting the next two bytes from the DMA when CNT == MOD. The processor reference guide gives very little detail about how the timer makes the request to the DMA – certainly the request source is set up in the DMAMUX etc. but the FTM diagram on pp 979 of the processor manual doesn’t have a DMA line anywhere (as far as I can see). Similarly Application Note AN5261 only mentions the DMA in the first paragraph and application note AN4560 doesn’t mention the DMA at all.
#define BUFF_LENGTH 16U
#define SIM_BLOCK 0x40047000 // system integration module
#define SIM_PORT_CLK_GATE_OFFSET 0x00001038 // Register that enables the clock gate to the ports.
#define PORTA_BLOCK 0x40049000 // Port A base address.
#define PORTC_BLOCK 0x4004b000 // Port C base address.
#define DMAMUX0_BLOCK 0x40021000 // DMAMUX0
#define FTM0_BASE_ADDR 0x40038000 // FlexTimer 0 (TPM0) base address.
#define FTM0_MOD 0x00000008 // FlexTimer 0 MOD register offset
#define FTM0_C6SC_OFFSET 0x0000003C // Offset for FTM0_C6SC register offset
#define FTM0_C6V_OFFSET 0x00000040 // Offset for FTM0_C6V register offset
#define FTM0_C7SC_OFFSET 0x00000044 // Offset for FTM0_C7SC register offset
#define FTM0_C7V_OFFSET 0x00000048 // Offset for FTM0_C7V register offset
#define FTM0_C0SC_OFFSET 0x0000000C // Offset for FTM0_C0SC register offset
#define FTM0_C0V_OFFSET 0x00000010 // Offset for FTM0_C0V register offset
#define FTM0_C1SC_OFFSET 0x00000014 // Offset for FTM0_C1SC register offset
#define FTM0_C1V_OFFSET 0x00000018 // Offset for FTM0_C1V register offset
#define eDMA_DESCRIPTORS 0x40009000 // eDMA descriptor memory
#define eDMA_BASE_ADDR 0x40008000 // eDMA base Address
static void fnBareMetalSPWM(unsigned short *ptrBuffer, unsigned long ulBufferLength)
{
// System Clock Gating Control Register 5 (SIM_SCGC5), switch on clock for port A
//*(volatile unsigned long *)(SIM_BLOCK + SIM_PORT_CLK_GATE_OFFSET) |= 0x00000200;
// System Clock Gating Control Register 5 (SIM_SCGC5), switch on clock for port C
*(volatile unsigned long *)(SIM_BLOCK + SIM_PORT_CLK_GATE_OFFSET) |= 0x00000800;
// FTM. Ensure that the FlexTimer/TPM module is powered up
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;
// FTM0_CH1 on PTC2 (alt. function 4) and high drive strength
*(volatile unsigned long *)(PORTC_BLOCK + 0x08) = (0x00000400 | 0x00000040);;
// FTM0_CH6 on PTA1 (alt. function 3) and high drive strength
//*(volatile unsigned long *)(PORTA_BLOCK + 0x04) = 0x00000340;
// FTM0_CH7 on PTA2 (alt. function 3)
// *(volatile unsigned long *)(PORTA_BLOCK + 0x08) = (0x00000300 | 0x00000040);
// allow timer to continue operating when debugging
*(unsigned long *)(FTM0_BASE_ADDR + 0x084) = 0x000000c0;
// high-true pulses/enable DMA trigger from this channel
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C1SC_OFFSET) = (0x00000020 | 0x00000008 | 0x00000001 | 0x00000040);
// Set counter initial value to zero
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x04c) = 0;
// Set counter MOD register to the value that provides the frequency of interest.
*(unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = 0x1770; // 10 khz with a prescaler of 1 and a 120MHz clock.
// FTM External Trigger (FTMx_EXTTRIG) trigger from CH0
//*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x6C) = 0x10;
// prepare first PWM value (place it in the CnV register)
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C1V_OFFSET) = *ptrBuffer;
// eDMA TCD Signed Source Address Offset (DMA_TCD1_SOFF) i.e. source address increment per DMA minor loop
*(signed short *)(eDMA_DESCRIPTORS + 0x024) = sizeof(unsigned short);
// eDMA TCD Signed Destination Address Offset (DMA_TCD1_DOFF) i.e. destination address is not incremented
*(signed short *)(eDMA_DESCRIPTORS + 0x034) = 0;
// eDMA TCD Last Destination Address Adjustment/Scatter Gather Address (DMA_TCD1_DLASTSGA). No destination displacement on transmit buffer completion
*(volatile signed long *)(eDMA_DESCRIPTORS + 0x038) = 0;
// eDMA TCD Last Source Address Adjustment (DMA_TCD1_SLAST). When the buffer has been transmitted set the destination back to the start of the buffer.
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x02c) = (-(signed long)(ulBufferLength));
// eDMA TCD Transfer Attributes (DMA_TCD1_ATTR). Transfer size is half-words i.e. 16 bits, the same as the destination register width.
*(unsigned short *)(eDMA_DESCRIPTORS + 0x026) = (0x0001 | 0x0100);
// eDMA TCD Source Address (DMA_TCD1_SADDR).
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x020) = (unsigned long)ptrBuffer;
// eDMA TCD Control and Status (DMA_TCD1_CSR). Free-running mode without any interrupt
*(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x03c) = 0;
// eDMA TCD Destination Address (DMA_TCD1_DADDR). Destination address is the FTM CnV register (the PWM register)
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x030) = (unsigned long)((FTM0_BASE_ADDR + FTM0_C1V_OFFSET));
// eDMA TCD Minor Byte Count (Minor Loop Mapping Disabled) (DMA_TCD1_NBYTES_MLNO). Each request starts a single transfer of this size (minor byte transfer count)
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x028) = sizeof(unsigned short);
/* eDMA TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Enabled) (DMA_TCD1_BITER_ELINKYES).
*(unsigned short *)(eDMA_DESCRIPTORS + 0x03e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x036) = (signed short)(ulBufferLength / sizeof(unsigned short));
// System Clock Gating Control Register 6 (SIM_SCGC6). Enable DMA multiplexer 0.
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x00000002;
// Channel Configuration register (DMAMUX_CHCFG0). Connect trigger source to DMA channel.
*(unsigned char *)(DMAMUX0_BLOCK + 0x01) = (unsigned char)(0x14 | 0x80);
// eDMA Enable Request Register (DMA_ERQ). Enable the channel's operation
*(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000002;
// FTM Status And Control (FTM0_SC). Enable FTM0 operation (system clock)
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x000) = 0x00000008;
}
int main(void) {
int i = 0;
// Duty cycle in percent. This represents one cycle of a sine wave
unsigned short dutyCyclePercent[BUFF_LENGTH] = {50, 69, 85, 96, 100, 96, 85, 69, 50, 31, 15, 4, 0, 4, 15, 31};
// buffers used by DMA to hold the data that will be passed to the FTM.
unsigned short sineData[BUFF_LENGTH];
unsigned short *ptrBuffer = &sineData[0];
// ensure that the FlexTimer/TPM module is powered up
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;
// Set counter MOD register to the value that provides the frequency of interest.
*(unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = 0x1770;
unsigned long *mod;
mod = (FTM0_BASE_ADDR + FTM0_MOD);
// write the data that will be passed by the DMA to the FTM CnV registers.
for(i = 0 ; i<BUFF_LENGTH; i++)
{
sineData[i] = (*mod * dutyCyclePercent[i]) / 100;
}
fnBareMetalSPWM(sineData, sizeof(sineData));
}
Cheers,
James