GD32F103配置SPI+DMA收发数据

  GD32是国产的32位单片机,它和STM32非常非常像,就连以前的库函数都差不多(现在从GD官网下载的库函数换了一种风格)。配置SPI和DMA的方法和STM32差不多。

DMA0各通道请求表  上图是DMA0各通道请求表,SPI0的收发分别用了通道1和通道2,我们就配置SPI0。

配置SPI0(或者说SPI1,GD的命名顺序让人闹心)

#define SPI1_CS_HIGH() (GPIOA->BOR = GPIO_PIN_4)
#define SPI1_CS_LOW()  (GPIOA->BCR = GPIO_PIN_4)
void SPI1Init(void)
{
	GPIO_InitPara GPIO_InitStructure;
	SPI_InitPara SPI_InitStructure;
	
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_SPI0);
	//GD32的外设命名顺序很烦人,它的手册上都是从0开始数的,到了代码里,使能时钟这里是从0数,到后面又是从1数了
	/* SPI1_SCK(PA5), SPI1_MISO(PA6) and SPI1_MOSI(PA7) GPIO pin configuration */
	GPIO_InitStructure.GPIO_Pin = GPIO_PIN_5 | GPIO_PIN_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	GPIO_InitStructure.GPIO_Pin = GPIO_PIN_6;
	GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	/* SPI1_CS(PA4) GPIO pin configuration */
	GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	/* chip select invalid*/
	SPI1_CS_HIGH();
	/* SPI1 parameter config */
	SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_FULLDUPLEX;
	SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
	SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;
	SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;
	SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_1EDGE;
	SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;
	SPI_InitStructure.SPI_PSC = SPI_PSC_4;
	SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
	SPI_InitStructure.SPI_CRCPOL = 7;
	SPI_Init(SPI1, &SPI_InitStructure);//这里变成的SPI1了,没有SPI0
	/* enable SPI1 */
	SPI_Enable(SPI1, ENABLE);
}

unsigned char SPI1ReadWriteByte(unsigned char byte)
{
	/* loop while data register in not emplty */
	while (RESET == SPI_I2S_GetBitState(SPI1,SPI_FLAG_TBE));
	/* send byte through the SPI0 peripheral */
	SPI1->DTR = byte;
	/* wait to receive a byte */
	while(RESET == SPI_I2S_GetBitState(SPI1,SPI_FLAG_RBNE));
	/* return the byte read from the SPI bus */
	return (SPI1->DTR);
}

配置DMA0

void SPI1DMAInit(void)	//DMA0 (count from 0) Channel3 (count from 1)
{
	DMA_InitPara DMA_InitStructure;
//	NVIC_InitPara NVIC_InitParaStruct;	//没用上中断
	
	rcu_periph_clock_enable(RCU_DMA0);
	//Tx_DMA
	DMA_DeInit(DMA1_CHANNEL3);	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned int)&(SPI1->DTR);
	DMA_InitStructure.DMA_MemoryBaseAddr = 0;	//内存地址待定,启动传输时配置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERALDST;
	DMA_InitStructure.DMA_BufferSize = 0;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERALINC_DISABLE;
	DMA_InitStructure.DMA_MemoryInc = DMA_MEMORYINC_ENABLE;	//收发通道都配成内存地址递增就行
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPHERALDATASIZE_BYTE;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MEMORYDATASIZE_BYTE;
	DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;
	DMA_InitStructure.DMA_Priority = DMA_PRIORITY_MEDIUM;
	DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;
	
//	NVIC_InitParaStruct.NVIC_IRQ = DMA1_Channel3_IRQn;
//	NVIC_InitParaStruct.NVIC_IRQPreemptPriority = 0;
//	NVIC_InitParaStruct.NVIC_IRQSubPriority = 2;
//	NVIC_InitParaStruct.NVIC_IRQEnable = ENABLE;
//	NVIC_Init(&NVIC_InitParaStruct);
//	NVIC_EnableIRQ(DMA1_Channel3_IRQn);
	
	DMA_Init(DMA1_CHANNEL3, &DMA_InitStructure);
	DMA_ClearIntBitState(DMA1_INT_TC3);
	//DMA_INTConfig(DMA1_CHANNEL3, DMA_INT_TC, ENABLE);
	
	//Rx_DMA
	DMA_DeInit(DMA1_CHANNEL2);
	DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERALSRC;
	DMA_InitStructure.DMA_BufferSize = 0;
	DMA_InitStructure.DMA_MemoryInc = DMA_MEMORYINC_ENABLE;
	
