您的位置:首页 > 财经 > 金融 > 网站建设服务器技术有哪些_免费素材网图片_西安网站推广排名_营销网站建设

网站建设服务器技术有哪些_免费素材网图片_西安网站推广排名_营销网站建设

2025/3/12 17:01:33 来源:https://blog.csdn.net/weixin_74078488/article/details/146167504  浏览:    关键词:网站建设服务器技术有哪些_免费素材网图片_西安网站推广排名_营销网站建设
网站建设服务器技术有哪些_免费素材网图片_西安网站推广排名_营销网站建设

stm32学习

三.通信

5.硬件读取I2C

硬件读取I2C的代码(main.c与软件读取相同):

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS  0xD0  //MPU6050的I2C从机地址/*** 函    数:MPU6050等待事件* 参    数:同I2C_CheckEvent* 返 回 值:无*/
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{uint32_t Timeout;Timeout = 10000;         //给定超时计数时间while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循环等待指定事件{Timeout --;          //等待时,计数值自减if (Timeout == 0)        //自减到0后,等待超时{/*超时的错误处理代码,可以添加到此处*/break;          //跳出等待,不等了}}
}/*** 函    数:MPU6050写寄存器* 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF* 返 回 值:无*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{I2C_GenerateSTART(I2C2, ENABLE);          //硬件I2C生成起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);     //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6I2C_SendData(I2C2, RegAddress);           //硬件I2C发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);   //等待EV8I2C_SendData(I2C2, Data);            //硬件I2C发送数据MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);    //等待EV8_2I2C_GenerateSTOP(I2C2, ENABLE);           //硬件I2C生成终止条件
}/*** 函    数:MPU6050读寄存器* 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* 返 回 值:读取寄存器的数据,范围:0x00~0xFF*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;I2C_GenerateSTART(I2C2, ENABLE);          //硬件I2C生成起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);     //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6I2C_SendData(I2C2, RegAddress);           //硬件I2C发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);    //等待EV8_2I2C_GenerateSTART(I2C2, ENABLE);          //硬件I2C生成重复起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);     //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);  //硬件I2C发送从机地址,方向为接收MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);  //等待EV6I2C_AcknowledgeConfig(I2C2, DISABLE);         //在接收最后一个字节之前提前将应答失能I2C_GenerateSTOP(I2C2, ENABLE);           //在接收最后一个字节之前提前申请停止条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);    //等待EV7Data = I2C_ReceiveData(I2C2);           //接收数据寄存器I2C_AcknowledgeConfig(I2C2, ENABLE);         //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作return Data;
}/*** 函    数:MPU6050初始化* 参    数:无* 返 回 值:无*/
void MPU6050_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);  //开启I2C2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //开启GPIOB的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);     //将PB10和PB11引脚初始化为复用开漏输出/*I2C初始化*/I2C_InitTypeDef I2C_InitStructure;      //定义结构体变量I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;    //模式,选择为I2C模式I2C_InitStructure.I2C_ClockSpeed = 50000;    //时钟速度,选择为50KHzI2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  //时钟占空比,选择Tlow/Thigh = 2I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;    //应答,选择使能I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //应答地址,选择7位,从机模式下才有效I2C_InitStructure.I2C_OwnAddress1 = 0x00;    //自身地址,从机模式下才有效I2C_Init(I2C2, &I2C_InitStructure);      //将结构体变量交给I2C_Init,配置I2C2/*I2C使能*/I2C_Cmd(I2C2, ENABLE);         //使能I2C2,开始运行/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);    //电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);    //电源管理寄存器2,保持默认值0,所有轴均不待机MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);    //采样率分频寄存器,配置采样率MPU6050_WriteReg(MPU6050_CONFIG, 0x06);     //配置寄存器,配置DLPFMPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);   //陀螺仪配置寄存器,选择满量程为±2000°/sMPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);   //加速度计配置寄存器,选择满量程为±16g
}/*** 函    数:MPU6050获取ID号* 参    数:无* 返 回 值:MPU6050的ID号*/
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);  //返回WHO_AM_I寄存器的值
}/*** 函    数:MPU6050获取数据* 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 返 回 值:无*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL;        //定义数据高8位和低8位的变量DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);  //读取加速度计X轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);  //读取加速度计X轴的低8位数据*AccX = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);  //读取加速度计Y轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);  //读取加速度计Y轴的低8位数据*AccY = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);  //读取加速度计Z轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);  //读取加速度计Z轴的低8位数据*AccZ = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);  //读取陀螺仪X轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);  //读取陀螺仪X轴的低8位数据*GyroX = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);  //读取陀螺仪Y轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);  //读取陀螺仪Y轴的低8位数据*GyroY = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);  //读取陀螺仪Z轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);  //读取陀螺仪Z轴的低8位数据*GyroZ = (DataH << 8) | DataL;      //数据拼接,通过输出参数返回
}

6.SPI通信

SPI(Serial Peripheral Interface):四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
同步、全双工、没有规定的速率
支持总线挂载多设备
MOSI也可能叫DI,MISO也可能叫DO,SCK也可能叫SCLK、CLK、CK
所有SPI设备的SCK、MOSI、MISO分别连在一起
主机另外引出多条SS控制线,分别接到各从机的SS引脚
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
当SS为1时,从机保持沉默,MISO为高阻态(防止一条线有多个输出,而导致的电平冲突),当SS为0时,表示主机要与该从机通信
SPI通信的逻辑是主机的数据与从机的数据进行交换,如果仅传输则无视从机输出的数据,如果仅输入则随便发送数据(一般是0x00或者0xFF)

图片消失了

其中,当SS为1时,MISO处于中间位置,表示高阻态
若CPOL=1,则空闲时,SCK为1
若CPHA=1,则SCK第一个边沿移入数据,第二个边沿移出数据(第一个数据在SS变0后移出)

7.W25Q64

W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,长应用于数据存储、字库存储(用stm32存储汉字字库比较浪费)、固件程序存储等场景
存储介质:Nor Flash(闪存)
时钟频率:80MHz/160MHz(Dual SPI)/320MHz(Quad SPI)
存储容量(24位地址):64Mbit/8MByte(有一半地址没用到)

图片消失了

最左边是普通的SPI电路
中间的多了写保护和数据保持的引脚,其中CS、HOLD、WP上面的横线表示低电平触发
右边的HOLD、WP可以当作io引脚使用,这样最多有4个引脚进行输入输出,这时的时钟频率是320MHz(Quad SPI)
W25Q64框图:

图片消失了

其中,存储空间每64kb划分一个块,块内每4kb划分一个页
存储地址最大7FFFFFh是因为一半的地址没用到
中间有个高电压发生器,这与Flash的存储原理有关
flash的存储是掉电不丢失的,存储需要较长的时间,所以右下角有个256byte的页缓存,当缓存满时,会向左侧的状态寄存器发送忙信号,使SPI暂停

Flash操作注意事项:
写入操作时:

  1. 每次写入操作前,必须先进行写使能
  2. 每个数据位只能由1改写成0,不能由0改写成1
  3. 写入数据前必须先擦除,擦除后,所有数据位变为1,所以没数据的空间存储的是FF
  4. 擦除必须按最小擦除单元进行
  5. 连续写入多字节时,最多写入一页的数据,超过页尾的数据,会回到页首覆盖写入
  6. 写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:

  1. 直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

代码:
main.c的测试代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;       //定义用于存放MID号的变量
uint16_t DID;       //定义用于存放DID号的变量uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04}; //定义要写入数据的测试数组
uint8_t ArrayRead[4];        //定义要读取数据的测试数组int main(void)
{/*模块初始化*/OLED_Init();      //OLED初始化W25Q64_Init();      //W25Q64初始化/*显示静态字符串*/OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");/*显示ID号*/W25Q64_ReadID(&MID, &DID);   //获取W25Q64的ID号OLED_ShowHexNum(1, 5, MID, 2);  //显示MIDOLED_ShowHexNum(1, 12, DID, 4);  //显示DID/*W25Q64功能函数测试*/W25Q64_SectorErase(0x000000);     //扇区擦除W25Q64_PageProgram(0x000000, ArrayWrite, 4); //将写入数据的测试数组写入到W25Q64中W25Q64_ReadData(0x000000, ArrayRead, 4);  //读取刚写入的测试数据到读取数据的测试数组中/*显示数据*/OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);  //显示写入数据的测试数组OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);   //显示读取数据的测试数组OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

