RF4463F30半双工模组,伪全双工透传方案(STM32平台)(第一章,环境搭建)

article/2025/9/11 9:17:02

RF4463F30半双工模组,伪全双工透传方案(STM32平台)(第一章,环境搭建)

  • 前言
  • 模块介绍
    • 基础信息
    • 参数配置
      • 通讯频段
      • 空中速率
    • 配置文件生成
      • 头文件修改处1
      • 头文件修改处2
      • 库函数修改处1
      • 库函数修改处2
        • 宏定义来源
    • 例程简述
      • 关键函数
        • sdn_reset //模块复位函数
        • SI4463_init //模块参数配置函数
        • tx_data //发送数据函数
        • rx_init //接受状态初始化函数
        • test_Receive //接受数据函数(源例程是一段代码块,我取出来并稍微修改了一下)
      • 关键参数/变量
        • tx_ph_data //发送数据缓存
        • payload_length //通讯包长度
      • 其余部分
  • 平台对接
    • 第一步-IO口对应
    • 第二步-启用SPI(如果是软件SPI则忽略这一步)
    • 第三步-自定义测试函数
    • 第四步调试模组
  • 结语

前言

经过长期的测试,我惊奇的发现,e70模块在传输距离和质量上多么的不足(心疼我研究的时间,下次一定先测距离),于是我转移了重心到另一组模块上RF4463F30(思为无线),这同样是半双工模块,然后通讯质量却让我震惊,首先是速度,其次是距离,再三是稳定性,最后是可调节性。价格相近,性能优越,再加上充分的技术支持,OMG,买它!!!。
虽然后期官方给我提供了stm32的例程,但是前期我用的官方提供的vc的例程,因此给了我较大的发挥空间和理解工程的机会。本文主要讲解如何将vc的例程转为stm32的例程。

模块介绍

以下信息来自模块的介绍手册,想详细了解的请去官网下载。
链接:官网4463页面

基础信息

首先我放出这个模块的基础工作外围电路
(作为软件开发,我最关心的的就是可操作的IO口了):
在这里插入图片描述
从上图来看,主要的控制IO口一共是7个,这7个IO具体的功能如下:
在这里插入图片描述
其中的SDI,SDO,SCK接入单片机时可以直接配置外设的SPI,nIRQ作为中断信号脚需配置为下拉输入(建议再配置外部中断),
SDN 作为芯片开关,对接单片机时应采用推挽输出,nSEL 同样是推挽输出。

接着附上引脚图:
在这里插入图片描述
最后贴上手册里描述的产品特点:
在这里插入图片描述
这些我觉得跟软件开发没什么关系,我就不介绍了。

参数配置

这个模块的参数可以手动修改初始化程序里的spi写入部分,也可以直接使用厂家提供WDS配置软件,详细操作我不描述了,请购买后自行参照厂家提供的操作手册,我重点说一下几个核心:

通讯频段

在这里插入图片描述
实际通讯频率是:主频率+偏移频率*倍数。 请确保通讯的模块工作频率相同(频率越高速度越快,通讯距离约近,频率越低速度约慢,通讯距离和抗干扰性越强)。具体选择什么频率请根据实际工作需求进行抉择。

空中速率

在这里插入图片描述
空速和频率决定模块的实际通讯速度。 这一部分我只建议修改这两处,其余还是选择默认值为好,其中根据官方技术支持的建议:当空速低于19200时,频率填5khz,当空速高于19200时,频率填10khz,平时建议空速低于38400,如果高于38400则建议提高频率到15khz。 我追求高速传输因此选择了空速是115200,频率为20khz。

配置文件生成

在这里插入图片描述
点击这个按键,然后选择第二项:保存头文件。
然后你就可以获得一个头文件:radio_config_Si4463.h
注意,这个头文件不能直接替换,一共有4处修改地方,两处是在头文件里,两处是在例程里。

头文件修改处1

在这里插入图片描述
这一处要注释掉,很好理解吧。

头文件修改处2

在这里插入图片描述
很关键的修改,千万不能忘记,否则将直接导致模块发烫甚至损毁(我前期操作不当,损失了一块stm32)

库函数修改处1

在这里插入图片描述
我也不知道这是什么,反正就是这一处,原来是8,生成的文件里是10

库函数修改处2

在这里插入图片描述

宏定义来源

同理,另外这个high_speed 是我自定义的,原理如下:
在这里插入图片描述
其中_n结尾的是原始的头文件,另一个是我自己生成的头文件我区分了名字都放在一个目录下(能看明白吧)

