学习沁恒微CH32V307VCT6开发板---SPI外设 - RISC-V MCU技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

学习沁恒微CH32V307VCT6开发板---SPI外设

这两天准备学习学习沁恒微CH32V307VCT6的SPI外设。以前用过ARM架构单片机的SPI,虽然架构不同,但相信在用法上和开发上应该区别不大。根据应用手册SPI章节,需要使用端口复用机能。在半双工的例程中,使用的GPIO口为A5和A7,在应用手册119页有如下描述:

图片1.png

也就是说,例程中使用PA5作为SPI的SCK用,输出时钟;PA7作为MOSI使用,输出数据。例程中SPI1的初始化处理如下所示(只保留主机模式下的代码,从机模式的已经删除):

void SPI_1Lines_HalfDuplex_Init(void) {

	GPIO_InitTypeDef GPIO_InitStructure={0};

	SPI_InitTypeDef SPI_InitStructure={0};

	NVIC_InitTypeDef NVIC_InitStructure={0};

    

    // 配置时钟总线

	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );

 

    // 设置GPIO口,使用PA5(SCK),PA7(MOSI)

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

	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_7;

	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF_PP ;

	GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz ;

	GPIO_Init( GPIOA, &GPIO_InitStructure );

    

    // 向外设发数据

	SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;

	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    

    // 数据位数(为了方便测试,程序中我改成8位)

	SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;

	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;   // 时钟平时为低

	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;  // 第一个脉冲沿数据有效

	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;      // 片选使用软件模式

	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;

	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位数据在前

	SPI_InitStructure.SPI_CRCPolynomial = 7;

	SPI_Init( SPI1, &SPI_InitStructure );

 

	// 配置SPI中断

NVIC_InitStructure.NVIC_IRQChannel =  SPI1_IRQn ;

	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

	NVIC_InitStructure.NVIC_IRQChannelCmd =  ENABLE ;

	NVIC_Init(&NVIC_InitStructure);

 

	SPI_Cmd( SPI1, *ENABLE* );

}

主程序:

int main(void) {

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

	SystemCoreClockUpdate();

	Delay_Init();

	USART_Printf_Init(115200);	

	printf("SystemClk:%d\\r\\n",SystemCoreClock);

	printf( "ChipID:%08x\\r\\n", DBGMCU_GetCHIPID() );

    

    // 初始化SPI

	SPI_1Lines_HalfDuplex_Init();

 

	printf("HOST Mode\\r\\n");

	Delay_Ms(2000);

    // 允许中断

	SPI_I2S_ITConfig( SPI1, SPI_I2S_IT_TXE , ENABLE );

 

	while(1) {

        // 等待发送完成?

		while( Txval<18 );

        

        // 发送缓冲寄存器是否空?空表示可以发送或者之前的发送已经完成

		while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET ) {

             // 设备忙?

			if(SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_BSY ) == RESET) {

                 // 不忙,禁用SPI

				SPI_Cmd( SPI1, DISABLE );

				while(1);

			}

		}

	}

}

中断处理:

void  SPI1_IRQHandler ( void ) {

 if ( SPI_I2S_GetITStatus( SPI1, SPI_I2S_IT_TXE ) != RESET ) {

      // 如果是SPI中断,发送下一组数据

		SPI_I2S_SendData( SPI1, TxData[Txval++] );

		 if ( Txval == 18 ) {

             // 发完18组数据后,禁止SPI发送结束中断

			SPI_I2S_ITConfig( SPI1, SPI_I2S_IT_TXE , DISABLE );

		}

	}

}

以上是例程中,主机模式下的主要代码部分,我不是很懂,注释是根据含有的英文缩写猜测的。

SPI1的中断状态寄存器:
图片2.png

SPI_I2S_FLAG_TXE=0x0002,对应TXE-发送缓冲区空标志位,执行SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ),就是为了获取发送缓冲区空标志位。如果是RESET值,表示非空,前此数据爱没有发送完。

SPI_I2S_FLAG_BSY=0x0080,对应BSY-忙标志位,执行SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_BSY )是用来判断SPI是否处于忙的状态。如果是RESET值,表示不在通讯状态,可以使用。

为了验证数据是否发送出去,我用74HC164以级联方式做了一个接收用的模块,主要是为了通过LED观察传出来的数据是什么样。

图片3.png

测试时,先直接看运行结果,看看接收模块的LED显示是否有变化,以证明接线又没有错误。

以下是修改后的主程序:

u32 i=0;

   

   // 初始化中断

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

	SystemCoreClockUpdate();

	Delay_Init();

 

// 初始化串口 

	USART_Printf_Init(115200);	

	 printf ("SystemClk:%d\\r\\n",SystemCoreClock);

	 printf ( "ChipID:%08x\\r\\n", DBGMCU_GetCHIPID() );

 

// 初始化SPI外设

	SPI_1Lines_HalfDuplex_Init();

 

	Delay_Ms(2000);

	SPI_I2S_ITConfig( SPI1, SPI_I2S_IT_TXE , ENABLE );

 

	GPIO_LED_INIT();

 

	 while (1)

	{

		 while ( Txval<3 );

 

		 while (1) {

			i++;

			 **printf** ("i=%d\\r\\n", i);

			TxData[0]=255-((i & 0xff0000)>>16);

			TxData[1]=255-((i & 0x00ff00)>>8);

			TxData[2]=255-(i & 0x0000ff);

			// 开始传输

			GPIO_WriteBit(GPIOA, GPIO_Pin_1, 0);

 

			// 启动传输

			Txval=0;

			SPI_I2S_ITConfig( SPI1, SPI_I2S_IT_TXE , ENABLE );

 

			// 等待数据都发送完

			 while ( Txval<3 );

 

			// 开始传输

			GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1);

			Delay_Ms(300);

		}

 

	}

}

处理中通过周期改变输出到SPI1外设的数据,在那个扩展板上可以看到二进制加法产生LED规律亮灭。

调试过程中,发现一个很奇怪的现象,如果扩展板使用5V电源,LED现实的也有规律,但已经不是二进制加法。可能是由于接口电平产生的问题。

还有一点,现在程序使用了中断方式,来发送数据。如果不使用中断,而在程序中直接直接发送,可以使用下面的处理:

// 以非中断方式发送 正常 begin

		 while (1) {

			i++;

			 printf ("i=%d\\r\\n", i);

			TxData[0]=255-((i & 0xff0000)>>16);

			TxData[1]=255-((i & 0x00ff00)>>8);

			TxData[2]=255-(i & 0x0000ff);

 

			GPIO_WriteBit(GPIOA, GPIO_Pin_1, 0);

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ==  RESET ) ; 	// 等空

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) !=  RESET ) ; 	// 等不忙

			SPI_I2S_SendData( SPI1, TxData[0]);

 

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ==  RESET ) ; 	// 等空

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) !=  RESET ) ; 	// 等不忙

			SPI_I2S_SendData( SPI1, TxData[1]);

 

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ==  *RESET* ) ; 	// 等空

			 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) !=  *RESET* ) ; 	// 等不忙

			SPI_I2S_SendData( SPI1, TxData[2]);

 

			GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1);

 

			Delay_Ms(1000);

		}

		// 以非中断方式发送 正常 end

刚开始用eclipse编写、编译、下载硬件开发用的C语言程序,还不熟,一点点摸索中。开始总是出一些编译错误,代码本身肯定没有任何语法错误,也不明白是啥情况

。害得我一遍又一遍重新导入程序。debug模式也启动不起来。后来就索性下载,靠log输出来分析原因了。

demo

更多回帖

×
发帖