物联网开发(高级):通信协议及高级外设使用

一、I2C总线通信

I2C简介

I2C总线,两线式的串行总线。
特征:
两条总线线路:SDA(数据线),SCL(时钟线),属于同步通信
I2C总线上每一个设备都可以是主设备也可以是从设备(一主多从),每一个设备都有一个唯一地址
传输速率标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。
I2C总线上的主设备与从设备之间以字节(8位)为单位进行单双工的数据传输
物理拓扑图:
一般芯片作为主设备,其他设备作为从设备
接上拉电阻原因:当I2C不工作时,默认处于高电平状态
在这里插入图片描述
总线协议:
空闲状态:SDA、SCL保持高电平
起始信号:SCL高电平,SDA由高电平转为低电平
结束信号:SCL高电平,SDA由低电平转为高电平
在这里插入图片描述
数据传输:
数据传输以字节为单位(8位),数据只有在SCL为高电平采样,在SCL低电平时可以修改电平。
主设备传输数据:首先指定设备地址+1位操作位(读1/写0),SCL默认低电平回应,之后传输发送数据,SCL1位应答
在这里插入图片描述
读写操作示意图:
主设备向从设备写:
在这里插入图片描述
主设备向从设备读:
在这里插入图片描述
主设备读从设备的某个寄存器
在这里插入图片描述
软件模拟I2C和硬件控制产生I2C(基本上所有芯片都会自带这个功能)。

EEPROM(AT24C01为例)

EEPROM,带电可擦可编程只读存储器,掉电不丢失存储芯片,常用来存储一些配置信息

ATX芯片引脚图

在这里插入图片描述
在这里插入图片描述
设备地址:
在这里插入图片描述
前四位固定为1010,低三位取决A0-A2的值

一个字节写时序:
在这里插入图片描述
读时序:
在这里插入图片描述

EEPROM读写实例

#define Waddr 0XA0
#define Raddr 0XA1
uint8_t Wbuf[]="EEPROM is OK";
uint8_t Rbuf[20]={0};
/******************************************************************************************************/

/*单字节写*/
void EEPROM_WRITE(uint8_t Memaddr,uint8_t* Wbuf,uint8_t len){
		for(int i=0;i<len;i++){
			while(HAL_I2C_Mem_Write(&hi2c1, Waddr, Memaddr, I2C_MEMADD_SIZE_8BIT, Wbuf, 1, 100)!=HAL_OK);
			Memaddr++;
			Wbuf++;
		}
		return;
}
/*随机读*/
void EEPROM_READ(uint8_t Memaddr,uint8_t* Rbuf,uint8_t len){

		while(HAL_I2C_Mem_Read(&hi2c1,Raddr,Memaddr,I2C_MEMADD_SIZE_8BIT,Rbuf,len,100)!=HAL_OK);
		return;
}
/******************************************************************************************************/
	EEPROM_WRITE(0,Wbuf,sizeof(Wbuf));
	
	HAL_Delay(500);
	
	EEPROM_READ(0,Rbuf,sizeof(Wbuf));

	printf("READ:%s\n",Rbuf);

二、SPI总线协议

SPI简介

