STM32的SPI及IIC

SPI配置方法

1.SPI简介
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。

SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
在这里插入图片描述
2.SPI工作原理总结
①硬件上为4根线。
②主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
③串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
④外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。

3.配置方法
1)时钟使能。GPIO时钟使能RCC->APB2ENR,SPI时钟使能RCC->APB2ENR设置。(为什么还要连接GPIO时钟,参见STM32参考手册8.1.4节。手册上这么说的:对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。)

RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();

2)配置GPIO工作模式。配置GPIO片选,由软件管理(即自定义引脚),推挽输出,上拉;配置SPI引脚SCK、MOSI、MISO所用到的引脚为复用功能;GPIOX->CR1 GPIOX->ODR;

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

3)SPI设置工作模式。通过配置SPIx->CR1来设置SPI 的工作模式。配置工作模式为全双工,主机模式,SCK闲时电平为高,第二个时钟沿(上升沿)采样数据,内部从机选择软件管理模式。设置SPI的时钟频率(最大18MHZ),设置数据格式(MSB在前还是LSB在后),内部从机选择设置为主机(置1)。

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

4)使能SPI,启动传输。

 void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

5)SPI传输数据。

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

6)查看SPI传输状态。

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);

总结

void SPI1_Init(void)
{	 
  GPIO_InitTypeDef  GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOA时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
 
  //PB3,4,5初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
	
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为SPI1
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为SPI1
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为SPI1
 
//这里只针对SPI口初始化
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
 
 
  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_High;		//串行同步时钟的空闲状态为高电平
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号由SSI控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为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外设
 
  SPI1_ReadWriteByte(0xff);//启动传输		 
}   
//SPI1速度设置函数
//SPI速度=FAPB2/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
//fAPB2时钟一般为84Mhz
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
	SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率
	SPI1->CR1|=SPI_BaudRatePrescaler;	//设置SPI1的速度
	SPI_Cmd(SPI1,ENABLE); //使能SPI1
} 
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{		 			 
 
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空 
	
     SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte数据
		
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte 
 
     return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
 		    
}

IIC配置方法

1.IIC功能

I2C模块接收和发送数据,并将数据从串行转换成并行,或并行转换成串行。可以开启或禁止中断。接口通过数据引脚(SDA)和时钟引脚(SCL)连接到I2C总线。允许连接到标准(高达100kHz)或快速(高达400kHz)的I2C总线。

在这里插入图片描述

  1. I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
    开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
    结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
    应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

#include "iic.h"
 