例程简述

下文将对官方提供的例程做一个简单注解,并重点描述关键函数关键参数

关键函数

sdn_reset //模块复位函数

void sdn_reset(void)
{	U8 i;i = 0;while(i!=0xff)i = check_cts();				//新增优化SDN_1;delay_1ms(2);SDN_0;delay_1ms(10);nSEL_1;   SCK_0;nSEL_0;for (i = 0; i< 7; i++)spi_byte(RF_POWER_UP_data[i]);  nSEL_1;delay_1ms(20);
}	

sdn_reset 这个函数是用于复位4463模块的其中有一个关键点就是这部分代码:(注释为新增优化的部分)

while(i!=0xff)i = check_cts();				//新增优化

这块代码是用于让spi通讯纯净化的,可以防止残留的错误spi数据影响对模块的读写。在例程里对spi操作的所有地方都有这个代码块(我获得的例程里在这一部分没有这个代码块,请自行核对检查)。4463模块有一个特点如果spi初始化参数没有写入成功(一段对IO口电平配置的程序写入失败),按着原始的参数进行工作,那么模块就会发烫。这里也是一处很重要的操作。(原始例程里少了这一块,我恰好环境里噪声多,因此初始化失败,持续发烫导致我损耗一块元器件)

SI4463_init //模块参数配置函数