定义:全双工三线同步串行总线,采用主从模式(一主多从)。
拓扑结构:
SCLK:时钟线,用于同步
MISO(主机数据输入,从机数据输出):主机接受数据,从机发送数据
MOSI(主机数据输入,从机数据输入):主机发送数据,从机接受数据
/SS(片选引脚):确定连接设备(默认低电平使能)
在这里插入图片描述
SPI总线协议:
在这里插入图片描述
起始信号:NSS信号由高变低,设备使能,为起始信号
结束信号:NSS信号由低变高,设备失能,为结束信号
数据传输:SCL用于时钟同步,MISO和MOSI用于主机和从机之间数据传输,注意:输入输出是同时进行的,如果仅仅是只读或者只写,都需要进行完成的读写过程
在这里插入图片描述
主机将数据通过SPI数据寄存器发送到从机的SPI数据寄存器,与此同时,从机的也将数据通过SPI数据寄存器发送给主机的SPI数据寄存器,两个过程是同时进行的。
如果主机只想要读,那么他需要任意发送一个字符(一般是0)给从机,从机忽略主机发送的数据即可,主机可以一直读。
如果主机只想要写,那么他需要将写的数据发送给从机,从机接受数据,与此同时将数据发送给主机,主句忽略接受的数据即可。
即不管是只读或者只写,都会经历一个完整的收发过程
SPI四种通信模式(根据从机模式来设置)
时钟极性CPOL : 设置时钟空闲时的电平
当CPOL = 0 ,SCK引脚在空闲状态保持低电平;
当CPOL = 1 ,SCK引脚在空闲状态保持高电平。
时钟相位CPHA :设置数据采样时的时钟沿
当 CPHA=0 时,MOSI或 MISO 数据线上的信号将会在 SCK时钟线的奇数边沿被采样
当 CPHA=1时, MOSI或 MISO 数据线上的信号将会在 SCK时钟线的偶数边沿被采样
在这里插入图片描述
总结表:
在这里插入图片描述notes:SPI控制器可以设置数据帧的长度(8/16位),也可以配置MSB(高位在前,默认使用)或者 LSB(低位在前)模式

串行FLASH_W25QXX介绍

flash:fflash又称作闪存,掉电不丢失,和EEPPROM一样是存储元件,但是EEPROM存储容量小,flash存储容量大,且进行一大片的擦写。EEPROM容量小,可以进行单字节擦写。
SPI_W25QXX介绍
W25Q256JV阵列被组织成131,072个可编程页,每个页256字节。每次最多可编程256字节。页可以以16为一组(4KB扇区擦除)、128为一组(32KB块擦除)、256为一组(64KB块擦除)或整个芯片(芯片擦除)进行擦除。
硬件连线:
在这里插入图片描述
CE:片选引脚,可以接普通的gpio引脚,而可以不使用SPI提供的引脚
SI_IO0(MOSI):主机输出,从机输入
SO_IO1(MISO):主机输入,从机输出
CLK:时钟引脚
WP:写保护引脚
HOLD:可用于暂停通讯,可不使用

控制指令:

在这里插入图片描述
读取设备ID指令(90)

通常在调试的情况下用到,以此来确定设备是否可用
在这里插入图片描述
写使能命令(06H)
在向flash写数据时,首先要使能写操作
在这里插入图片描述
扇区擦除(20H)
flash存储器特性,决定他只能把原来的1改为0,而不能执行将0写为1的操作,因此在写入之前需要对目标存储矩阵进行擦除
在这里插入图片描述
读状态寄存器(05H)
使用前需要打开写使能,flash向存储矩阵写入或者擦除操作时,需要耗费一定的时间,因此需要判断这些操作是否已经完成,因此只需要判断状态寄存器最后一位的值。(1表示忙碌,0表示空闲)
在这里插入图片描述
读数据(03H)
,读数据可以从存储器中依次读出1个或者多个字节,每传输一个字节地址都会自动递增,只要时钟继续传输,可以不断读取存储器中的数据。
在这里插入图片描述

写数据(02H)(page program页编程)
页编程可以在擦除的存储单元写入256个字节。如果超过页地址,那么寻址将会自动的切换到页开头,进行覆盖。
也变成可能存在的一些问题:
页(256个字节)->扇区->块(段)
当写到一个新的扇区的时候,需要重新发起一个页编程信号才能继续写入
在这里插入图片描述

SPI读写flash实例

w25q.h

#ifndef _WX25Q_H_
#define _WX25Q_H_

#include "stm32f4xx_hal.h"

#define sFLASH_ReadDeviceID 				0X90
#define sFLASH_WriteEnable 	 				0X06
#define sFLASH_Earse								0X20
#define sFLASH_ReadRegrestStatus		0X50
#define sFLASH_ReadDate							0X03
#define sFLASH_PageWrite						0X02

#define sFLASH_DUMMY_BYTE						0X00
#define sFLASH_BUSY_FLAG						0X01
#define sFLASH_PAGE_SIZE						0X100