实现SPI通信的代码:

#include "stm32f10x.h"                  // Device header/*引脚配置层*//*** 函    数:SPI写SS引脚电平* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);  //根据BitValue,设置SS引脚的电平
}/*** 函    数:SPI写SCK引脚电平* 参    数:BitValue 协议层传入的当前需要写入SCK的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCK为低电平,当BitValue为1时,需要置SCK为高电平*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);  //根据BitValue,设置SCK引脚的电平
}/*** 函    数:SPI写MOSI引脚电平* 参    数:BitValue 协议层传入的当前需要写入MOSI的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置MOSI为低电平,当BitValue为1时,需要置MOSI为高电平*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);  //根据BitValue,设置MOSI引脚的电平,BitValue要实现非0即1的特性
}/*** 函    数:I2C读MISO引脚电平* 参    数:无* 返 回 值:协议层需要得到的当前MISO的电平,范围0~1* 注意事项:此函数需要用户实现内容,当前MISO为低电平时,返回0,当前MISO为高电平时,返回1*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);   //读取MISO电平并返回
}/*** 函    数:SPI初始化* 参    数:无* 返 回 值:无* 注意事项:此函数需要用户实现内容,实现SS、SCK、MOSI和MISO引脚的初始化*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);     //将PA4、PA5和PA7引脚初始化为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);     //将PA6引脚初始化为上拉输入/*设置默认电平*/MySPI_W_SS(1);           //SS默认高电平MySPI_W_SCK(0);           //SCK默认低电平
}/*协议层*//*** 函    数:SPI起始* 参    数:无* 返 回 值:无*/
void MySPI_Start(void)
{MySPI_W_SS(0);    //拉低SS,开始时序
}/*** 函    数:SPI终止* 参    数:无* 返 回 值:无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);    //拉高SS,终止时序
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;     //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到for (i = 0; i < 8; i ++)      //循环8次,依次交换每一位数据{/*两个!可以对数据进行两次逻辑取反,作用是把非0值统一转换为1,即:!!(0) = 0,!!(非0) = 1*/MySPI_W_MOSI(!!(ByteSend & (0x80 >> i))); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(1);        //拉高SCK,上升沿移出数据if (MySPI_R_MISO()){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0MySPI_W_SCK(0);        //拉低SCK,下降沿移入数据}return ByteReceive;        //返回接收到的一个字节数据
}