//*****************************************************
// IIC2初始化函数:初始化STM32硬件自带的IIC2
//                 IIC2_SCL对应GPIO.B10
//                 IIC2_SDA对应GPIO.B11
//*****************************************************
void I2C2_Init(void)
{
	/*GPIO与IIC初始化结构体*/
	GPIO_InitTypeDef GPIO_InitStructure;
	I2C_InitTypeDef I2C_InitStructure;
	
	/*GPIO与IIC时钟使能*/
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );		//GPIOB时钟使能
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C2, ENABLE );		//IIC2时钟使能
 
	/*初始化GPIO*/
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10 | GPIO_Pin_11;	//初始化GPIO.B10(IIC2_SCL),GPIO.B11(IIC2_SDA)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//最高输出速度50Hz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;				//输入输出模式为复用功能开漏输出
	GPIO_Init( GPIOB, &GPIO_InitStructure );					//根据GPIO初始化结构体初始化GPIOB
	
	/*初始化IIC2*/
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;					//设置为IIC模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;			//设置IIC的占空比,低电平除以高电平值为2
	I2C_InitStructure.I2C_OwnAddress1 = AT24C02_ADDRESS;		//指定第一个设备的地址为7位地址
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;					//使能ACK信号
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//指定7位地址
	I2C_InitStructure.I2C_ClockSpeed = 400000;					//时钟频率,必须小于等于400KHz
	
	I2C_Cmd( I2C2, ENABLE );   									//使能IIC2
	I2C_Init( I2C2, &I2C_InitStructure );						//根据IIC初始化结构体初始化IIC2
	
	/*允许一字节一应答模式*/
	I2C_AcknowledgeConfig( I2C2, ENABLE );  					//使能IIC2应答状态  
}
 
 
//*****************************************************
// IIC2写函数   :往IIC设备写入一个BYTE的data
// id           :IIC设备的id
// write_address:数据要写入IIC设备的地址
// data         :需要写入的数据
//*****************************************************
void I2C2_WriteByte( u8 id, u8 write_address, u8 data )
{
	while (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));								//当IIC2状态为BUSY时,一直停在这里死循环
	
	/*发送START之后要等待,意味着START条件被正确释放,此时IIC总线上没有其它外设*/
	I2C_GenerateSTART( I2C2, ENABLE );											//一旦不为BUSY,跳出循环,产生START条件	
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));				//如果主机被选中(死循环等待ACK信号)
	 
	/*主机发送地址联系从机后,主机要等待从机的ACK,如果主机设置为发射,*/
	/*则收到ACK信号时 I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED 被置1 */
	I2C_Send7bitAddress( I2C2, id, I2C_Direction_Transmitter );					//发送从机地址以选择从机,主机为发送模式
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));	//如果主机发射模式被选中(死循环等待ACK信号)
 
	/*通讯建立(生成START过程与从机地址被承认)以后,主机发送数据,*/
	/*如果数据被转移并输出IIC总线,则 I2C_EVENT_MASTER_BYTE_TRANSMITTED 被置1*/
	I2C_SendData( I2C2, write_address );										//将write_address,即要写的地址通过IIC2发送出去	
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));			//如果地址已经从IIC2成功发射出去(死循环等待ACK信号)
 
	/*往寄存器发送数据data*/
	I2C_SendData( I2C2, data );
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));			
 
	I2C_GenerateSTOP( I2C2, ENABLE );											//IIC2产生STOP条件
 
	/*循环确保IIC设备与主机的通讯建立*/
	do
	{                
		I2C_GenerateSTART( I2C2, ENABLE );										//IIC2产生START条件
		I2C_Send7bitAddress( I2C2, AT24C02_ADDRESS, I2C_Direction_Transmitter );//发送EEPROM地址0XA0
	}
	while (!(I2C_ReadRegister(I2C2,I2C_Register_SR1)&0x0002));					//读取IIC2->SR1的值,当IIC2->SR1[1] = 1时跳出循环
																				//此时地址发送结束
 
	I2C_ClearFlag( I2C2, I2C_FLAG_AF );											//IIC2清除应答错误标志位   
	I2C_GenerateSTOP( I2C2, ENABLE );  											//IIC2产生STOP条件
}
 
 
//*****************************************************
// IIC2读函数   :从IIC设备的指定地址中读出一个BYTE的数据
// id           :IIC设备的id
// read_address :需要读取IIC数据的设备的地址
// 返回值       :读出的数据
//*****************************************************
u8 I2C2_ReadByte( u8 id, u8 read_address )
{  
	u8 data;  
	/***主机发送地址***/
	while (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));								//当IIC2状态为BUSY时,一直停在这里死循环
	
	/*发送START之后要等待,意味着START条件被正确释放,此时IIC总线上没有其它外设*/
	I2C_GenerateSTART( I2C2, ENABLE );											//一旦不为BUSY,跳出循环,产生START条件	
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));				//如果主机被选中(死循环等待ACK信号)
 
	/*主机发送地址联系从机后,主机要等待从机的ACK,如果主机设置为发射,*/
	/*则收到ACK信号时 I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED 被置1 */
	I2C_Send7bitAddress( I2C2, id, I2C_Direction_Transmitter );					//发送从机地址以选择从机,主机为发送模式
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));	//如果主机发射模式被选中(死循环等待ACK信号)
 
	/*通讯建立(生成START过程与从机地址被承认)以后,主机发送数据,*/
	/*如果数据被转移并输出IIC总线,则 I2C_EVENT_MASTER_BYTE_TRANSMITTED 被置1*/
	I2C_Cmd( I2C2, ENABLE );													//使能IIC2
	I2C_SendData( I2C2, read_address );  										//将write_address,即要读的地址通过IIC2发送出去
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));			//如果地址已经从IIC2成功发射出去(死循环等待ACK信号?
	  
	I2C_GenerateSTART( I2C2, ENABLE );											//产生START条件
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));				//如果主机被选中(死循环等待ACK信号)
	
	
	/***主机接收数据***/
	I2C_Send7bitAddress( I2C2, id, I2C_Direction_Receiver );					//主机设置为接收模式
	while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));		//如果主机接收模式被选中(死循环等待ACK信号)
 
	I2C_AcknowledgeConfig( I2C2, DISABLE );										//失能IIC2的应答状态
	I2C_GenerateSTOP( I2C2, ENABLE );											//产生STOP条件
	while (!(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)));			//如果主机接收数据成功(死循环等待ACK信号)
		  
	data = I2C_ReceiveData(I2C2);												//返回IIC2接收的数据,赋给temp
	I2C_AcknowledgeConfig( I2C2, ENABLE );										//再一次使能IIC2的应答状态
 
	return data;																//返回接收到的数据
}

SPI驱动OLED屏幕

#include "OLED.h"
#include "oledfont.h"


void delay(int ms)    //延时毫秒
{
	delay_init(72);
	delay_ms(ms);
}



//SPI时序为在时钟的上升沿把数据写入

void OLED_WR_Byte(unsigned char dat,unsigned char cmd)
{	
	unsigned char i;			  
	if(cmd)                      //判断是写命令还是数据,从而拉低对应的IO引脚
	  OLED_DC_Set();
	else 
	  OLED_DC_Clr();		  
	OLED_CS_Clr();               //拉低片选线
	for(i=0;i<8;i++)
	{			  
		OLED_SCLK_Clr();         //拉低时钟线,数据会在时钟的上升沿把数据写入
		if(dat&0x80)             //从高位开始传送数据
			{
				OLED_SDIN_Set();
			}
		else
		   OLED_SDIN_Clr();
		
		OLED_SCLK_Set();
		dat<<=1;   
	}				 		  
	OLED_CS_Set();
	OLED_DC_Set();   	  
} 