#define sFLASH_CS_LOW()							HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_RESET)
#define sFLASH_CS_HIGH()						HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_SET)

uint8_t sFLASH_SendByte(uint8_t byte);
uint16_t sFLASH_ReadID(void);
void sFLASH_Sector_Earse(uint32_t Sector_Addr);
void sFLASH_WaitForEnd(void);
void sFLASH_Read_Data(uint8_t* Rxbuff,uint32_t ReadAddr,uint32_t len);
void sFLASH_Write_Enable(void);
void sFLASH_Write_Page(uint8_t *Txbuf,uint32_t  WriteAddr,uint32_t len);
void sFLASH_Write_Buff(uint8_t * txbuf,uint32_t txaddr,uint32_t len);
#endif

w25q.c

#include "wx25q.h"

extern SPI_HandleTypeDef hspi5;
/*读写一个字节函数*/
uint8_t sFLASH_SendByte(uint8_t byte){
		uint8_t tx_data=byte;
		uint8_t rx_data=0;
		HAL_SPI_TransmitReceive(&hspi5, &tx_data, &rx_data, 1,1000);

		return rx_data;
}
uint16_t sFLASH_ReadID(void){
	uint8_t temp1=0;
	uint8_t temp2=0;
	uint16_t device_id=0;
	sFLASH_CS_LOW();
	sFLASH_SendByte(sFLASH_ReadDeviceID);
	sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	/*接收ID*/
	temp1=sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	temp2=sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	
	device_id=temp1<<8 | temp2;
	sFLASH_CS_HIGH();
	return device_id;
}

void sFLASH_WaitForEnd(void){
	uint8_t Busy_Flag=0;
	sFLASH_CS_LOW();
	
	sFLASH_SendByte(sFLASH_ReadRegrestStatus);
	do{
		Busy_Flag=sFLASH_SendByte(sFLASH_DUMMY_BYTE);
	}while(Busy_Flag & sFLASH_BUSY_FLAG);
	
	sFLASH_CS_HIGH();

}
void sFLASH_Write_Enable(void){
	sFLASH_CS_LOW();
	sFLASH_SendByte(sFLASH_WriteEnable);
	sFLASH_CS_HIGH();
}
void sFLASH_Sector_Earse(uint32_t Sector_Addr){
	sFLASH_Write_Enable();
	sFLASH_CS_LOW();
	sFLASH_SendByte(sFLASH_Earse);
	sFLASH_SendByte((Sector_Addr>>16) & 0xff);
	sFLASH_SendByte((Sector_Addr>>8) 	& 0xff);
	sFLASH_SendByte((Sector_Addr>>0) 	& 0xff);
	sFLASH_CS_HIGH();
	
	sFLASH_WaitForEnd();

}
void sFLASH_Read_Data(uint8_t* Rxbuff,uint32_t ReadAddr,uint32_t len){
		
		sFLASH_CS_LOW();
		sFLASH_SendByte(sFLASH_ReadDate);
		sFLASH_SendByte((ReadAddr>>16) & 0xff);
		sFLASH_SendByte((ReadAddr>>8)	 & 0xff);
		sFLASH_SendByte((ReadAddr>>0)  & 0xff);
		while(len--){
			*Rxbuff=sFLASH_SendByte(sFLASH_DUMMY_BYTE);
			Rxbuff++;
		}
	
		sFLASH_CS_HIGH();	
}
void sFLASH_Write_Page(uint8_t *Txbuf,uint32_t WriteAddr,uint32_t len){
		if(len>sFLASH_PAGE_SIZE){
			len=sFLASH_PAGE_SIZE;
			printf("the number of data is over a page\n");
		}
	
		sFLASH_Write_Enable();
	
		sFLASH_CS_LOW();
		sFLASH_SendByte(sFLASH_PageWrite);
		sFLASH_SendByte((WriteAddr>>16) & 0xff);
		sFLASH_SendByte((WriteAddr>>8) 	& 0xff);
		sFLASH_SendByte((WriteAddr>>0) 	& 0xff);
		while(len--){
			sFLASH_SendByte(*Txbuf);
			Txbuf++;
		}
	
		sFLASH_CS_HIGH();
		
		sFLASH_WaitForEnd();
}

