Hello,
I am using MK22F512 series controller. We want to generate frequency using GPIO Pin.
Can anyone help me out?
Thanks,
Mayur
Hi Mayur,
If you look into KSDK 2.1, you will find a pit demo for FRDM-K22F, which sets up a one-second interrupt for PIT, you may modify the period for your application and toggle the GPIO in the ISR.
To download KSDK 2.1, please refer to Welcome to MCUXpresso | MCUXpresso Config Tools
Hope that helps,
Have a great day,
Kan
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi
In case you prefer to toggle an output in an ISR (it uses a lot more processor time and is only recommended for lower frequencies - it will also have more jitter) the only change to the above code is to add an IRQ handler call-back.
DMA can also be used at the same time but usually there would be no real advantage in this.
...
pit_setup.int_handler = irq_toggle_output; // rather than pit_setup.int_handler = 0;
...
// IRQ handler to toggle LED rather than allow DMA to do it
//
static void irq_toggle_output(void)
{
_TOGGLE_PORT(A, PORTA_BIT1)
}
Further notes:
- when using interrupts for generating a square wave it may have some jitter due to other interrupts or due to critical regions globally disabling interrupts
- when generating fast speeds there is a risk that a period may be missed and thus a half-cycle jump occurs (when the processor misses servicing quickly enough)
- when deleting/programming flash it is necessary to disable interrupts in most cases so the frequency will also stop (eg. during about 13ms for a sector erase) - this may be a (serious) problem for some uses
Conclusion:
As long as there is a single DMA channel available in any Kinetis part (K, KL etc.) with DMA it would be unwise to use interrupts to generate a GPIO square wave in the majority of cases. In such instances the solution would represent a poor solution, which can easily be improved greatly with the simple technique shown.
Regards
Mark
Hi Mayur
If your pin has FlexTimer functionality you can use a FlexTimer to generate a frequency (or PWM).
In case you don't have such a peripheral function and the frequency is not too high you can use any timer interrupt (FlexTimer, PIT, SYSTICK, LPTMR) to generate a periodic interrupt and toggle the GPIO in it.
In the second case a more efficient solution is also to use a periodic timer to generate a DMA trigger that writes to the GPIO toggle register of the GPIO in question (this can also toggle multiple outputs with the same frequency with no CPU intervention and so allows high speeds).
Regards
Mark
Kinetis for Professionals: http://www.utasker.com/kinetis.html
K22: http://www.utasker.com/kinetis/FRDM-K22F.html
HW timers: http://www.utasker.com/docs/uTasker/uTaskerHWTimers.PDF
Hi Mark,
Thank you for reply.
My Pin don't have FlexTimer functionality. So, i have to go your 3rd option (periodic Timer).
I have tried and got its working only for 1 to 10 Hz. for higher value its not working.
Can you share any example code?
Regards,
Mayur Savaj
Hi
In the uTasker project I can generate a 500kHz frequency on one (or more bits on a port) ports when I configure a PIT as follows:
PIT_SETUP pit_setup; // interrupt configuration parameters
pit_setup.int_type = PIT_INTERRUPT;
pit_setup.int_handler = 0; // no interrupt due to DMA
pit_setup.int_priority = PIT2_INTERRUPT_PRIORITY; // not used since we don't enable the interrupt
pit_setup.mode = (PIT_PERIODIC | PIT_OUTPUT_DMA_TRIG); // periodic with DMA trigger to control a port toggle
pit_setup.ulPortBits = PORTA_BIT1; // toggle PTA1 on each PIT trigger
pit_setup.ucPortRef = PORTA; // the pin(s) to be toggled is on port A
pit_setup.ucPIT = 2; // use PIT2 (this will use DMA channel 2, 0 would use DMA channel 0, etc.)
pit_setup.count_delay = PIT_US_DELAY(1); // 1us period for 500kHz frequency generated
fnConfigureInterrupt((void *)&pit_setup); // configure PITSo you can get an idea of the method, this is what the configuration does when PIT_OUTPUT_DMA_TRIG is set (it also sets up the PIT for periodic operation - not shown):
if ((PIT_settings->mode & PIT_OUTPUT_DMA_TRIG) != 0) { // if the PIT is to trigger a port toggle by DMA
// Note that PIT channel number must match with the DMA channel (hardware requirement) and the DMA channel is chosen here to match the PIT
//
static unsigned long ulPortBits[PITS_AVAILABLE] = {0};
ulPortBits[PIT_settings->ucPIT] = PIT_settings->ulPortBits; // the port bit(s) to be toggled
fnConfigDMA_buffer(PIT_settings->ucPIT, (DMAMUX0_DMA0_CHCFG_SOURCE_PIT0 + PIT_settings->ucPIT), sizeof(ulPortBits[PIT_settings->ucPIT]), &ulPortBits[PIT_settings->ucPIT], (unsigned long *)(GPIO_BLOCK + (0x040 * PIT_settings->ucPortRef) + 0x00c), (DMA_DIRECTION_OUTPUT | DMA_LONG_WORDS), 0, 0); // source is the port bit and destination is the GPIO toggle register
fnDMA_BufferReset(PIT_settings->ucPIT, DMA_BUFFER_START);
}Note that in the case of a PIT, the PIT can only trigger the DMA channel with the same number as itself.
The real work to set up the DMA for a K part (KL parts have a different DMA controller - the uTasker project supports both in a compatible manner but I only show the relevant K part code here for clarity) is
// Buffer source to fixed destimation address or fixed source address to buffer
//
static void fnConfigDMA_buffer(unsigned char ucDMA_channel, unsigned char ucDmaTriggerSource, unsigned long ulBufLength, void *ptrBufSource, void *ptrBufDest, unsigned long ulRules, void(*int_handler)(void), int int_priority)
{
unsigned char ucSize = (unsigned char)(ulRules & 0x07); // transfer size 1, 2 or 4 bytes
KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;
ptrDMA_TCD += ucDMA_channel;
ptrDMA_TCD->DMA_TCD_SOFF = ucSize; // source increment (buffer)
ptrDMA_TCD->DMA_TCD_DOFF = 0; // destination not incremented
ptrDMA_TCD->DMA_TCD_DLASTSGA = 0; // no destination displacement on transmit buffer completion
ptrDMA_TCD->DMA_TCD_SLAST = (-(signed long)(ulBufLength)); // when the buffer has been transmitted set the destination back to the start of it
switch (ucSize) {
default:
case 1:
ucSize = 1;
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_8 | DMA_TCD_ATTR_SSIZE_8); // transfer sizes bytes
break;
case 2:
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_16 | DMA_TCD_ATTR_SSIZE_16); // transfer sizes words
break;
case 4:
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_32 | DMA_TCD_ATTR_SSIZE_32); // transfer sizes long words
break;
}
ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)ptrBufSource; // source buffer
ptrDMA_TCD->DMA_TCD_NBYTES_ML = ucSize; // each request starts a single transfer
_DMA_handler[ucDMA_channel] = int_handler; // user interrupt callback
ptrDMA_TCD->DMA_TCD_CSR = 0; // free-running mode without any interrupt
ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)ptrBufDest; // destination
ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = (signed short)(ulBufLength / ucSize); // the number of service requests
POWER_UP(6, SIM_SCGC6_DMAMUX0); // enable DMA multiplexer 0
*(unsigned char *)(DMAMUX0_BLOCK + ucDMA_channel) = (ucDmaTriggerSource | DMAMUX_CHCFG_ENBL); // connect trigger source to DMA channel
DMA_ERQ |= (DMA_ERQ_ERQ0 << ucDMA_channel); // enable request source
}I have also attached a FRDM-K22F binary that will generate a 1MHz signal on the RED led using the technique (using the code above). It also has USB-CDC device on the USB with a command line menu allowing various functions. One needs to be aware that, although the frequency being generated is high precision with low jitter and doesn't use CPU power, the DMA transfers are loading the bus "slightly" - the frequency continues to be generated when the CPU is halted (eg. with debugger) as long as the PIT continues to run. If the toggling speed is increased to a higher rate there can be an impact with USB operation (which also needs to transfer data via the bus) which can cause USB unreliability - this happens when 20MHz is attempted, for example, but 1MHz is not an issue.
Regards
Mark