GD32F405RGT6-SPI主从模式代码

GD32F405RGT6-SPI主从模式代码

唔,其实好难受,对于我们这种有着轻微强迫症的人来说只能利用下班以及周末的空闲时间对GD的外设撸代码。。。。一个周末加今天的下班时间,撸到了GD的SPI外设。在我的印象中对于SPI通信最直观的想法就是“一问一答”。不论是主从双方,要想询问对方都得发东西过去,然后同时接受自己想要的。

咳咳,那也顺便聊一聊SPI通信吧。有兴趣的个人觉的还是自己手写一遍SPI的代码弄懂时序,结合逻辑分析仪应该上手是很快的。(个人观点)。

1.介绍

SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO。用于 CPU与各种外围器件进行全双工、同步串行通讯。

2.SPI主要特点

SPI主要特点有:可以同时发出和接收串行数据,可以当作主机或从机工作,提供频率可编程时钟,发送结束中断标志,写冲突保护,总线竞争保护等。

3.SPI的工作方式

SPI总线有四种工作方式(SP0、SP1、SP2、SP3),其中使用的最为广泛的是 SPI0 和 SPI3 方式。SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。

如果CPOL = 0,串行同步时钟的空闲状态为低电平。如果CPOL = 1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA = 0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样。如果CPHA = 1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。

SPI主模块和与之通信的外设音时钟相位和极性应该一致。

原文链接:https://blog.csdn.net/zhengqijun_/article/details/52550855

二、SPI时序详解*

1.SPI时序图

SPI接口在模式0下输出第一位数据的时刻SPI接口有四种不同的数据传输时序,取决于CPOL 和 CPHL这两位的组合。图1表现了这四种时序,时序与CPOL、CPHL的关系也可以从图1中看出。

在这里插入图片描述
CPOL 是用来决定 SCK 时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。CPHA 是用来决定采样时刻的,CPHA = 0,在每个周期的第一个时钟沿采样,CPHA = 1,在每个周期的第二个时钟沿采样。由于器件工作在模式0这种时序(CPOL = 0,CPHA = 0),所以将图1简化为图2,只关注模式0的时序。
在这里插入图片描述
SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),在时钟的后沿输出数据(下降沿,第二个时钟沿)。

首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,在时钟的前沿被从器件采样,那主器件是在何时刻输出 bit1 的呢?

bit1 的输出时刻实际上在 SCK 信号有效以前,比 SCK 的上升沿还要早半个时钟周期。bit1 的输出时刻与 SSEL 信号没有关系。

再来看从器件,主器件的输入口(MISO)同样是在时钟的前沿采样从器件输出的 bit1 的,那从器件又是在何时刻输出 bit1 的呢?

从器件是在SSEL信号有效后,立即输出 bit1,尽管此时SCK信号还没有起效。
在这里插入图片描述
从这张图就可以很清楚的看出主从器件的bit1 是怎样输出的。
原文链接https://blog.csdn.net/zhengqijun_/article/details/52550855

上代码(主机模式):

/*
************************************************************
*
*	notes:	
*	(1):PC1--MOSI
*	(2):PC10--SCK
*	(3):PC11--MISO
*	(4):PA4--NSS
************************************************************
*/
void SPI2_Init(void)
{
	spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_SPI2);
	
	 /* SPI2_CLK(PC10), SPI2_MISO(PC11), SPI2_MOSI(PC1)*/
    gpio_af_set(GPIOC, GPIO_AF_5, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
	

    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;				
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;		
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;			
    spi_init_struct.prescale             = SPI_PSC_32;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI2, &spi_init_struct);
	
	spi_enable(SPI2);		
	
}


//发送半字
uint16_t spi_flash_send_halfword(uint16_t half_word)
{
    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));

    spi_i2s_data_transmit(SPI2,half_word);				

    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_RBNE));

    return spi_i2s_data_receive(SPI2);					
}



//发送一个字节
uint8_t spi_flash_send_byte(uint8_t byte)
{
 
    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));

    spi_i2s_data_transmit(SPI2,byte);					

    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_RBNE));

    return(spi_i2s_data_receive(SPI2));					
}

上代码(从机模式)

/*
************************************************************	
*	(1):PC1--MOSI
*	(2):PC10--SCK
*	(3):PC11--MISO
*	(4):PA4--NSS
************************************************************
*/
void SPI2_Init(void)
{
	spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_SPI2);
	
	 /* SPI2_CLK(PC10), SPI2_MISO(PC11), SPI2_MOSI(PC1)*/
    gpio_af_set(GPIOC, GPIO_AF_5, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_1|GPIO_PIN_10| GPIO_PIN_11);
	
	
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_SLAVE;				
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;		
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;			
    spi_init_struct.prescale             = SPI_PSC_32;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI2, &spi_init_struct);
	
	spi_enable(SPI2);		
	
	spi_i2s_interrupt_enable(SPI2,SPI_I2S_INT_RBNE);	
	
	nvic_irq_enable(SPI2_IRQn, 0, 2); 
	
}