//	NVIC_InitParaStruct.NVIC_IRQ = DMA1_Channel2_IRQn;
//	NVIC_InitParaStruct.NVIC_IRQPreemptPriority = 0;
//	NVIC_InitParaStruct.NVIC_IRQSubPriority = 2;
//	NVIC_InitParaStruct.NVIC_IRQEnable = ENABLE;
//	NVIC_Init(&NVIC_InitParaStruct);
//	NVIC_EnableIRQ(DMA1_Channel2_IRQn);
	
	DMA_Init(DMA1_CHANNEL2, &DMA_InitStructure);
	DMA_ClearIntBitState(DMA1_INT_TC2);
	//DMA_INTConfig(DMA1_CHANNEL2, DMA_INT_TC, ENABLE);
}

void SPI1TxDMAEnable(unsigned int mem_addr, unsigned int buf_size)
{
	//tx
	DMA_Enable(DMA1_CHANNEL3, DISABLE);
	while(DMA_GetCmdStatus(DMA1_CHANNEL3));
	DMA_SetCurrDataCounter(DMA1_CHANNEL3, buf_size);	//设置传输数据字节数
	DMA_MemoryTargetConfig(DMA1_CHANNEL3, mem_addr);	//设置内存地址
	SPI_I2S_DMA_Enable(SPI1, SPI_I2S_DMA_TX, ENABLE);
	//rx
	DMA_Enable(DMA1_CHANNEL2, DISABLE);
	while(DMA_GetCmdStatus(DMA1_CHANNEL2));
	DMA_SetCurrDataCounter(DMA1_CHANNEL2, buf_size);
	DMA_MemoryTargetConfig(DMA1_CHANNEL3, mem_addr);	//接收通道的内存地址和发送通道设置成一样的,之前的数据被覆盖前已经通过发送通道发出去了
	SPI_I2S_DMA_Enable(SPI1, SPI_I2S_DMA_RX, ENABLE);
	//enable
	DMA1_CHANNEL2->CTLR |= DMA_CTLR_CHEN;	//DMA Enable
	DMA1_CHANNEL3->CTLR |= DMA_CTLR_CHEN;	//DMA Enable
	while(!DMA_GetIntBitState(DMA1_INT_TC2));
	DMA_ClearIntBitState(DMA1_INT_TC2);
	while(!DMA_GetIntBitState(DMA1_INT_TC3));
	DMA_ClearIntBitState(DMA1_INT_TC3);
}

  上面代码中函数SPI1TxDMAEnable(unsigned int mem_addr, unsigned int buf_size)用来启动一次DMA传输,RX通道和TX通道的内存地址配成同一个就行,因为SPI的数据收发是在一个时钟控制下同时进行,内存的变量被读入的数据覆盖之前已经被发送出去了,不用担心内存数据被覆盖而发生错误。

版权声明:本文为CSDN博主「Chenxr32」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/QDchenxr/article/details/119813261

生成海报
点赞 0

Chenxr32

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

如何在MCU上通过ToD+PPS 获取同步时间(一)

欢迎淘宝搜索飞灵科技,我司相关新产品陆续上线 在很多工业使用场景中,网络内一些设备终端与设备终端,或设备终端与主控之间需要进行时间同步。 比如给传感器数据打时间戳,比如总线的时分复用等场景

rt_thread hc32f460开发四:pwm驱动移植

RT-Thread 驱动开发简介 RT-Thread驱动开发最开始应该是要阅读官方的文档,理解驱动的运行原理和使用方法。PWM部分的文档在这里https://www.rt-thread.org/document/site/#/rt

w25qxx spi flash WP写保护引脚的正确使用方式

WP引脚使用方式 spi flash有一个外部写保护引脚WP, 此引脚并不是直接保护flash上的数据内容,而是保护状态寄存器不被异常改写.flash上的内容写保护是通过状态寄存器的BPxbit的不同组合来实现的. 对于环境相对恶劣的使用场景