//开启OLED显示    
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}



//关闭OLED显示     
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}		


//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)  
{  
	unsigned char i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
		OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
		OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)
			OLED_WR_Byte(0,OLED_DATA);     //每一页有128列,8行,每写入一个字节会一次性写入8行,每写入一列后再写入的时候会自动写入下一位
	} //更新显示
}


//设置要在那一列的哪一列画点,所谓的画点就是让屏幕上某一个位置的点亮或者不亮,因为每次至少写入一个字节,所以一次点亮或关闭的至少是竖着的8个点
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WR_Byte(0xb0+y,OLED_CMD);                 //设置页地址
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);     //设置列地址高4位
	OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD);          //设置列地址低4位
}   	   


//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~7
//mode:0,反白显示;1,正常显示				 
//size:选择字体 16/12 
//char为要显示的字符,这个字符要是ASCII码中的字符

//下面这个函数是在指定位置显示ASCII码的函数
//每一个ASCII码占16*8个点
//x, y指的是要写入的字符左上角是在哪一列,哪一页,x对应列,y对应页
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr)
{      	
		unsigned char c=0,i=0;	
		c=chr-' ';                            //得到偏移后的值			
		if(x>Max_Column-1)
		{
			x=0;
			y=y+2;
		}
		
		if(SIZE ==16)
			{
				OLED_Set_Pos(x,y);	//在第y页,第x列开始写字符
				
				for(i=0;i<8;i++)    //从第y页,第x列开始写第一个字符,连续写8列
					OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
				
				OLED_Set_Pos(x,y+1); //因为字体垂直方向为16,水平方向为8,所以每写一个字符要设计到两页
				
				for(i=0;i<8;i++)
					OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
			}
		else 
		{	
			OLED_Set_Pos(x,y+1);
			
			for(i=0;i<6;i++)
				OLED_WR_Byte(F6x8[c][i],OLED_DATA);		
		}
}

IIC驱动mpu6050

#include "mpu6050.h"

uint8_t buffer[14];

void MPU6050_setClockSource(uint8_t source){
    MPU_IICwriteBits(0x68, 0x6B, 2, 3, source);
}

void MPU6050_setFullScaleGyroRange(uint8_t range) {
    MPU_IICwriteBits(0x68, 0x1B, 4, 2, range);
}

void MPU6050_setFullScaleAccelRange(uint8_t range) {
    MPU_IICwriteBits(0x68, 0x1C, 4, 2, range);
}


void MPU6050_resetEnabled(uint8_t enabled) {
    MPU_IICwriteBit(0x68, 0x6B, 7, enabled);
	Delay_ms(100);
}


void MPU6050_set_Gyro_Accel_outRange(uint8_t range) {
    MPU_IICwriteBits(0x68, 0x6C, 5, 6, range);
}

void MPU6050_set_SMPRT_DIV(uint8_t range) {
	  MPU_IICwriteByte(0x68,0x19,range);
}

void MPU6050_set_Gyro_DLPF_outRange(uint8_t range) {
    MPU_IICwriteBits(0x68, 0x1A, 2, 3, range);
}


void MPU6050_INT_Disabled(uint8_t enabled) {
    MPU_IICwriteBit(0x68, 0x38, 0, enabled);
}


void MPU6050_setSleepEnabled(uint8_t enabled) {
    MPU_IICwriteBit(0x68, 0x6B, 6, enabled);
}


void MPU6050_testConnection(void) {
	
   while(buffer[0] != 0x68){
			MPU_IICreadBytes(0x68, 0x75, 1, buffer);
	 };  
}

void MPU6050_setI2CMasterModeEnabled(uint8_t enabled) {
    MPU_IICwriteBit(0x68, 0x6A, 5, enabled);
}

void MPU6050_setI2CBypassEnabled(uint8_t enabled) {
    MPU_IICwriteBit(0x68, 0x37, 1, enabled);
}


void MPU6050_initialize(void) {
	MPU_IIC_Init();             //IIC初始化
	MPU6050_testConnection();     //检测MPU6050 是否已经连接
	MPU6050_resetEnabled(1);   //复位mpu6050  需延时100ms
    MPU6050_setClockSource(2); //设置时钟
	MPU6050_set_Gyro_Accel_outRange(0);  // 设置输出三轴陀螺仪和三轴加速度数据
	MPU6050_INT_Disabled(0);    //禁止中断
	MPU6050_set_SMPRT_DIV(0);   //采样分频
    MPU6050_setFullScaleGyroRange(3);//陀螺仪最大量程 +-1000度每秒
    MPU6050_setFullScaleAccelRange(0);	//加速度度最大量程 +-2G
  	MPU6050_set_Gyro_DLPF_outRange(2);  //设置陀螺仪和加速度计的低通滤波器
    MPU6050_setSleepEnabled(0); //进入工作状态
	MPU6050_setI2CMasterModeEnabled(0);	 //不让MPU6050 控制AUXI2C
	MPU6050_setI2CBypassEnabled(1);	 //主控制器的I2C与	MPU6050的AUXI2C	直通。控制器可以直接访问HMC5883L
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值