//发送半字
uint16_t spi_flash_send_halfword(uint16_t half_word)
{
    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));

    spi_i2s_data_transmit(SPI2,half_word);				

    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_RBNE));

    return spi_i2s_data_receive(SPI2);					
}



//发送一个字节
uint8_t spi_flash_send_byte(uint8_t byte)
{
 
    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));
    spi_i2s_data_transmit(SPI2,byte);					

    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_RBNE));

    return(spi_i2s_data_receive(SPI2));					
}


//中断服务函数
void SPI2_IRQHandler(void)
{
	u8 spi_resive = 0;
	
	if(spi_i2s_interrupt_flag_get(SPI2, SPI_I2S_INT_FLAG_RBNE) != RESET)
	{
		  while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_RBNE));
			spi_resive = spi_i2s_data_receive(SPI2);					
		
		  while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));
			spi_i2s_data_transmit(SPI2,spi_resive);					
		
			SPI_DATA(SPI2);	
	}
}

注意在SPI的中断函数这一块,它的库并没有提供清除中断标志的操作,我们就可以通过查看手册知道:
在这里插入图片描述
可通过读SPI 的数据寄存器清除该标志:SPI_DATA(SPI2);

附上完整的工程代码:
(1)主机模式:https://download.csdn.net/download/tiange1996/84847574

(2)从机模式:
https://download.csdn.net/download/tiange1996/84847782

  • 4
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: gd32f405是一款具有DMA功能的微控制器,可以通过串口与外部设备进行数据通信。串口通信是一种常用的数据传输方式,它通过数据线来传输数据,并且具有较好的适应性和广泛的应用场景。 gd32f405支持使用DMA(直接内存访问)来处理串口数据传输。DMA是一种硬件模块,可以在不需要CPU干预的情况下,直接从内存读取数据并将其传输到外部设备,或将外部设备的数据传输到内存。这样可以减轻CPU的负担,提高数据传输效率。 使用DMA进行串口数据传输有以下几个步骤: 1. 配置DMA通道:首先需要配置DMA通道用于串口数据传输的接口。在gd32f405中,可以使用多个DMA通道进行不同功能的数据传输。 2. 配置串口:需要配置串口相关参数,包括波特率、数据位数、校验位等。gd32f405的串口模块具有多个发送和接收缓冲区,可以通过调用相应的函数对串口进行配置。 3. 配置DMA传输方向和长度:根据具体需求,需要设置DMA的传输方向,即是从内存到外设还是从外设到内存。同时需要设置数据传输的长度。 4. 启动DMA传输:配置完成后,通过调用相应的函数启动DMA传输。DMA将自动从内存读取数据并进行串口传输,或从串口接收数据并写入内存,无需CPU干预。 通过使用DMA进行串口数据传输,可大大提高数据传输效率,减少CPU的负担。gd32f405的DMA功能使得串口通信更加可靠和高效。 ### 回答2: gd32f405是一款单片机芯片,而串口和DMA是其提供的两种功能。 串口是一种用于数据传输的接口,通过串行通信的方式可以实现将数据从一个设备传输到另一个设备。gd32f405内置了多个串口通道,可以通过配置相关寄存器和使用相应的引脚来实现与其他设备的串口通信。 而DMA(Direct Memory Access)是一种直接内存访问的技术,可以在不经过CPU的情况下,直接将数据从外设读取到内存或从内存写入到外设。在gd32f405中,通过配置DMA通道的相关寄存器,可以实现串口数据的DMA传输。 具体实现串口-DMA的步骤如下: 1. 配置串口:包括波特率、数据位、停止位、校验位等参数。 2. 配置DMA通道:包括传输方向、数据宽度、传输大小等参数。 3. 配置内存地址和外设地址:分别指定DMA传输的起始地址和目标地址。 4. 使能串口的DMA模式和DMA通道。 5. 当需要进行数据传输时,触发DMA传输请求。 6. 当DMA传输完成后,通过相应的中断或标志位进行处理。 通过串口-DMA的方式,可以实现高效的数据传输,减轻CPU负担。此外,gd32f405还支持多个DMA通道,可以同时进行多路数据传输,提高系统的数据处理效率。 总结来说,gd32f405芯片提供了串口和DMA功能,可以通过配置相关寄存器和引脚,实现串口数据的高效传输,并且支持多通道的DMA操作,提高系统的性能和效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修才生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值