/*解决跨页写的问题*/
void sFLASH_Write_Buff(uint8_t * txbuf,uint32_t txaddr,uint32_t len){
		uint8_t Numberofpage=0,Numberofbyte=0,offset=0,count=0;
		offset=txaddr % sFLASH_PAGE_SIZE;
		count=sFLASH_PAGE_SIZE - offset;
	
		if(offset && (len>count)){
			sFLASH_Write_Page(txbuf,txaddr,count);
			txbuf+=count;
			txaddr+=count;
			Numberofpage=(len-count)/sFLASH_PAGE_SIZE;
			Numberofbyte=(len-count)%sFLASH_PAGE_SIZE;
		}

		while(Numberofpage--){
			sFLASH_Write_Page(txbuf,txaddr,sFLASH_PAGE_SIZE);
			txaddr+=sFLASH_PAGE_SIZE;
			txbuf+=sFLASH_PAGE_SIZE;
		}
		
		if(Numberofbyte){
			sFLASH_Write_Page(txbuf,txaddr,Numberofbyte);
		}
		
		sFLASH_WaitForEnd();

}

main.c

int main(void)
{
  /* USER CODE BEGIN 1 */
	uint16_t device_id=0;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI5_Init();
  MX_USART6_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("this is spi test\n");
	
	printf("读取设备id测试\n");
	device_id=sFLASH_ReadID();
	printf("devce_id=%x\n",device_id);
	
	printf("扇擦除测试\n");
	sFLASH_Sector_Earse(0);
	sFLASH_Read_Data(rxbuf,0,4096);
	for(int i=0;i<strlen((const char *)rxbuf);i++){
		printf("%x ",rxbuf[i]);
	}
	printf("\n");
	
	printf("写数据测试\n");
	for(int i=0;i<1000;i++){
		txbuf[i]=0x55;
	}
	
	sFLASH_Write_Page(txbuf,0,200);
	HAL_Delay(500);//延迟才能接收到数据,否则出现读不到数据情况(暂时不知道原因)
	sFLASH_Read_Data(rxbuf,0,200);
	for(int i=0;i<200;i++){
		printf("%x ",rxbuf[i]);
	}
	printf("\n");
	
	printf("跨页写数据测试\n");
	sFLASH_Write_Buff(txbuf,200,1000);
	HAL_Delay(500);//延迟才能接收到数据,否则出现读不到数据情况(暂时不知道原因)
	sFLASH_Read_Data(rxbuf,200,1000);
	for(int i=0;i<1000;i++){
		printf("%x ",rxbuf[i]);
	}
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

三、单总线传感器(略)

单总线传感器(红外遥控,温度传感器),只需要一根I/O线即可完成数据的传输,主要是依靠一根性传输高低电平的时间长短来规定协议,这些元器件基本上购买产品时已经有配套的驱动代码,只需要略微改动一下即可使用,因此本章节不在具体叙述。后期有使用在来完善这部分。

四、FSMC扩展SRAM(略)

常用存储器介绍

在这里插入图片描述
DRAM(动态随机存储器)
有电荷表示1,无电荷表示0,需要定期刷新
同步和异步DRAM(有无CLK),同步速率更快,因此采用更多,称为SDRAM;
DDR SDRAM:为了提高速率而设计,由原来的单边沿读取数据变成双边沿读取数据

SRAM(静态)随机存储器
内部结构为锁存器的结构,能保持当前状态,因此不需要定时刷新
在这里插入图片描述
非易失性存储器

ROM:
在这里插入图片描述
flash:
在这里插入图片描述

SDRAM(W9825G6KH-6芯片)

W9825G6KH是一种高速同步动态随机存取存储器(SDRAM),由4M字x 4组x 16位组成。

配置实例:
数据手册PH7对应为SDCKE1,所以是选择SDRAM2.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用SDRAM扩展内存
1.使能GPIO,设置引脚模式
2.SDRAM,初始化SDRAM外设在这里插入图片描述

  /* USER CODE BEGIN FMC_Init 2 */
	/*1.提供时钟信号*/
	FMC_SDRAM_CommandTypeDef Command;
	Command.CommandMode=FMC_SDRAM_CMD_CLK_ENABLE;
	Command.AutoRefreshNumber=1;
	Command.CommandTarget=FMC_SDRAM_CMD_TARGET_BANK2;
	Command.ModeRegisterDefinition=0;
	
	HAL_SDRAM_SendCommand(&hsdram2, &Command, 0XFFFF);
	
	/*2.至少延迟200us*/
	HAL_Delay(1);
	/*3.引脚预充电*/
	Command.CommandMode=FMC_SDRAM_CMD_PALL;
	Command.AutoRefreshNumber=1;
	Command.CommandTarget=FMC_SDRAM_CMD_TARGET_BANK2;
	Command.ModeRegisterDefinition=0;
	
	HAL_SDRAM_SendCommand(&hsdram2, &Command, 0XFFFF);
	/*4.插入8个自动刷新周期*/
	Command.CommandMode=FMC_SDRAM_CMD_AUTOREFRESH_MODE;
	Command.AutoRefreshNumber=8;
	Command.CommandTarget=FMC_SDRAM_CMD_TARGET_BANK2;
	Command.ModeRegisterDefinition=0;
	
	HAL_SDRAM_SendCommand(&hsdram2, &Command, 0XFFFF);
	
	/*5.编程SDRAM加载模式寄存器*/
	
	Command.CommandMode=FMC_SDRAM_CMD_LOAD_MODE;
	Command.AutoRefreshNumber=1;
	Command.CommandTarget=FMC_SDRAM_CMD_TARGET_BANK2;
	Command.ModeRegisterDefinition=0X230;
	
	HAL_SDRAM_SendCommand(&hsdram2, &Command, 0XFFFF);
	/*6.配置STM32FMC自动刷新周期*/
	FMC_SDRAM_ProgramRefreshRate((FMC_SDRAM_TypeDef *)&hsdram2, 703);
  /* USER CODE END FMC_Init 2 */

5、编程加载寄存器 Command.ModeRegisterDefinition参数解释
在这里插入图片描述
6。配置STM32FMC自动刷新周期

在这里插入图片描述
90*7.81-20

五、FSMC-LCD显示屏(略)

六、电源管理

电源管理结构框图

在这里插入图片描述
1.备份域
拥有纽扣电池给电路供电,锁存器结构,主电源供电时,纽扣电池不要供电,主电源断开,纽扣电池供电,保持电路运行
2.调压器供电电路
为调压器以及待机电路以外的电路供电
3.ADC电源电路
ADC精度要求,独立供电

工作模式

运行模式
全功率供电
停止模式
关闭所有时钟,所有外设停止工作,但是保留内核电源供电,因此内核寄存器信息未丢失
在这里插入图片描述

睡眠模式
仅关闭内核时钟,内核停止运行,但是外设正常工作
在这里插入图片描述
待机模式
关闭所有电源,唤醒相当于复位
在这里插入图片描述

相关配置函数

睡眠模式
HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry);//进入睡眠模式,中断唤醒
但是systick始终中断未关闭,因此会一直唤醒,因此还需要关闭systick中断
void HAL_SuspendTick(void)//关闭systick中断
void HAL_ResumeTick(void)//开启中断

停止模式
HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry)//进入停止模式
关闭了时钟,因此不需要关闭systick,但是唤醒后默认是HSI(内部低速时钟,因此需要重新指定系统工作时钟(HSE,PLL))

待机模式
1.使能wakeup按键
void HAL_PWR_DisableWakeUpPin(uint32_t WakeUpPinx);
2.清除标志位(唤醒)
3.进入待机模式
void HAL_PWR_EnterSTANDBYMode(void)

七、STM32-SDIO外设(SD卡)使用(略)

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值