RISC-V/pwm/dma_gpio.c
2024-03-01 14:14:21 +01:00

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" );
}
}