void SI4463_init(void)
{	U8 app_command_buf[20];//spi_write(0x07, RF_GPIO_PIN_CFG_data);   app_command_buf[0] = 0x13;			// SET GPIO PORTapp_command_buf[1]  = 0x21; 		// gpio 0 ,Rx data//0app_command_buf[2]  = 0x20;    		// gpio1, output 0//0app_command_buf[3]  = 0x21;  		// gpio2, hign while in receive modeapp_command_buf[4]  = 0x20; 		// gpio3, hign while in transmit mode app_command_buf[5]  = 0x27;   		// nIRQapp_command_buf[6]  = 0x0b;  		// sdospi_write(7, app_command_buf); // spi_write(0x05, RF_GLOBAL_XO_TUNE_1_data); app_command_buf[0] = 0x11;  app_command_buf[1]  = 0x00;    app_command_buf[2]  = 0x01;    app_command_buf[3]  = 0x00;  app_command_buf[4]  = 98;  			// freq  adjustmentspi_write(5, app_command_buf);// spi_write(0x05, RF_GLOBAL_CONFIG_1_data);app_command_buf[0]  = 0x11;  app_command_buf[1]  = 0x00;app_command_buf[2]  = 0x01; app_command_buf[3]  = 0x03; app_command_buf[4]  = 0x40;  		// tx = rx = 64 byte,PH,high performance modespi_write(5, app_command_buf); spi_write(0x08, RF_FRR_CTL_A_MODE_4_data);    // disable all fast response register// spi_write(0x0D, RF_PREAMBLE_TX_LENGTH_9_data); // set Preambleapp_command_buf[0]  = 0x11;  app_command_buf[1]  = 0x10;    app_command_buf[2]  = 0x09;    app_command_buf[3]  = 0x00;   app_command_buf[4]  = 0x08;							//  8 bytes Preambleapp_command_buf[5]  = 0x14;							//  detect 20 bitsapp_command_buf[6]  = 0x00;						app_command_buf[7]  = 0x0f;app_command_buf[8]  = 0x31;  						//  no manchest.1010.。。app_command_buf[9]  = 0x00;app_command_buf[10]  = 0x00;app_command_buf[11]  = 0x00;app_command_buf[12]  = 0x00;spi_write(13, app_command_buf);	//  RF_SYNC_CONFIG_5_data,							// set syncapp_command_buf[0]  = 0x11;  app_command_buf[1]  = 0x11;app_command_buf[2]  = 0x05;app_command_buf[3]  = 0x00;app_command_buf[4]  = 0x01;   						// no manchest , 2 bytesapp_command_buf[5]  = 0x2d;   						// sync byte3app_command_buf[6]  = 0xd4;							// sync byte2app_command_buf[7]  = 0x00;							// sync byte1app_command_buf[8]  = 0x00;							// sync byte0spi_write(9, app_command_buf);//  packet crc         app_command_buf[0]  = 0x11;  app_command_buf[1]  = 0x12; app_command_buf[2]  = 0x01; app_command_buf[3]  = 0x00;app_command_buf[4]  = 0x81;							// CRC = itu-c, enable crcspi_write(5, app_command_buf);  // packet   gernale configuration        app_command_buf[0] = 0x11;  app_command_buf[1]  = 0x12;app_command_buf[2]  = 0x01;app_command_buf[3]  = 0x06;app_command_buf[4]  = 0x02;							// CRC MSB, data MSBspi_write(5, app_command_buf);// spi_write(0x07, RF_PKT_LEN_3_data);   app_command_buf[0] = 0x11;  app_command_buf[1]  = 0x12;app_command_buf[2]  = 0x03;app_command_buf[3]  = 0x08;app_command_buf[4]  = 0x00;app_command_buf[5]  = 0x00;app_command_buf[6]  = 0x00;						 spi_write(7, app_command_buf);         app_command_buf[0] = 0x11;  app_command_buf[1]  = 0x12;app_command_buf[2]  = 0x0c;app_command_buf[3]  = 0x0d;app_command_buf[4]  = 0x00;app_command_buf[5]  = payload_length;app_command_buf[6]  = 0x04;app_command_buf[7]  = 0xaa;app_command_buf[8]  = 0x00;app_command_buf[9]  = 0x00;app_command_buf[10]  = 0x00;app_command_buf[11]  = 0x00;app_command_buf[12]  = 0x00; app_command_buf[13]  = 0x00;app_command_buf[14]  = 0x00;app_command_buf[15]  = 0x00;spi_write(16, app_command_buf);					// set length of Field 1 -- 4// spi_write(0x0C, RF_PKT_FIELD_4_LENGTH_12_8_8_data);app_command_buf[0] = 0x11;  app_command_buf[1]  = 0x12; app_command_buf[2]  = 0x08;app_command_buf[3]  = 0x19;app_command_buf[4]  = 0x00;app_command_buf[5]  = 0x00;app_command_buf[6]  = 0x00;app_command_buf[7]  = 0x00;app_command_buf[8]  = 0x00;app_command_buf[9]  = 0x00;app_command_buf[10]  = 0x00;app_command_buf[11]  = 0x00;spi_write(12, app_command_buf);spi_write(0x10, RF_MODEM_MOD_TYPE_12_data);        spi_write(0x05, RF_MODEM_FREQ_DEV_0_1_data);spi_write(0x10, RF_MODEM_TX_RAMP_DELAY_12_data);    	spi_write(0x10, BCR_NCO_OFFSET_2_12_data);spi_write(0x10, RF_MODEM_TX_RAMP_DELAY_12_data);			spi_write(0x07, RF_MODEM_AFC_LIMITER_1_3_data);	//spi_write(0x10, BCR_NCO_OFFSET_2_12_data);spi_write(0x05, RF_MODEM_AGC_CONTROL_1_data);		spi_write(0x10, AGC_WINDOW_SIZE_12_data);	#ifdef high_speedspi_write(0x0E, RF_MODEM_RAW_CONTROL_10_data);#elsespi_write(0x0c, RF_MODEM_RAW_CONTROL_8_data);#endif//	spi_write(0x10, AGC_WINDOW_SIZE_12_data);// spi_write(0x05, RF_MODEM_RSSI_COMP_1_data);app_command_buf[0] = 0x11;  app_command_buf[1] = 0x20;                                                     app_command_buf[2] = 0x01;                                                   app_command_buf[3] = 0x4e;             app_command_buf[4]  = 0x40;spi_write(5, app_command_buf);            	     spi_write(0x10, COE13_7_0_12_data);spi_write(0x10, COE1_7_0_12_data);spi_write(0x10, COE7_7_0_12_data);       // RF_PAapp_command_buf[0] = 0x11;  app_command_buf[1]  = 0x22;                                                    app_command_buf[2]  = 0x04;                                               app_command_buf[3]  = 0x00;                                                     app_command_buf[4]  = 0x08;app_command_buf[5]  = 127;							// set max powerapp_command_buf[6]  =0x00;app_command_buf[7]  = 0x3d;spi_write(8, app_command_buf);        spi_write(0x0B, RF_SYNTH_PFDCP_CPFF_7_data);// header matchapp_command_buf[0] = 0x11;  app_command_buf[1]  = 0x30;                                                     app_command_buf[2]  = 0x0c;                                                   app_command_buf[3]  = 0x00;                                                       app_command_buf[4]  = 's';app_command_buf[5]  = 0xff;app_command_buf[6]  = 0x40;app_command_buf[7]  = 'w';                                      app_command_buf[8]  = 0xff;                                       app_command_buf[9]  = 0x01; app_command_buf[10] = 'w';                                   app_command_buf[11]  =0xff;                                       app_command_buf[12]  =0x02;app_command_buf[13]  = 'x';                                  app_command_buf[14]  = 0xff;app_command_buf[15]  =0x03;spi_write(16, app_command_buf);spi_write(6, RF_MODEM_RAW_SEARCH2_2_data);spi_write(12, RF_FREQ_CONTROL_INTE_8_data); 	    // set frequency    	
}        

