150 lines
4.6 KiB
C
150 lines
4.6 KiB
C
// DMA GPIO Output Example - this example shows
|
|
// how you can output 8 pins all simultaneously
|
|
// with a planned bit pattern at 4MSamples/s.
|
|
//
|
|
// It outputs a pattern of repeating 010101 and
|
|
// 00000 alternating "frames".
|
|
//
|
|
// The interrupt fires once at the beginning and
|
|
// once at the end.
|
|
//
|
|
|
|
#include "ch32v003fun.h"
|
|
#include <stdio.h>
|
|
|
|
volatile uint32_t count;
|
|
|
|
#define MBSAMPS 1024
|
|
uint8_t memory_buffer[1024];
|
|
|
|
void DMA1_Channel2_IRQHandler( void ) __attribute__((interrupt)) __attribute__((section(".srodata")));
|
|
void DMA1_Channel2_IRQHandler( void )
|
|
{
|
|
int i;
|
|
static int frameno;
|
|
volatile int intfr = DMA1->INTFR;
|
|
do
|
|
{
|
|
DMA1->INTFCR = DMA1_IT_GL2;
|
|
|
|
// Gets called at the end-of-a frame.
|
|
if( intfr & DMA1_IT_TC2 )
|
|
{
|
|
uint32_t fv = (frameno&1)?0:0xaa55aa55;
|
|
uint32_t * mbb = (uint32_t*)( memory_buffer + MBSAMPS/2 );
|
|
for( i = 0; i < MBSAMPS/8; i++ )
|
|
{
|
|
mbb[i] = fv; // Fill in the frame data
|
|
}
|
|
frameno++;
|
|
}
|
|
|
|
// Gets called halfway through the frame
|
|
if( intfr & DMA1_IT_HT2 )
|
|
{
|
|
uint32_t fv = (frameno&1)?0:0xaa55aa55;
|
|
uint32_t * mbb = (uint32_t*)( memory_buffer );
|
|
for( i = 0; i < MBSAMPS/8; i++ )
|
|
{
|
|
mbb[i] = fv; // Fill in the frame data.
|
|
}
|
|
}
|
|
intfr = DMA1->INTFR;
|
|
} while( intfr );
|
|
}
|
|
|
|
int main()
|
|
{
|
|
int i;
|
|
|
|
SystemInit();
|
|
|
|
// Reset all the peripherals we care about.
|
|
RCC->APB2PRSTR = 0xffffffff;
|
|
RCC->APB2PRSTR = 0;
|
|
|
|
// Enable DMA
|
|
RCC->AHBPCENR = RCC_AHBPeriph_SRAM | RCC_AHBPeriph_DMA1;
|
|
|
|
// Enable GPIO and Timer 1
|
|
RCC->APB2PCENR = RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA;
|
|
|
|
// GPIO D0/D4 Push-Pull (LED)
|
|
GPIOD->CFGLR =
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*1) | // PD1 = SWIO (so we don't go off-bus)
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*2) | // PD2 = T1CH1
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
|
|
|
|
// GPIO C All output.
|
|
GPIOC->CFGLR =
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*1) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*2) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*5) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*6) |
|
|
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*7);
|
|
|
|
|
|
// Fill in the plan of what we will be sending out.
|
|
for( i = 0; i < sizeof(memory_buffer) / sizeof(memory_buffer[0]); i++ )
|
|
{
|
|
memory_buffer[i] = (i&1)?0xaa:0x55;
|
|
}
|
|
|
|
// DMA2 can be configured to attach to T1CH1
|
|
// The system can only DMA out at ~2.2MSPS. 2MHz is stable.
|
|
DMA1_Channel2->CNTR = sizeof(memory_buffer) / sizeof(memory_buffer[0]);
|
|
DMA1_Channel2->MADDR = (uint32_t)memory_buffer;
|
|
DMA1_Channel2->PADDR = (uint32_t)&GPIOC->OUTDR;
|
|
DMA1_Channel2->CFGR =
|
|
DMA_CFGR1_DIR | // MEM2PERIPHERAL
|
|
DMA_CFGR1_PL | // High priority.
|
|
0 | // 8-bit memory
|
|
0 | // 8-bit peripheral
|
|
DMA_CFGR1_MINC | // Increase memory.
|
|
DMA_CFGR1_CIRC | // Circular mode.
|
|
DMA_CFGR1_HTIE | // Half-trigger
|
|
DMA_CFGR1_TCIE | // Whole-trigger
|
|
DMA_CFGR1_EN; // Enable
|
|
|
|
NVIC_EnableIRQ( DMA1_Channel2_IRQn );
|
|
DMA1_Channel2->CFGR |= DMA_CFGR1_EN;
|
|
|
|
// NOTE: You can also hook up DMA1 Channel 3 to T1C2,
|
|
// if you want to output to multiple IO ports at
|
|
// at the same time. Just be aware you have to offset
|
|
// the time they read at by at least 1/8Mth of a second.
|
|
|
|
// Setup Timer1.
|
|
RCC->APB2PRSTR = RCC_APB2Periph_TIM1; // Reset Timer
|
|
RCC->APB2PRSTR = 0;
|
|
|
|
// Timer 1 setup.
|
|
TIM1->PSC = 0x0000; // Prescaler
|
|
TIM1->ATRLR = 11; // Auto Reload - sets period (48MHz / (11+1) = 4MHz)
|
|
TIM1->SWEVGR = TIM_UG | TIM_TG; // Reload immediately + Trigger DMA
|
|
TIM1->CCER = TIM_CC1E | TIM_CC1P; // Enable CH1 output, positive pol
|
|
TIM1->CHCTLR1 = TIM_OC1M_2 | TIM_OC1M_1; // CH1 Mode is output, PWM1 (CC1S = 00, OC1M = 110)
|
|
TIM1->CH1CVR = 6; // Set the Capture Compare Register value to 50% initially
|
|
TIM1->BDTR = TIM_MOE; // Enable TIM1 outputs
|
|
TIM1->CTLR1 = TIM_CEN; // Enable TIM1
|
|
TIM1->DMAINTENR = TIM_UDE | TIM_CC1DE; // Trigger DMA on TC match 1 (DMA Ch2) and TC match 2 (DMA Ch3)
|
|
|
|
// Just debug stuff.
|
|
printf( "Setup OK\n" );
|
|
|
|
while(1)
|
|
{
|
|
GPIOD->BSHR = 1 | (1<<4); // Turn on GPIOs
|
|
printf( "%lu\n", GPIOD->OUTDR );
|
|
Delay_Ms( 250 );
|
|
GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIODs
|
|
printf( "%lu\n", GPIOD->OUTDR );
|
|
Delay_Ms( 250 );
|
|
printf( "Step\n" );
|
|
}
|
|
}
|
|
|