根据SPI通信准则跟W25Q64实现通信功能的代码:

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"/*** 函    数:W25Q64初始化* 参    数:无* 返 回 值:无*/
void W25Q64_Init(void)
{MySPI_Init();     //先初始化底层的SPI
}/*** 函    数:MPU6050读取ID号* 参    数:MID 工厂ID,使用输出参数的形式返回* 参    数:DID 设备ID,使用输出参数的形式返回* 返 回 值:无*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID);   //交换发送读取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收MID,通过输出参数返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收DID高8位*DID <<= 8;         //高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); //或上交换接收DID的低8位,通过输出参数返回MySPI_Stop();        //SPI终止
}/*** 函    数:W25Q64写使能* 参    数:无* 返 回 值:无*/
void W25Q64_WriteEnable(void)
{MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE);  //交换发送写使能的指令MySPI_Stop();        //SPI终止
}/*** 函    数:W25Q64等待忙* 参    数:无* 返 回 值:无*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);    //交换发送读状态寄存器1的指令Timeout = 100000;       //给定超时计数时间while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01) //循环等待忙标志位{Timeout --;        //等待时,计数值自减if (Timeout == 0)      //自减到0后,等待超时{/*超时的错误处理代码,可以添加到此处*/break;        //跳出等待,不等了}}MySPI_Stop();        //SPI终止
}/*** 函    数:W25Q64页编程* 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray 用于写入数据的数组* 参    数:Count 要写入数据的数量,范围:0~256* 返 回 值:无* 注意事项:写入的地址范围不能跨页*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();      //写使能MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM);  //交换发送页编程的指令MySPI_SwapByte(Address >> 16);    //交换发送地址23~16位MySPI_SwapByte(Address >> 8);    //交换发送地址15~8位MySPI_SwapByte(Address);     //交换发送地址7~0位for (i = 0; i < Count; i ++)    //循环Count次{MySPI_SwapByte(DataArray[i]);   //依次在起始地址后写入数据}MySPI_Stop();        //SPI终止W25Q64_WaitBusy();       //等待忙
}/*** 函    数:W25Q64扇区擦除(4KB)* 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF* 返 回 值:无*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();      //写使能MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //交换发送扇区擦除的指令MySPI_SwapByte(Address >> 16);    //交换发送地址23~16位MySPI_SwapByte(Address >> 8);    //交换发送地址15~8位MySPI_SwapByte(Address);     //交换发送地址7~0位MySPI_Stop();        //SPI终止W25Q64_WaitBusy();       //等待忙
}/*** 函    数:W25Q64读取数据* 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回* 参    数:Count 要读取数据的数量,范围:0~0x800000* 返 回 值:无*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();        //SPI起始MySPI_SwapByte(W25Q64_READ_DATA);   //交换发送读取数据的指令MySPI_SwapByte(Address >> 16);    //交换发送地址23~16位MySPI_SwapByte(Address >> 8);    //交换发送地址15~8位MySPI_SwapByte(Address);     //交换发送地址7~0位for (i = 0; i < Count; i ++)    //循环Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //依次在起始地址后读取数据}MySPI_Stop();        //SPI终止
}

8.硬件SPI通信

stm32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
可配置8位/16位数据帧,配置高位先行、低位先行
时钟频率: f P C L K f_{PCLK} fPCLK/(2, 4, 8, 16, 32, 64, 128, 256)
支持多主机模型、主或从操作
可精简为半双工/单工通信
支持DMA
兼容I2S协议(多用于传输音频)
STM32F103C8T6硬件SPI资源:SPI1(APB2)、SPI2(APB1)

图片消失了

重要的是左上角的缓冲区和寄存器组成的无延迟交换模块,右下角的寄存器可以看手册描述

图片消失了

连续传输的实现十分复杂,代码不易封装,所以一般用的是下面的非连续传输

图片消失了

硬件实现SPI通信代码(只有初始化函数和交换字节函数有修改):

void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SPI1的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);     //将PA4引脚初始化为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);     //将PA5和PA7引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);     //将PA6引脚初始化为上拉输入/*SPI初始化*/SPI_InitTypeDef SPI_InitStructure;      //定义结构体变量SPI_InitStructure.SPI_Mode = SPI_Mode_Master;   //模式,选择为SPI主模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  //数据宽度,选择为8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //先行位,选择高位先行SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;    //SPI极性,选择低极性SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;   //SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;    //NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC多项式,暂时用不到,给默认值7SPI_Init(SPI1, &SPI_InitStructure);      //将结构体变量交给SPI_Init,配置SPI1/*SPI使能*/SPI_Cmd(SPI1, ENABLE);         //使能SPI1,开始运行/*设置默认电平*/MySPI_W_SS(1);           //SS默认高电平
}
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空SPI_I2S_SendData(SPI1, ByteSend);        //写入数据到发送数据寄存器,开始产生时序while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET); //等待接收数据寄存器非空return SPI_I2S_ReceiveData(SPI1);        //读取接收到的数据并返回
}

9.unix时间戳

unix时间戳定义为1970年1月1日0时0分0秒到现在经历的秒数,不考虑闰秒
时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量
世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间

GMT:格林尼治标准时间,以地球自转为基础的时间计量系统,将自转一周的时间分为24小时(但是地球自转一周的时间是在变化的)

UTC:协调世界时,以原子钟为基础的时间计量系统。当原子钟计时一天的时间与地球自转一周的时间差超过0.9s时,UTC会执行闰秒来保证其计时与地球自转的协调一致
c语言的库函数中提供了时间获取和时间戳转换的相关函数:

图片消失了

时间戳转换示意图(具体的用法可以百度):

图片消失了

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com