STM32F1 2.4G无线通信模块24L01是一个2.4G的无线通信模块,空旷地实测通信距离70-80米左右。24L01有6个通道,也就是一个无线模块最多可以同时接收6个无线模块的数据,一个通道对应一个。地址一共40位,但是只有通道0可以自由设置地址。其他的5路通道只能设置最后8位地址,前面的32位地址是固定的。自动应答功能,自动重发功能(最多16次),(实测;距离25cm,最大功率0db、开启自动应答、自动重发16次的情况下,传输30-50次会丢失一次数据)使用SPI协议通信,最大通信速度10m bps供电电压1.9-3.6v,一般使用3.3v供电使用(不能使用5v),但是IO口可以容忍5v,可以与5v单片机直接通信。24L01有不同的工作模式,接收模式、发送模式、掉电模式、待机模式等,数据手册都有说明。可以产生中断,节省了单片机的查询时间24L01的指令有点坑,读寄存器的时候直接操作相应寄存器的地址就行。但是在写寄存器的时候就不是直接操作寄存器的地址了,低5位才是寄存器地址,高三位是011.一般发射数据的时候才设置为发送模式,平时用待机模式就行。配置寄存器比较多,也不需要全部配置吧,配置一部分就行,代码中有注释。本实验分为两个工程,一个用于发送,一个用于接收。注释都在发送板的实验里,接收板的注释最好不要看。发送板功能;上电时如果检测到存在24L01 LED会亮一秒钟才会闪烁。如果不存在24L01模块则上电第一秒LED是黑的。一次发送32个u8的数据,但是只有数组的第11位才是有用的,也就是数组下标为10的那个。如果数据发送成功则LED快闪,一秒钟闪5次。如果没有成功发送数据则LED慢闪,一秒钟一次。接收板功能;上电后检查24L01模块,如果上电时LED是黑的且LED不闪烁,那就是没有检测到24L01模块,如果上电后LED常亮表示检测到模块但是没有接收到数据,如果LED闪烁就是接收到数据了。注意;两个工程有部分地方不一样。1、LED的IO口不一样2、24L01模块的控制IO不一样,但是SPI口是一样的3、主函数不一样,一个纯接收一个纯发送当然不一样了至于具体怎么配置寄存器,发送数据接收数据的流程工程里面的函数都注释的很清楚。发送板的工程分5个文件,3个c文件,两个h文件文件
STM32F1 2.4G无线通信模块
24L01是一个2.4G的无线通信模块,空旷地实测通信距离70-80米左右。
24L01有6个通道,也就是一个无线模块最多可以同时接收6个无线模块的数据,一个通道对应一个。地址一共40位,但是只有通道0可以自由设置地址。其他的5路通道只能设置最后8位地址,前面的32位地址是固定的。
自动应答功能,自动重发功能(最多16次),
(实测;距离25cm,最大功率0db、开启自动应答、自动重发16次的情况下,传输30-50次会丢失一次数据)
使用SPI协议通信,最大通信速度10m bps
供电电压1.9-3.6v,一般使用3.3v供电使用(不能使用5v),但是IO口可以容忍5v,可以与5v单片机直接通信。
24L01有不同的工作模式,接收模式、发送模式、掉电模式、待机模式等,数据手册都有说明。
可以产生中断,节省了单片机的查询时间
24L01的指令有点坑,读寄存器的时候直接操作相应寄存器的地址就行。但是在写寄存器的时候就不是直接操作寄存器的地址了,低5位才是寄存器地址,高三位是011.
一般发射数据的时候才设置为发送模式,平时用待机模式就行。
配置寄存器比较多,也不需要全部配置吧,配置一部分就行,代码中有注释。
本实验分为两个工程,一个用于发送,一个用于接收。注释都在发送板的实验里,接收板的注释最好不要看。
发送板功能;上电时如果检测到存在24L01 LED会亮一秒钟才会闪烁。如果不存在24L01模块则上电第一秒LED是黑的。一次发送32个u8的数据,但是只有数组的第11位才是有用的,也就是数组下标为10的那个。如果数据发送成功则LED快闪,一秒钟闪5次。如果没有成功发送数据则LED慢闪,一秒钟一次。
接收板功能;上电后检查24L01模块,如果上电时LED是黑的且LED不闪烁,那就是没有检测到24L01模块,如果上电后LED常亮表示检测到模块但是没有接收到数据,如果LED闪烁就是接收到数据了。
注意;两个工程有部分地方不一样。
1、LED的IO口不一样
2、24L01模块的控制IO不一样,但是SPI口是一样的
3、主函数不一样,一个纯接收一个纯发送当然不一样了
至于具体怎么配置寄存器,发送数据接收数据的流程工程里面的函数都注释的很清楚。
发送板的工程分5个文件,3个c文件,两个h文件
文件 Main.c
#include "sys.h"
#include "delay.h"
#include "spi_24l01.h"
#include "24L01.h"
#define LED(value) GPIO_WriteBit(GPIOE,GPIO_Pin_5,value)
/************************************************************
功能;LED闪烁,间隔为100ms
其中LED接在PB5上,低电平有效
****************************************************************/
void init_led();
int main(void)
{
u16 ms_count=0;
u8 led_flag=0;
u8 buf_data[33]={0};//用来存放临时数据
delay_init(); //延时函数初始化
init_led(); //LED初始化。LED是低电平有效
init_24L01();//无线模块初始化
LED(NRF24L01_Check());//如果检测到无线模块,LED会亮1秒钟
delay_ms(1000);
NRF24L01_TX_Mode();//设置为发送模式
while(1)
{
if(NRF24L01_TxPacket(buf_data) == TX_OK)//其实数据是一直发送的
{
buf_data[10]=!buf_data[10];//就拿数组中的一个元素作为LED的标志位吧,
delay_ms(100);
LED(buf_data[10]);//如果发送成功,那么接收板和发送板的LED是同步闪烁的
}
else//如果发送失败
{
ms_count++;
delay_ms(1);
if(ms_count>1000)//那就是慢闪了
{
ms_count=0;
led_flag = !led_flag;
LED(led_flag);
}
}
}
}
void init_led()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIO_WriteBit(GPIOE,GPIO_Pin_5,1);//初始化输出1吧
}
文件spi_24l01.c
好像这样命名文件很不规范,不过无所谓了
#include "spi_24l01.h"
/**********************************
SPI初始化,
这个初始化是给24l01用的,所以在24l01初始化的时候不需要更改SPI初始化的配置
至于24L01的SPI协议中相位选择,高位在前还是低位在前等在24L01的时序图中可以看出来
***********************************/
void spi_init()
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);//PB13/14/15上拉,不加上拉不知道行不行
SPI_Cmd(SPI2, DISABLE); // 最好是在关闭状态配置SPI
SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;//72/8=9mhz
SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;//第一个时钟就开始传数据了
SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;//时钟线空闲的时候为低
SPI_InitStruct.SPI_CRCPolynomial=7;//据说这玩意不为0就行
SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;//每帧数据位8位
SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//火力全开,全双工
SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;//好像时序图上是先传输低位的,????????
SPI_InitStruct.SPI_Mode=SPI_Mode_Master;//STM32当然作为主机了
SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;//这玩意也不是很懂,大概意思是由软件控制SPI何时开始传输吧
SPI_Init(SPI2,&SPI_InitStruct);
SPI_Cmd(SPI2,ENABLE);//既然配置好了就开工吧
}
/**********************************************************
SPI收发一帧数据,因为是双向同步传输的,所以读和写可以共用一个函数
*************************************************************/
u8 spi_readWrite_byte(u8 SPI_TXdata)
{
u8 try_count=0;
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)== 0)//等待发送寄存器把数据发送完
{
try_count++;
if(try_count>240)
break;
}
SPI_I2S_SendData(SPI2,SPI_TXdata);
try_count=0;
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)== 0)//这一次是等待接收完成
{
try_count++;
if(try_count>240)
break;
}
return SPI_I2S_ReceiveData(SPI2);//返回读到的数据
}
文件spi_24l01.h
#ifndef _SPI_24L01_H
#define _SPI_24L01_H
#include "sys.h"
void spi_init();
u8 spi_readWrite_byte(u8 L_data);
#endif
文件24L01.c
#include "24L01.h"
#include "spi_24l01.h"
#include "delay.h"
//设置地址,每个通道都可以设置不同的地址,但是这里只设置了一个通道
const u8 TX_ADDRESS[5]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[5]={0x34,0x43,0x10,0x10,0x01};
/*******************************************************
24l01模块用起来不难,但是该怎么封装函数才是最合理的呢,
感觉原子哥的函数封装得挺合理的,就像原子的代码一样封装函数吧
**********************************************************/
void init_24L01()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOB, &GPIO_InitStruct);
//GPIO_WriteBit(GPIOB,GPIO_Pin_12,1);//把这个拉高是为了防止在同一个SPI上的其他模块干扰
GPIO_SetBits(GPIOB,GPIO_Pin_12);//上拉,原子的开发板才需要加这个上拉
//GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//这个速度应该是无所谓的吧
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_ResetBits(GPIOG,GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//PG6,7,8上拉 ,这几个IO是用来控制24L01模块的
spi_init(); //SPI初始化,这里不需要重新配置SPI协议,因为SPI就是根据24L01的要求初始化的
NRF24L01_CE=0; //发送数据时拉高,现在不发送数据,所以暂时为低
NRF24L01_CSN=1; //SPI片选取消
}
/********************************
读取寄存器
入口;寄存器地址
返回值;读到的数据
说明;入口直接给寄存器地址就好,不需要考虑读寄存器的高三位指令,
**********************************/
u8 NRF24L01_Read_Reg(u8 reg)
{
u8 temp_RXdata;
NRF24L01_CSN=0;
spi_readWrite_byte(reg);
temp_RXdata = spi_readWrite_byte(0xff);
NRF24L01_CSN=1;
return temp_RXdata;
}
/**************************************
写寄存器;
入口参数1;寄存器地址,高三位会在函数里面处理
入口参数2;要写进寄存器的值
返回值;好像没什么用
*************************************/
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
u8 temp_RXdata;
NRF24L01_CSN=0;
spi_readWrite_byte(reg);
temp_RXdata = spi_readWrite_byte(value);
NRF24L01_CSN=1;
return temp_RXdata;
}
/******************************************
向寄存器中连续写数据;一般是设置通道地址和把数据从单片机发送到24L01的缓冲区
入口参数1;寄存器地址
入口参数2;要写入数组的地址
入口参数3;数据的长度
返回值;这个并没有什么用
******************************************/
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
{
u8 i,status;
NRF24L01_CSN=0;
status = spi_readWrite_byte(reg);
for(i=0;i
{
spi_readWrite_byte(*pBuf++);
}
NRF24L01_CSN=1;
return status;//返回这个状态值有什么用呢
}
/********************************************
把数据从24L01的接收缓冲区读取到单片机中
入口参数1;寄存器地址
入口参数2;接收数组的地址
入口参数3;要读取的数据的长度
返回值;并没有什么用
********************************************/
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{
u8 i,status;
NRF24L01_CSN=0;
status = spi_readWrite_byte(reg);
for(i=0;i
{
pBuf
=spi_readWrite_byte(0xff);
}
NRF24L01_CSN=1;
return status;//返回这个状态值有什么用呢
}
/*************************************
检测是否纯存在24L01模块
原理;往模块的寄存器里写数据,然后再把数据读出来
如果读取出来的数据是写进去的数据的话,证明模块存在,
如果数据不一样,那就是不存在了。
返回值;0=存在模块,1=不存在模块
注意;不管模块存在与否,程序都不能在这里死等
****************************************/
u8 NRF24L01_Check(void)
{
u8 status;
u8 test_data[2]={0x01,0x01};
NRF24L01_CSN=0;
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,test_data,2);
NRF24L01_Read_Buf(TX_ADDR,test_data,2);
if(test_data[0] + test_data[1] == 0x02)
status = 0;//存在就返回0
else
status = 1;//不存在就返回1
NRF24L01_CSN=1;
return status;
}
/***************************************
把24L01接收到的数据读取到单片机里
入口参数;接收回来后存放数据的数组地址
返回值;一个标志位,0=成功,1=失败
说明;每次数据接收默认接收32个u8
******************************************/
u8 NRF24L01_RxPacket(u8 *txbuf)
{
u8 status;
status = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
NRF24L01_Write_Reg(W_REGISTER | STATUS,status);//因为寄存器是写1清零,所以把读取到的数据重新写进去就可以清除状态了
NRF24L01_Read_Buf(R_RX_PAYLOAD,txbuf,32);
if(status&RX_OK)
{
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除TX FIFO寄存器 ,如果不清除会怎么样?
return 0; //接收成功就返回0
}
return 1;//返回1就是接收失败了
}
/***************************************
把数据通过无线电发送出去
分两个步骤;
1、把数据通过SPI从单片机发送到24L01的缓冲区
2、把数据从24L01的缓冲区通过无线电发送出去
入口参数1;要发送的数组的首地址
返回值;标志位,MAX_TX=发送次数到达最大,反正就是发送失败了
TX_OK=发送成功,0xff=其他乱七八糟的原因导致的发送失败
*****************************************/
u8 NRF24L01_TxPacket(u8 *rxbuf)
{
u8 status;
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_RX_PAYLOAD,rxbuf,32);//一次性把32字节写到24L01的缓冲区
NRF24L01_CE=1;//把数据通过无线发送出去
while(NRF24L01_IRQ == 1);//等待发送完成,完成后会产生一个低电平中断
status = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
NRF24L01_Write_Reg(W_REGISTER | STATUS,status);//因为寄存器是写1清零,所以把读取到的数据重新写进去就可以清除状态了
if(status&MAX_TX)//到达最大重发次数
{
NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 ,如果不清除会怎么样?
return MAX_TX;
}
if(status&TX_OK)//发送成功
{
return TX_OK;
}
return 0xff;//能运行到这里,肯定是发生了一些神奇的错误
}
//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR
//PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了
//CE为高大于10us,则启动发送.
void NRF24L01_TX_Mode(void)
{
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_REGISTER | TX_ADDR,(u8*)TX_ADDRESS,5);//写TX节点地址
NRF24L01_Write_Buf(W_REGISTER | RX_ADDR_P0,(u8*)RX_ADDRESS,5); //设置TX节点地址,主要为了使能ACK
NRF24L01_Write_Reg(W_REGISTER | EN_AA,0x01); //使能通道0的自动应答
NRF24L01_Write_Reg(W_REGISTER | EN_RXADDR,0x01); //使能通道0的接收地址
NRF24L01_Write_Reg(W_REGISTER | SETUP_RETR,0x1f);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:16次
NRF24L01_Write_Reg(W_REGISTER | RF_CH,40); //设置RF通道为40
NRF24L01_Write_Reg(W_REGISTER | RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启
NRF24L01_Write_Reg(W_REGISTER | CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
NRF24L01_CE=1;//CE为高,10us后启动发送
}
//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了
void NRF24L01_RX_Mode(void)
{
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_REGISTER | RX_ADDR_P0,(u8*)RX_ADDRESS,5);//写RX节点地址
NRF24L01_Write_Reg(W_REGISTER | EN_AA,0x01); //使能通道0的自动应答
NRF24L01_Write_Reg(W_REGISTER | EN_RXADDR,0x01);//使能通道0的接收地址
NRF24L01_Write_Reg(W_REGISTER | RF_CH,40); //设置RF通信频率
NRF24L01_Write_Reg(W_REGISTER | RX_PW_P0,32);//选择通道0的有效数据宽度
NRF24L01_Write_Reg(W_REGISTER | RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
NRF24L01_Write_Reg(W_REGISTER | CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
NRF24L01_CE = 1; //CE为高,进入接收模式
}
文件24L01.h
#ifndef _24L01_H
#define _24L01_H
#include "sys.h"
void NRF24L01_TX_Mode(void);
void NRF24L01_RX_Mode(void);
u8 NRF24L01_RxPacket(u8 *rxbuf);
u8 NRF24L01_TxPacket(u8 *txbuf);
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len);
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len);
u8 NRF24L01_Read_Reg(u8 reg);
u8 NRF24L01_Write_Reg(u8 reg,u8 value);
u8 NRF24L01_Check(void);
void init_24L01();
//********************************************
//下面是24L01的端口控制线
//注意;发送板和接收板这3个引脚是不一样的
#define NRF24L01_CE PGout(8) //24L01片选信号 ,0=使能,1=不使能,
#define NRF24L01_CSN PGout(7) //SPI片选信号 0=SPI使能,1=SPI不使能,
#define NRF24L01_IRQ PGin(6) //IRQ主机数据输入,24L01的中断口,中断产生时本中断线被拉低
//***********************************************************
//下面是24L01模块的指令和寄存器地址,是直接从数据手册拷贝过来的
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define R_RX_PAYLOAD 0x61
#define W_RX_PAYLOAD 0xa0
#define FLUSH_TX 0xe1
#define FLUSH_RX 0xe2
#define REUSE_TX_PL 0xe3
#define NOP 0xff
//这几个其实就是状态寄存器里面的状态位,为了计算方便定义为宏
#define MAX_TX 0x10 //达到最大发送次数中断
#define TX_OK 0x20 //TX发送完成中断
#define RX_OK 0x40 //接收到数据中断
//寄存器地址
#define CONFIG 0X00
#define EN_AA 0X01
#define EN_RXADDR 0X02
#define SETUP_AW 0X03
#define SETUP_RETR 0X04
#define RF_CH 0X05
#define RF_SETUP 0X06
#define STATUS 0X07
#define OBSERVE_TX 0X08
#define CD 0X09
#define RX_ADDR_P0 0X0A
#define RX_ADDR_P1 0X0B
#define RX_ADDR_P2 0X0C
#define RX_ADDR_P3 0X0D
#define RX_ADDR_P4 0X0E
#define RX_ADDR_P5 0X0F
#define TX_ADDR 0X10
#define RX_PW_P0 0X11
#define RX_PW_P1 0X12
#define RX_PW_P2 0X13
#define RX_PW_P3 0X14
#define RX_PW_P4 0X15
#define RX_PW_P5 0X16
#define FIFO_STATUS 0X17
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#endif
对于接收板,只有Main.c不一样,其他的四个文件大部分都是一样的(除了控制线的IO口不一样),所以这里只放接收板的main.c文件
#include "sys.h"
#include "delay.h"
#include "spi_24l01.h"
#include "24L01.h"
#define LED(value) GPIO_WriteBit(GPIOA,GPIO_Pin_8,value)
/************************************************************
功能;LED闪烁,间隔为100ms
其中LED接在PB5上,低电平有效
****************************************************************/
void init_led();
int main(void)
{
u8 buf_data[33]={0};//用来存放临时数据
delay_init(); //延时函数初始化
init_led(); //LED初始化
init_24L01();
LED(NRF24L01_Check());
NRF24L01_RX_Mode();
while(1)
{
if(NRF24L01_RxPacket(buf_data) == 0)
{
LED(buf_data[10]);
}
else
{
delay_us(200);
}
}
}
void init_led()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_WriteBit(GPIOA,GPIO_Pin_5,1);//初始化输出1吧
}
STM32F1 2.4G无线通信模块24L01是一个2.4G的无线通信模块,空旷地实测通信距离70-80米左右。24L01有6个通道,也就是一个无线模块最多可以同时接收6个无线模块的数据,一个通道对应一个。地址一共40位,但是只有通道0可以自由设置地址。其他的5路通道只能设置最后8位地址,前面的32位地址是固定的。自动应答功能,自动重发功能(最多16次),(实测;距离25cm,最大功率0db、开启自动应答、自动重发16次的情况下,传输30-50次会丢失一次数据)使用SPI协议通信,最大通信速度10m bps供电电压1.9-3.6v,一般使用3.3v供电使用(不能使用5v),但是IO口可以容忍5v,可以与5v单片机直接通信。24L01有不同的工作模式,接收模式、发送模式、掉电模式、待机模式等,数据手册都有说明。可以产生中断,节省了单片机的查询时间24L01的指令有点坑,读寄存器的时候直接操作相应寄存器的地址就行。但是在写寄存器的时候就不是直接操作寄存器的地址了,低5位才是寄存器地址,高三位是011.一般发射数据的时候才设置为发送模式,平时用待机模式就行。配置寄存器比较多,也不需要全部配置吧,配置一部分就行,代码中有注释。本实验分为两个工程,一个用于发送,一个用于接收。注释都在发送板的实验里,接收板的注释最好不要看。发送板功能;上电时如果检测到存在24L01 LED会亮一秒钟才会闪烁。如果不存在24L01模块则上电第一秒LED是黑的。一次发送32个u8的数据,但是只有数组的第11位才是有用的,也就是数组下标为10的那个。如果数据发送成功则LED快闪,一秒钟闪5次。如果没有成功发送数据则LED慢闪,一秒钟一次。接收板功能;上电后检查24L01模块,如果上电时LED是黑的且LED不闪烁,那就是没有检测到24L01模块,如果上电后LED常亮表示检测到模块但是没有接收到数据,如果LED闪烁就是接收到数据了。注意;两个工程有部分地方不一样。1、LED的IO口不一样2、24L01模块的控制IO不一样,但是SPI口是一样的3、主函数不一样,一个纯接收一个纯发送当然不一样了至于具体怎么配置寄存器,发送数据接收数据的流程工程里面的函数都注释的很清楚。发送板的工程分5个文件,3个c文件,两个h文件文件
STM32F1 2.4G无线通信模块
24L01是一个2.4G的无线通信模块,空旷地实测通信距离70-80米左右。
24L01有6个通道,也就是一个无线模块最多可以同时接收6个无线模块的数据,一个通道对应一个。地址一共40位,但是只有通道0可以自由设置地址。其他的5路通道只能设置最后8位地址,前面的32位地址是固定的。
自动应答功能,自动重发功能(最多16次),
(实测;距离25cm,最大功率0db、开启自动应答、自动重发16次的情况下,传输30-50次会丢失一次数据)
使用SPI协议通信,最大通信速度10m bps
供电电压1.9-3.6v,一般使用3.3v供电使用(不能使用5v),但是IO口可以容忍5v,可以与5v单片机直接通信。
24L01有不同的工作模式,接收模式、发送模式、掉电模式、待机模式等,数据手册都有说明。
可以产生中断,节省了单片机的查询时间
24L01的指令有点坑,读寄存器的时候直接操作相应寄存器的地址就行。但是在写寄存器的时候就不是直接操作寄存器的地址了,低5位才是寄存器地址,高三位是011.
一般发射数据的时候才设置为发送模式,平时用待机模式就行。
配置寄存器比较多,也不需要全部配置吧,配置一部分就行,代码中有注释。
本实验分为两个工程,一个用于发送,一个用于接收。注释都在发送板的实验里,接收板的注释最好不要看。
发送板功能;上电时如果检测到存在24L01 LED会亮一秒钟才会闪烁。如果不存在24L01模块则上电第一秒LED是黑的。一次发送32个u8的数据,但是只有数组的第11位才是有用的,也就是数组下标为10的那个。如果数据发送成功则LED快闪,一秒钟闪5次。如果没有成功发送数据则LED慢闪,一秒钟一次。
接收板功能;上电后检查24L01模块,如果上电时LED是黑的且LED不闪烁,那就是没有检测到24L01模块,如果上电后LED常亮表示检测到模块但是没有接收到数据,如果LED闪烁就是接收到数据了。
注意;两个工程有部分地方不一样。
1、LED的IO口不一样
2、24L01模块的控制IO不一样,但是SPI口是一样的
3、主函数不一样,一个纯接收一个纯发送当然不一样了
至于具体怎么配置寄存器,发送数据接收数据的流程工程里面的函数都注释的很清楚。
发送板的工程分5个文件,3个c文件,两个h文件
文件 Main.c
#include "sys.h"
#include "delay.h"
#include "spi_24l01.h"
#include "24L01.h"
#define LED(value) GPIO_WriteBit(GPIOE,GPIO_Pin_5,value)
/************************************************************
功能;LED闪烁,间隔为100ms
其中LED接在PB5上,低电平有效
****************************************************************/
void init_led();
int main(void)
{
u16 ms_count=0;
u8 led_flag=0;
u8 buf_data[33]={0};//用来存放临时数据
delay_init(); //延时函数初始化
init_led(); //LED初始化。LED是低电平有效
init_24L01();//无线模块初始化
LED(NRF24L01_Check());//如果检测到无线模块,LED会亮1秒钟
delay_ms(1000);
NRF24L01_TX_Mode();//设置为发送模式
while(1)
{
if(NRF24L01_TxPacket(buf_data) == TX_OK)//其实数据是一直发送的
{
buf_data[10]=!buf_data[10];//就拿数组中的一个元素作为LED的标志位吧,
delay_ms(100);
LED(buf_data[10]);//如果发送成功,那么接收板和发送板的LED是同步闪烁的
}
else//如果发送失败
{
ms_count++;
delay_ms(1);
if(ms_count>1000)//那就是慢闪了
{
ms_count=0;
led_flag = !led_flag;
LED(led_flag);
}
}
}
}
void init_led()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIO_WriteBit(GPIOE,GPIO_Pin_5,1);//初始化输出1吧
}
文件spi_24l01.c
好像这样命名文件很不规范,不过无所谓了
#include "spi_24l01.h"
/**********************************
SPI初始化,
这个初始化是给24l01用的,所以在24l01初始化的时候不需要更改SPI初始化的配置
至于24L01的SPI协议中相位选择,高位在前还是低位在前等在24L01的时序图中可以看出来
***********************************/
void spi_init()
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);//PB13/14/15上拉,不加上拉不知道行不行
SPI_Cmd(SPI2, DISABLE); // 最好是在关闭状态配置SPI
SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;//72/8=9mhz
SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;//第一个时钟就开始传数据了
SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;//时钟线空闲的时候为低
SPI_InitStruct.SPI_CRCPolynomial=7;//据说这玩意不为0就行
SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;//每帧数据位8位
SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//火力全开,全双工
SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;//好像时序图上是先传输低位的,????????
SPI_InitStruct.SPI_Mode=SPI_Mode_Master;//STM32当然作为主机了
SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;//这玩意也不是很懂,大概意思是由软件控制SPI何时开始传输吧
SPI_Init(SPI2,&SPI_InitStruct);
SPI_Cmd(SPI2,ENABLE);//既然配置好了就开工吧
}
/**********************************************************
SPI收发一帧数据,因为是双向同步传输的,所以读和写可以共用一个函数
*************************************************************/
u8 spi_readWrite_byte(u8 SPI_TXdata)
{
u8 try_count=0;
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)== 0)//等待发送寄存器把数据发送完
{
try_count++;
if(try_count>240)
break;
}
SPI_I2S_SendData(SPI2,SPI_TXdata);
try_count=0;
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)== 0)//这一次是等待接收完成
{
try_count++;
if(try_count>240)
break;
}
return SPI_I2S_ReceiveData(SPI2);//返回读到的数据
}
文件spi_24l01.h
#ifndef _SPI_24L01_H
#define _SPI_24L01_H
#include "sys.h"
void spi_init();
u8 spi_readWrite_byte(u8 L_data);
#endif
文件24L01.c
#include "24L01.h"
#include "spi_24l01.h"
#include "delay.h"
//设置地址,每个通道都可以设置不同的地址,但是这里只设置了一个通道
const u8 TX_ADDRESS[5]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[5]={0x34,0x43,0x10,0x10,0x01};
/*******************************************************
24l01模块用起来不难,但是该怎么封装函数才是最合理的呢,
感觉原子哥的函数封装得挺合理的,就像原子的代码一样封装函数吧
**********************************************************/
void init_24L01()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOB, &GPIO_InitStruct);
//GPIO_WriteBit(GPIOB,GPIO_Pin_12,1);//把这个拉高是为了防止在同一个SPI上的其他模块干扰
GPIO_SetBits(GPIOB,GPIO_Pin_12);//上拉,原子的开发板才需要加这个上拉
//GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//这个速度应该是无所谓的吧
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_ResetBits(GPIOG,GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//PG6,7,8上拉 ,这几个IO是用来控制24L01模块的
spi_init(); //SPI初始化,这里不需要重新配置SPI协议,因为SPI就是根据24L01的要求初始化的
NRF24L01_CE=0; //发送数据时拉高,现在不发送数据,所以暂时为低
NRF24L01_CSN=1; //SPI片选取消
}
/********************************
读取寄存器
入口;寄存器地址
返回值;读到的数据
说明;入口直接给寄存器地址就好,不需要考虑读寄存器的高三位指令,
**********************************/
u8 NRF24L01_Read_Reg(u8 reg)
{
u8 temp_RXdata;
NRF24L01_CSN=0;
spi_readWrite_byte(reg);
temp_RXdata = spi_readWrite_byte(0xff);
NRF24L01_CSN=1;
return temp_RXdata;
}
/**************************************
写寄存器;
入口参数1;寄存器地址,高三位会在函数里面处理
入口参数2;要写进寄存器的值
返回值;好像没什么用
*************************************/
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
u8 temp_RXdata;
NRF24L01_CSN=0;
spi_readWrite_byte(reg);
temp_RXdata = spi_readWrite_byte(value);
NRF24L01_CSN=1;
return temp_RXdata;
}
/******************************************
向寄存器中连续写数据;一般是设置通道地址和把数据从单片机发送到24L01的缓冲区
入口参数1;寄存器地址
入口参数2;要写入数组的地址
入口参数3;数据的长度
返回值;这个并没有什么用
******************************************/
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
{
u8 i,status;
NRF24L01_CSN=0;
status = spi_readWrite_byte(reg);
for(i=0;i
{
spi_readWrite_byte(*pBuf++);
}
NRF24L01_CSN=1;
return status;//返回这个状态值有什么用呢
}
/********************************************
把数据从24L01的接收缓冲区读取到单片机中
入口参数1;寄存器地址
入口参数2;接收数组的地址
入口参数3;要读取的数据的长度
返回值;并没有什么用
********************************************/
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{
u8 i,status;
NRF24L01_CSN=0;
status = spi_readWrite_byte(reg);
for(i=0;i
{
pBuf=spi_readWrite_byte(0xff);
}
NRF24L01_CSN=1;
return status;//返回这个状态值有什么用呢
}
/*************************************
检测是否纯存在24L01模块
原理;往模块的寄存器里写数据,然后再把数据读出来
如果读取出来的数据是写进去的数据的话,证明模块存在,
如果数据不一样,那就是不存在了。
返回值;0=存在模块,1=不存在模块
注意;不管模块存在与否,程序都不能在这里死等
****************************************/
u8 NRF24L01_Check(void)
{
u8 status;
u8 test_data[2]={0x01,0x01};
NRF24L01_CSN=0;
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,test_data,2);
NRF24L01_Read_Buf(TX_ADDR,test_data,2);
if(test_data[0] + test_data[1] == 0x02)
status = 0;//存在就返回0
else
status = 1;//不存在就返回1
NRF24L01_CSN=1;
return status;
}
/***************************************
把24L01接收到的数据读取到单片机里
入口参数;接收回来后存放数据的数组地址
返回值;一个标志位,0=成功,1=失败
说明;每次数据接收默认接收32个u8
******************************************/
u8 NRF24L01_RxPacket(u8 *txbuf)
{
u8 status;
status = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
NRF24L01_Write_Reg(W_REGISTER | STATUS,status);//因为寄存器是写1清零,所以把读取到的数据重新写进去就可以清除状态了
NRF24L01_Read_Buf(R_RX_PAYLOAD,txbuf,32);
if(status&RX_OK)
{
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除TX FIFO寄存器 ,如果不清除会怎么样?
return 0; //接收成功就返回0
}
return 1;//返回1就是接收失败了
}
/***************************************
把数据通过无线电发送出去
分两个步骤;
1、把数据通过SPI从单片机发送到24L01的缓冲区
2、把数据从24L01的缓冲区通过无线电发送出去
入口参数1;要发送的数组的首地址
返回值;标志位,MAX_TX=发送次数到达最大,反正就是发送失败了
TX_OK=发送成功,0xff=其他乱七八糟的原因导致的发送失败
*****************************************/
u8 NRF24L01_TxPacket(u8 *rxbuf)
{
u8 status;
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_RX_PAYLOAD,rxbuf,32);//一次性把32字节写到24L01的缓冲区
NRF24L01_CE=1;//把数据通过无线发送出去
while(NRF24L01_IRQ == 1);//等待发送完成,完成后会产生一个低电平中断
status = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
NRF24L01_Write_Reg(W_REGISTER | STATUS,status);//因为寄存器是写1清零,所以把读取到的数据重新写进去就可以清除状态了
if(status&MAX_TX)//到达最大重发次数
{
NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 ,如果不清除会怎么样?
return MAX_TX;
}
if(status&TX_OK)//发送成功
{
return TX_OK;
}
return 0xff;//能运行到这里,肯定是发生了一些神奇的错误
}
//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR
//PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了
//CE为高大于10us,则启动发送.
void NRF24L01_TX_Mode(void)
{
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_REGISTER | TX_ADDR,(u8*)TX_ADDRESS,5);//写TX节点地址
NRF24L01_Write_Buf(W_REGISTER | RX_ADDR_P0,(u8*)RX_ADDRESS,5); //设置TX节点地址,主要为了使能ACK
NRF24L01_Write_Reg(W_REGISTER | EN_AA,0x01); //使能通道0的自动应答
NRF24L01_Write_Reg(W_REGISTER | EN_RXADDR,0x01); //使能通道0的接收地址
NRF24L01_Write_Reg(W_REGISTER | SETUP_RETR,0x1f);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:16次
NRF24L01_Write_Reg(W_REGISTER | RF_CH,40); //设置RF通道为40
NRF24L01_Write_Reg(W_REGISTER | RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启
NRF24L01_Write_Reg(W_REGISTER | CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
NRF24L01_CE=1;//CE为高,10us后启动发送
}
//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了
void NRF24L01_RX_Mode(void)
{
NRF24L01_CE=0;
NRF24L01_Write_Buf(W_REGISTER | RX_ADDR_P0,(u8*)RX_ADDRESS,5);//写RX节点地址
NRF24L01_Write_Reg(W_REGISTER | EN_AA,0x01); //使能通道0的自动应答
NRF24L01_Write_Reg(W_REGISTER | EN_RXADDR,0x01);//使能通道0的接收地址
NRF24L01_Write_Reg(W_REGISTER | RF_CH,40); //设置RF通信频率
NRF24L01_Write_Reg(W_REGISTER | RX_PW_P0,32);//选择通道0的有效数据宽度
NRF24L01_Write_Reg(W_REGISTER | RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
NRF24L01_Write_Reg(W_REGISTER | CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
NRF24L01_CE = 1; //CE为高,进入接收模式
}
文件24L01.h
#ifndef _24L01_H
#define _24L01_H
#include "sys.h"
void NRF24L01_TX_Mode(void);
void NRF24L01_RX_Mode(void);
u8 NRF24L01_RxPacket(u8 *rxbuf);
u8 NRF24L01_TxPacket(u8 *txbuf);
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len);
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len);
u8 NRF24L01_Read_Reg(u8 reg);
u8 NRF24L01_Write_Reg(u8 reg,u8 value);
u8 NRF24L01_Check(void);
void init_24L01();
//********************************************
//下面是24L01的端口控制线
//注意;发送板和接收板这3个引脚是不一样的
#define NRF24L01_CE PGout(8) //24L01片选信号 ,0=使能,1=不使能,
#define NRF24L01_CSN PGout(7) //SPI片选信号 0=SPI使能,1=SPI不使能,
#define NRF24L01_IRQ PGin(6) //IRQ主机数据输入,24L01的中断口,中断产生时本中断线被拉低
//***********************************************************
//下面是24L01模块的指令和寄存器地址,是直接从数据手册拷贝过来的
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define R_RX_PAYLOAD 0x61
#define W_RX_PAYLOAD 0xa0
#define FLUSH_TX 0xe1
#define FLUSH_RX 0xe2
#define REUSE_TX_PL 0xe3
#define NOP 0xff
//这几个其实就是状态寄存器里面的状态位,为了计算方便定义为宏
#define MAX_TX 0x10 //达到最大发送次数中断
#define TX_OK 0x20 //TX发送完成中断
#define RX_OK 0x40 //接收到数据中断
//寄存器地址
#define CONFIG 0X00
#define EN_AA 0X01
#define EN_RXADDR 0X02
#define SETUP_AW 0X03
#define SETUP_RETR 0X04
#define RF_CH 0X05
#define RF_SETUP 0X06
#define STATUS 0X07
#define OBSERVE_TX 0X08
#define CD 0X09
#define RX_ADDR_P0 0X0A
#define RX_ADDR_P1 0X0B
#define RX_ADDR_P2 0X0C
#define RX_ADDR_P3 0X0D
#define RX_ADDR_P4 0X0E
#define RX_ADDR_P5 0X0F
#define TX_ADDR 0X10
#define RX_PW_P0 0X11
#define RX_PW_P1 0X12
#define RX_PW_P2 0X13
#define RX_PW_P3 0X14
#define RX_PW_P4 0X15
#define RX_PW_P5 0X16
#define FIFO_STATUS 0X17
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#define NOP 0xff
#endif
对于接收板,只有Main.c不一样,其他的四个文件大部分都是一样的(除了控制线的IO口不一样),所以这里只放接收板的main.c文件
#include "sys.h"
#include "delay.h"
#include "spi_24l01.h"
#include "24L01.h"
#define LED(value) GPIO_WriteBit(GPIOA,GPIO_Pin_8,value)
/************************************************************
功能;LED闪烁,间隔为100ms
其中LED接在PB5上,低电平有效
****************************************************************/
void init_led();
int main(void)
{
u8 buf_data[33]={0};//用来存放临时数据
delay_init(); //延时函数初始化
init_led(); //LED初始化
init_24L01();
LED(NRF24L01_Check());
NRF24L01_RX_Mode();
while(1)
{
if(NRF24L01_RxPacket(buf_data) == 0)
{
LED(buf_data[10]);
}
else
{
delay_us(200);
}
}
}
void init_led()
{
GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIOB和GPIOE时钟
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;//PB5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//翻转速度=10MHZ
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_WriteBit(GPIOA,GPIO_Pin_5,1);//初始化输出1吧
}
举报