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); //等待EV5
-
- I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6
-
- I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8
-
- I2C_SendData(I2C2, Data); //硬件I2C发送数据
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2
-
- I2C_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); //等待EV5
-
- I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6
-
- I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2
-
- I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成重复起始条件
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5
-
- I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); //硬件I2C发送从机地址,方向为接收
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6
-
- I2C_AcknowledgeConfig(I2C2, DISABLE); //在接收最后一个字节之前提前将应答失能
- I2C_GenerateSTOP(I2C2, ENABLE); //在接收最后一个字节之前提前申请停止条件
-
- MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7
- Data = 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; //时钟速度,选择为50KHz
- I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,选择Tlow/Thigh = 2
- I2C_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); //配置寄存器,配置DLPF
- MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器,选择满量程为±2000°/s
- MPU6050_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改写成0,不能由0改写成1
- 写入数据前必须先擦除,擦除后,所有数据位变为1,所以没数据的空间存储的是FF
- 擦除必须按最小擦除单位举行
- 连续写入多字节时,最多写入一页的数据,凌驾页尾的数据,会回到页首覆盖写入
- 写入操纵结束后,芯片进入忙状态,不相应新的读写操纵
读取操纵时:
- 直接调用读取时序,无需使能,无需额外操纵,没有页的限定,读取操纵结束后不会进入忙状态,但不能在忙状态时读取
代码:
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); //显示MID
- OLED_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时,不做处理,指定位为默认的初值0
- MySPI_W_SCK(0); //拉低SCK,下降沿移入数据
- }
-
- return ByteReceive; //返回接收到的一个字节数据
- }
复制代码 根据SPI通讯准则跟W25Q64实现通讯功能的代码:
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模式0
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7
- SPI_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语言的库函数中提供了时间获取和时间戳转换的相干函数:
时间戳转换示意图(详细的用法可以百度):
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |