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和自行封包解包省去了我自拟协议封装的劳顿,强烈推荐给相关从业人士。
关于本文本来想一口气写完的,但是发现量非常大,因此分两章,本章讲述完毕例程的移植和调试,下一章讲如何实现业务逻辑,会在周末之前发出(绝对不鸽)。
我对模块的研究向来是浅尝辄止,实用为主,如果大家有什么看不明白的地方,或者和自身的了解有冲突,欢迎指正,谢谢。