这个函数是用于载入spi配置的,在其第一段有着配置gpio的功能,用户可以通过操作这部分代码验证spi初始化是否通过。

tx_data //发送数据函数

void tx_data(void)					
{	Flag.is_tx = 1;fifo_reset();  spi_write_fifo();  				enable_tx_interrupt();	clr_interrupt();tx_start();rf_timeout = 0;Flag.rf_reach_timeout = 0;delay_10ms();while(nIRQr)						{
//		//此处需要一个短延时
//		if(Flag.rf_reach_timeout)
//		{
//			sdn_reset();
// 			SI4463_init();  		
//			break;		
//		}			}

这个函数是接下来功能操作不可避免使用的函数但是不是很好用,这里对于中断脚信号的读取方式是查询电平高低,并且用死循环进行等待,浪费cpu。因此我简单改良了一下,改为读取标志位。如下:

void test_tx_data2(u8 *pdata)					
{	//delay_ms(1);Flag.is_tx = 1;Flag.run=0;fifo_reset();  m_spi_write_fifo(pdata); 				enable_tx_interrupt();	clr_interrupt();tx_start();rf_timeout = 0;Flag.rf_reach_timeout = 0;while(Flag.run!=2);					//等待发送成功Flag.run=0;	//clr_interrupt();Flag.is_tx=0;				  //转换为接收模式rx_init();					 	//接收模式初始化	Flag.run=0;	
}

rx_init //接受状态初始化函数

void rx_init(void)				
{Flag.is_tx = 0;fifo_reset();  					enable_rx_interrupt();	clr_interrupt();  				rx_start();
}	

test_Receive //接受数据函数(源例程是一段代码块,我取出来并稍微修改了一下)

void test_Receive(void)
{U8 i,chksum;if(!nIRQr){ clr_interrupt();   // clear interrupt	printf("---------------get_info-------------\n");if((spi_read_buf[4] &0x08) == 0)  // crc error check{spi_read_fifo();fifo_reset();chksum = 0;for(i=4;i<payload_length-1;i++)// Checksumchksum += rx_buf[i];          	 		if(( chksum == rx_buf[payload_length-1] )&&( rx_buf[4] == 0x41 ))  {;//LED_GREEN ^= 1;					// data right    	printf("***************data ok****************\n");for(i=0;i<payload_length;i++){printf("%X",rx_buf[i]);}printf("\n");} else{printf("---------------data wrong-------------\n");for(i=0;i<payload_length;i++){printf("%X",rx_buf[i]);}printf("\n");rx_init();     				// data wrong}}else{printf("---------------crc error-------------\n");rx_init();   						// crc error}		}	
}

这个函数过于复杂了,我之后进行了简化

void test_Receive2(void)
{U8 i;if(Flag.run==2){ Flag.run=0;clr_interrupt();   					// clear interrupt	if((spi_read_buf[4] &0x08) == 0)  // crc error check{spi_read_fifo();fifo_reset();m_test.Count2++;for(i=4;i<payload_length;i++){printf("%X",rx_buf[i]);}m_time.recevie_Time=GetTickCount();printf(",%d,%d,%d\n",m_time.recevie_Time,m_test.Count,m_test.Count2);Flag.is_tx=1;							//转换为发送模式}else{printf("---------------crc error-------------\n");rx_init();   						// crc error}		}i=i;	
}

以上就是获取数据的流程,其中获取到的数据会放在rx_buf里,并且,有前4个字节的帧头区分位,这部分数据也会在rx_buf,请注意隔离,我在代码里就从第4位开始了数据读取,抛弃了0~3这前3个字符。

关键参数/变量

tx_ph_data //发送数据缓存

static unsigned char tx_ph_data[payload_length] = {'s','w','w','x',0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x6d};  // test signal,The 10th data is a former nine checksum

其中前4位是头码,在SI4463_init函数里就有一段配置了头码,我没有尝试过取消头码的操作,如果有这方面需求,请自行联系官方技术支持。总长度14,4位是头码,后10位为自定义的数据,想要进行数据传输,就只能对这几位进行操作

payload_length //通讯包长度

#define payload_length  				PACK_MAX_LEN+4 

SI4463_init函数里有一段配置了数据包长度,因此每一次进行数据收发总包长是一定的。因此收发间隔是稳定且固定的,此处的PACK_MAX_LEN对应例程是10,实际需求可以根据自身的需要进行修改,但是不能超过60,也就是总包长不能大于64.

其余部分

剩下的我觉得没有讲解的必要了,基本都是一些不能动的东西,直接当成黑箱就行,只管用,不管内部的原理

//内部函数
void spi_read(U8 data_length, U8 api_command );						
//读取数据
unsigned char spi_byte(unsigned char data);									
//SPIbyte级别操作?
U8 check_cts(void);																					
//校验SPI通路
void spi_write(unsigned char tx_length, unsigned char *p);	
//spi写入数据
void spi_write_fifo(void);																	
//spi向FIFO写入数据
void spi_read_fifo(void);																		
//spi从FIFO读取数据
void clr_interrupt(void);																		
//清除中断
void fifo_reset(void);																			
//fifo重载入
void enable_tx_interrupt(void);															
//使能4463内部发送中断
void enable_rx_interrupt(void);															
//使能4463内部接受中断//工作函数
void tx_data(void);																					
//进入发送模式
void tx_start(void);																				
//开始发送void rx_init(void);																					
//重新进入接受模式
void rx_start(void);																				
//开始接受//状态改变函数
void rf_standby(void);																			
//进入休眠
void rf_init_freq(void);																		
//?//模块初始化函数
void port_init(void);																				
//模块IO口初始化
void sdn_reset(void);																				
//4463复位
void SI4463_init(void);																			
//设备初始化
void sdn_all_reset(void);																		
//模块再初始化//功能函数
void delay_10ms(void);																			
//10ms延时
void delay_x10ms(unsigned int dx10ms);											
//10ms级别延时
void delay_1ms(unsigned int delay_time);										
//毫秒级别延时函数

平台对接

操作其实是很简单的,关键是理解如何使用例程。

第一步-IO口对应

创建一个头文件。写入以下内容

#define   nSEL   				GPIO_Pin_4//GPIOA#define   SCK   				GPIO_Pin_5//GPIOA 
#define   SDO   				GPIO_Pin_6//GPIOA //out
#define   SDI   				GPIO_Pin_7//GPIOA #define   SDN					GPIO_Pin_1//GPIOA 
#define   nIRQ         			GPIO_Pin_0//GPIOA #define   nSEL_0				GPIO_ResetBits(GPIOA , nSEL),delay_us(1)
#define   nSEL_1				GPIO_SetBits	(GPIOA , nSEL),delay_us(1)#define   SDN_0					GPIO_ResetBits(GPIOA , SDN ),delay_us(1)
#define   SDN_1					GPIO_SetBits	(GPIOA , SDN ),delay_us(1)#define   SCK_0					GPIO_ResetBits(GPIOA , SCK ),delay_us(1)
#define   SCK_1					GPIO_SetBits	(GPIOA , SCK ),delay_us(1)#define   SDI_0					GPIO_ResetBits(GPIOA , SDI ),delay_us(1)
#define   SDI_1					GPIO_SetBits	(GPIOA , SDI ),delay_us(1)#define   nIRQr         GPIO_ReadInputDataBit(GPIOA , nIRQ)
#define   SDOr          GPIO_ReadInputDataBit(GPIOA , SDO)

这里的SDI_0和SDI_1对应vc例程里的SDI=0与SDI=1,以此类推
nIRQr对应vc例程里的nIRQ(因为stm32读取IO不是直接读取的)
随后修改关键函数:port_init

//IO function set
void port_init(void)
{//GPIO口初始化,nSEL,SDN,nIRQGPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	GPIO_InitStructure.GPIO_Pin = nSEL ;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	GPIO_InitStructure.GPIO_Pin = SDN ;GPIO_Init(GPIOA, &GPIO_InitStructure);//	
//	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	
//	GPIO_InitStructure.GPIO_Pin = SCK ;
//	GPIO_Init(GPIOA, &GPIO_InitStructure);
//	
//	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	
//	GPIO_InitStructure.GPIO_Pin = SDI ;
//	GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE );	GPIO_InitStructure.GPIO_Pin = nIRQ  ;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  		//浮空输入 下拉输入GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_ResetBits(GPIOA , nIRQ );GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //选择EXTI信号源EXTI_InitStructure.EXTI_Line = EXTI_Line0;               //中断线选择EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;      //EXTI为中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  //下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;                //使能中断EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;          //中断源:3,位于“stm32f10x.h”中NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级:1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;        //子优先级:1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //使能中断通道NVIC_Init(&NVIC_InitStructure);//	
//	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	  //浮空输入
//	GPIO_InitStructure.GPIO_Pin = SDO ;
//	GPIO_Init(GPIOA, &GPIO_InitStructure);}/******************************************************************************/
void EXTI0_IRQHandler(void)                       
{if(EXTI_GetITStatus(EXTI_Line0)!= RESET)  {  EXTI_ClearITPendingBit(EXTI_Line0);if(nIRQr==0)Flag.run=2;//Flag.run++;}   
}

这里有两种写法一种是硬件spi一种是软件spi,vc的例程是软件spi,因此初步移植的时候可以按着软件spi的写,spi校验通过了,再改成硬件spi。

第二步-启用SPI(如果是软件SPI则忽略这一步)

修改SPI的读写函数

unsigned char spi_byte(unsigned char data)		//核心替换
{return SPIx_ReadWriteByte(data);
//	unsigned char i;//	for (i = 0; i < 8; i++)	
//	{				
//		if (data & 0x80)
//			SDI_1;
//		else
//			SDI_0;
//			
//		data <<= 1;
//		SCK_1;
//		
//		if (SDOr)
//			data |= 0x01;
//		else
//			data &= 0xfe;
//			
//		SCK_0;
//	}	
//	return (data);	
}

配置SPI的初始化和SPI读写函数

SPI_InitTypeDef  SPI_InitStructure;void SPIx_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_SPI1, ENABLE );	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
//	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
//	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//	GPIO_Init(GPIOB, &GPIO_InitStructure);
//	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//选择了串行时钟的稳态:时钟悬空高0SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//数据捕获于第二个时钟沿1SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;		//定义波特率预分频的值:波特率预分频值为256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); //使能SPI外设
//	SPI_Cmd(SPI2, ENABLE); //使能SPI外设//	for(;;)
//	{
//		SPIx_ReadWriteByte(0xff);//启动传输		 
//	}}   
u8 SPIx_ReadWriteByte(u8 TxData)
{		u8 retry=0;				 	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位{retry++;if(retry>200)return 0;}			  SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位{retry++;if(retry>200)return 0;}	  						    return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据					    
}

第三步-自定义测试函数

//********************************************************
//          测试与调试函数 R
//********************************************************
void read_Version(void)
{spi_read(9,0x01);for(i=0;i<9;i++){printf("%X",spi_read_buf[i]);}printf("\n");delay_ms(500);	
}
void test_Receive2(void)
{U8 i;if(Flag.run==2){ Flag.run=0;clr_interrupt();   					// clear interrupt	if((spi_read_buf[4] &0x08) == 0)  // crc error check{spi_read_fifo();fifo_reset();m_test.Count2++;for(i=4;i<payload_length;i++){printf("%X",rx_buf[i]);}m_time.recevie_Time=GetTickCount();printf(",%d,%d,%d\n",m_time.recevie_Time,m_test.Count,m_test.Count2);rx_init();   						// rx init}else{printf("---------------crc error-------------\n");rx_init();   						// crc error}		}i=i;	
}
//********************************************************
//          测试与调试函数 T
//********************************************************
void test_tx_data2()					
{	//delay_ms(1);Flag.is_tx = 1;Flag.run=0;fifo_reset();  spi_write_fifo(); 		enable_tx_interrupt();	clr_interrupt();tx_start();while(Flag.run!=2);					//等待发送成功Flag.run=0;	
}

以上函数分别是:测试SPI,持续接受,持续发送

第四步调试模组

主函数:如下

int main(void)
{SystemInit();			 //系统时钟初始化delay_init(72);	     	 //延时初始化NVIC_Configuration();	 //中断组初始化TIM_Configuration();     //定时器1生成ms级别时间戳(配置略)USART1_Config(); 		 //USART1 配置 	SPIx_Init();			 //spi1简单初始化.delay_ms(5000);SDN_1	;printf("int 1 \n");port_init();				 //其余IO初始化printf("int 2 \n");sdn_reset();				 //复位设备printf("int 3 \n");delay_ms(500);SI4463_init();  		 //RF INITprintf("int 4 \n");delay_ms(500);printf("int finished \n");SDN_0	;Flag.is_tx=1;	//初始状态为T(此处改为0就是接受方了)if(Flag.is_tx==0)rx_init();					 //接收模式初始化while(1){read_Version();  	//读版本号测试//--------------------数据收发--------------------if(Flag.is_tx){test_tx_data2();				//发送}else{test_Receive2();				//接受}
}

结语

e70_433的开发失败给了我一些感悟:
1,优先研究项目需求
2,积极尝试同类型模块
4463的强劲在我的意料之外,原本还有一款芯片准备顶替4463,但是现现在不需要了。e70的问题就在于长距离传输上和传输协议上,距离差将尽5倍,并且透传模组,协议设计也不方便,4463 自带crc和自行封包解包省去了我自拟协议封装的劳顿,强烈推荐给相关从业人士。
关于本文本来想一口气写完的,但是发现量非常大,因此分两章,本章讲述完毕例程的移植和调试,下一章讲如何实现业务逻辑,会在周末之前发出(绝对不鸽)。
我对模块的研究向来是浅尝辄止,实用为主,如果大家有什么看不明白的地方,或者和自身的了解有冲突,欢迎指正,谢谢。


http://chatgpt.dhexx.cn/article/Etuwh5ZI.shtml

相关文章

OSI七层网络模型详解!

本文章为沐一Gin的笔记&总结&#xff0c;转载请标明出处。 本文有下一章&#xff0c;如果看完后觉得有帮助就留个言呗&#xff0c;我会继续加油的&#xff01; OSI七层网络模型 物理层&#xff08;Physical Layer&#xff09;&#xff1a; 该层为上层协议提供了一个传输…

linux网络编程基础-socket

Socket基础以及相关应用函数 什么是socket socket&#xff0c;也就是我们所谓的套接字&#xff0c;就像插座一样。 IP地址可以在网络环境中唯一的标识一台主机 端口号&#xff1a;在主机当中唯一标识一个进程。 因此&#xff0c;IP地址端口号&#xff0c;就可以在网络环境…

全双工管道

Fork() 函数&#xff1a; 返回0时是子进程&#xff0c;否则&#xff0c;是父进程。 pipe函数 int pipe(int filedes[2]); 返回值&#xff1a;成功&#xff0c;返回0&#xff0c;否则返回-1。参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道&#xff0c;fd[1]:写管道…

TCP/UDP 协议,和 HTTP、FTP、SMTP,区别及应用场景(单工,半双工,全双工)

TCP/UDP 协议&#xff0c;和 HTTP、FTP、SMTP&#xff0c;区别及应用场景 一、OSI 模型 OSI 模型主要作为一个通用模型来做理论分析&#xff0c;而TCP/IP 协议模型是互联网的实际通讯协议&#xff0c;两者一般做映射分析&#xff0c;以下不做严格区分和声明&#xff08;好吧&…

串口通讯理解

机器的通信方式有两种&#xff0c;分别是并行通信与串行通信 并行通信&#xff1a;并行通信是指多比特数据同时通过并行线进行传送&#xff0c;这样数据传送速度大大提高&#xff0c;但并行传送的线路长度受到限制&#xff0c;因为长度增加&#xff0c;干扰就会增加&#xff0c…

Linux-C语言-利用有名管道简单实现两个进程间的全双工通信

有名管道特点: 1.有名管道是对无名管道的改进&#xff0c;它可以使互不相关的两个进程互相通信,并且在文件系统中可见&#xff0c;可以通过文件名来找到。 2.半双工的通信方式,进程通过文件IO来操作有名管道。 3.有名管道遵循先进先出原则,不支持lseek()。 2.有名管道的创建 配…

E70_433半双工无线模组,伪全双工方案

E70_433半双工无线模组&#xff0c;伪全双工方案 前言架构设计外侧输入模块外侧输出模块内侧发帧模块内侧接受模块 实现代码通用宏定义代码解析 通用工具函数代码解析 串口输入输出函数代码解析 核心业务代码发送模块代码解析 接收模块代码解析代码流程 结语附带 前言 要搞一个…

计算机双工模式,100M 全双工、100M 半双工、10M全双工几种模式分别测试

1. 首先核实是否有多台电脑同时上网&#xff0c;或是当前电脑有没有在进行P2P下载或在线视频之类比较占用网络带宽的操作&#xff0c;此操作可能导致宽带数据量过大影响稳定性。若存在此情况&#xff0c;请关闭些软件断开些联网设备&#xff0c;减少带宽使用测试。 2. 若有其他…

Linux 查看网卡全双工 还是半双工 以及设置网卡为半双工

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴! 请问各位兄弟,在Linux中怎么看网卡是全双工工作还是半双工工作?mii-tool Linux:~ # mii…

路由之静态路由配置

一、路由概念 二、路由表的形成 三、路由协议 静态路由 路由器之间的连接需要配置静态路由 首先创建一个局域网&#xff0c;给pc0和pc1分别配置ip地址。 创建三个不同网段的局域网分别给电脑配置ip地址&#xff0c;将三个连接到一个路由上。为路由器与局域网相连的端口&#…

路由器静态路由配置实验

简单配置: 1.连接设备 2.配置PC和服务器的IP地址和网关 3.在路由器配置接口的IP地址(作为网关) 4.实现PC之间的通信 5.分别命名交换机和路由器,并设置时间 6.实现PC1可以telnet和ssh管理SW2和R1 7.配置静态路由实现全网互通 8.开启web服务器,在4台PC上测试是否能访问 实验…

静态路由配置实验

题目&#xff1a; 目录 1、首先进行基础配置 2、创建环回接口并配置IP地址 3、配置静态路由 对r1&#xff0c;要配置除自身连接的3个网段以外的5个网段 对r2&#xff0c;要配置除自身连接的3个网段以外的5个网段,其中3.3.3.0/24网段已配过&#xff0c;则配剩余4个网段 对r…

华为简单静态路由配置

今天通过学习路由交换的知识&#xff0c;学会了配置简单的静态路由&#xff0c;所以我这里介绍一个简单的实 验&#xff0c;在开始之前&#xff0c;我们要先了解一下什么是路由&#xff1f; 路由 路由是指导报文转发的路径信息&#xff0c;通过路由可以确认转发IP报文的路径。…

vue 3.0 静态路由配置

今天研究了一下vue3.0的静态路由配置&#xff0c;分享一下。 首先我们现在项目中建立router文件夹&#xff0c;如下图所示&#xff1a; 404文件夹存放404页面&#xff0c;components文件夹存放首页界面&#xff0c;config为设置文件夹&#xff0c;diz存放具体的业务逻辑和界面…

计算机配置静态路由目标,静态路由的配置命令

用户需求: 某学校网络拓扑图如图所示,要求配置静态路由,实现计算机PC1、PC2和PC3互通。 直连路由直连路由出现在路由表的条件 (1)接口为“up/up”状态。 (2)接口已经完成了IP地址的配置。直连路由的检查 静态路由是指由网络管理员手工配置的路由信息,用于定义去往目的网络的…

路由基础(简单的静态路由配置)

目录 一.路由基础 1.路由信息获取方式 2.路由加表前的比较-优先级&#xff1b;度量值 3.路由转发的选择-最长掩码 4.静态路由应用场景及配置 1.路由信息获取方式 路由器依据路由表进行转发&#xff0c;要实现转发功能&#xff0c;路由器需要发现路由&#xff0c;三种常见…

四种静态路由配置方法

拓扑图如下&#xff1a; 接口IP地址如拓扑图所示&#xff0c;配置过程省略&#xff0c;ar1和ar3都开启了dhcp。过程省略。 ar1的路由表&#xff1a; 此时只有直连的1.0和2.0网段的IP。没有3.0网段的。 那怎么有3.0网段的呢&#xff1f;接下来介绍四种方法。 方法一&#xff1a…

Windows系统的静态路由配置

Windows电脑静态路由的配置&#xff0c;使电脑同时使用两个不同网段的IP 操作方法&#xff1a; 1.开始菜单&#xff0c;输入cmd&#xff0c;右击cmd.exe&#xff0c;选择以管理员身份运行 2.查看当前路由的配置 命令格式&#xff1a; ##查看静态路由 route print 输出结果…

华为静态路由配置

静态路由 静态路由(Static Router)是由管理员通过手动配置的方式创建的路由&#xff0c;可以让路由器便捷的获知到达目的网络的路由。在静态路由基础上也可使用负载均衡、路由备份等技术。 本文分别对静态路由、负载均衡、路由备份进行实验配置及配置完之后如何验证。 网络拓…

ENSP静态路由配置

拓扑图 实现结果&#xff1a; 1.pc1访问pc345走上面R1,R2&#xff0c;R2实现备份21.1.1.0/24&#xff0c;实现备份pc3456访问pc12走下面的R3,R4并实现等价路由&#xff0c;尽量减少路由条目&#xff0c;全网可达 2.R7代表运行商&#xff0c;所有pc均可访问 第一步&#xff1a…