Peripheral DMA Controller (PDC)


Sam7 Peripheral DMA Controller (PDC)

Having gotten the AIC working I thought I'd take a look at the DMA controller.
I was thinking about using the AIC for interrupt driven serial I/O but it seems for serial input the PDC can do what I want and I don't really need to interrupt drive it.

The PDC doesn't have a control register space of its own. Instead all the peripherals which have PDC support include PDC register at an offset of hex 100 from the peripheral base address.

If for example I'm interested in DMA for a USART with a base address of FFFC0000 then the Receive Pointer Register for this USART will be at FFFC0100
These are the PDC register offsets etc....
Offset Register Register Name Read/Write Reset
0x100 Receive Pointer Register PERIPH(1)_RPR Read/Write 0x0
0x104 Receive Counter Register PERIPH_RCR Read/Write 0x0
0x108 Transmit Pointer Register PERIPH_TPR Read/Write 0x0
0x10C Transmit Counter Register PERIPH_TCR Read/Write 0x0
0x110 Receive Next Pointer Register PERIPH_RNPR Read/Write 0x0
0x114 Receive Next Counter Register PERIPH_RNCR Read/Write 0x0
0x118 Transmit Next Pointer Register PERIPH_TNPR Read/Write 0x0
0x11C Transmit Next Counter Register PERIPH_TNCR Read/Write 0x0
0x120 PDC Transfer Control Register PERIPH_PTCR Write-only -
0x124 PDC Transfer Status Register PERIPH_PTSR Read-only 0x0

I've defined these as constants in the include file PDC.FTH - I renamed PERIPH(1)_RPR to PERIPH_RPR.

In emforth the base address of the system USART is kept in a variable called *USART.
Which USART is used depends on the particular SAM7 board I am running and is usually figured out by the kernel at boot time.
By using *USART to access the PDC my code should be work on different boards without recompilation.

Kernel hack.

To allow the lowest level serial input routines to be redirected the variable *TIMEDSIN was added to emforth Ver1.78.
This normally points to the rountine "TSIN" which is a serial byte input routine with an optional timeout.
This is called by several serial input words including SIN.
I needed to be able to point this to my new buffered version of TSIN.

The test code worked and adds buffered serial input to emforth. The buffer is 256 bytes but could easily be changed for more or less.

The test code


INCLUDE ..\include\PDC.FTH

VARIABLE SERINBUF 100 VARALLOT // serial input buffer
VARIABLE SERININDEX // input buff pointer


For starters the PDC register address offsets are included and the buffer and pointer declared.

		SERINBUF SERINPTR @ + *USART @ PERIPH_RPR + @ - 	// Is there a new char.
		IF DROP SERINBUF SERININDEX @ + C@ 1 SERININDEX +! 	// yes, get it and inc pointer
		SERININDEX @ FF AND					// Do we need to wrap the pointer ?
			0= IF						// yes, wrap it
				SERINBUF *USART @ PERIPH_RNPR + ! 	// Set up serial in next ptr
				100 *USART @ PERIPH_RNCR + !  		// Set up serial in next cntr
				0 SERININDEX !				// wrap pointer to zero
		EXIT THEN 						// Return char - just exit
		DUP 0< IF DROP -1 THEN                       // If count negative force it to -1.
		1- DUP
	-1 = UNTIL

The serial input routine checks our buffer index plus buffer address against the PDC serial in pointer to determine if a new byte is present. If so it fetches it and adjusts the index.
If the index has reached past the end of the buffer it is reset and the NEXT pointer and counter are rewritten. The code is a little awkward because it can also loop around a set number of times as a crude timeout or loop forever. This works fine for the non-interrupted kernel but would most likely be changed to use a timer in any serious applications.

The rewriting of the NEXT registers could be done using interrupts but it easy to do it this way.

	SERINBUF *USART @ PERIPH_RPR + ! 		// Set up ser in ptr
	100 *USART @ PERIPH_RCR + !  			// Set up ser in cntr
	SERINBUF *USART @ PERIPH_RNPR + ! 		// Set up ser in next ptr
	100 *USART @ PERIPH_RNCR + !  			// Set up ser in next cntr
	0 SERININDEX !					// set the pointer to zero
	1 *USART @ PERIPH_PTCR + !			// enable dma for recieve
	[ ' BUFFTSIN LITERAL ] *TIMEDSIN !		// point *TIMEDSIN to our new version of TSIN

The setup code simply writes the correct values into the pointer and counter registers, zeroes the index, enabled the transfer then redirects *TIMEDSIN to point to my new input code.

All in all it was a lot easier than I expected.

Created by eddie. Last Modification: Thursday 03 of April, 2008 18:23:56 AEDT by eddie.

Main Index

Switch Theme


System Administrator, 13:56 AEST, Sun 07 of Aug, 2016: upgrading to tiki 15.2
eddie, 19:31 AEST, Sat 06 of Aug, 2016: This website is now served from Brisbane - in a Binarylane cloud.
System Administrator, 16:07 AEDT, Wed 06 of Mar, 2013: More f'n spam. No more anon posting from now on.
Anonymous, 18:44 AEDT, Mon 04 of Mar, 2013: deleted
Anonymous, 08:36 AEDT, Sun 03 of Mar, 2013: Very nice site!

Last-Visited Pages

Online Users

41 online users