前言
经过长期的测试,我惊奇的发现,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 PORT
app_command_buf[1] = 0x21; // gpio 0 ,Rx data//0
app_command_buf[2] = 0x20; // gpio1, output 0//0
app_command_buf[3] = 0x21; // gpio2, hign while in receive mode
app_command_buf[4] = 0x20; // gpio3, hign while in transmit mode
app_command_buf[5] = 0x27; // nIRQ
app_command_buf[6] = 0x0b; // sdo
spi_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 adjustment
spi_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 mode
spi_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 Preamble
app_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 Preamble
app_command_buf[5] = 0x14; // detect 20 bits
app_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 sync
app_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 bytes
app_command_buf[5] = 0x2d; // sync byte3
app_command_buf[6] = 0xd4; // sync byte2
app_command_buf[7] = 0x00; // sync byte1
app_command_buf[8] = 0x00; // sync byte0
spi_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 crc
spi_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 MSB
spi_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_speed
spi_write(0x0E, RF_MODEM_RAW_CONTROL_10_data);
#else
spi_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_PA
app_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 power
app_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 match
app_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++)// Checksum
chksum += 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,nIRQ
GPIO_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; //抢占优先级:1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级:1
NVIC_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工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //选择了串行时钟的稳态:时钟悬空高0
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第二个时钟沿1
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为256
SPI_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 INIT
printf("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和自行封包解包省去了我自拟协议封装的劳顿,强烈推荐给相关从业人士。
关于本文本来想一口气写完的,但是发现量非常大,因此分两章,本章讲述完毕例程的移植和调试,下一章讲如何实现业务逻辑,会在周末之前发出(绝对不鸽)。
我对模块的研究向来是浅尝辄止,实用为主,如果大家有什么看不明白的地方,或者和自身的了解有冲突,欢迎指正,谢谢。