STM32單片機_3

第十章IIC通信

協議規定, 起始之后主機必須先發送一個字節: 從機地址+讀寫位, 進行尋址

然后接收一下應答位,?

然后再發送一個字節, 寫入從機寄存器地址?

之后就可以進行數據的收發了

注意: 在 主機的接收應答的時候, 立刻釋放SDA 然后這時候從機會立刻做出反應, 即拉低SDA, 也就是置0, 說明給了應答, 如果后面SCL高電平期間, 主機讀應答位的時候, 發現是高電平, 說明從機沒給應答

?

軟件模擬代碼:

#include "Delay.h"
//用宏函數來方便改變電平, 但是這樣有一點缺點, 可以套一個函數
//改變引腳電平后延時10us
//開漏輸出+ 若上拉, 主機輸出1 不是輸出1 , 而不是釋放
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10, (BitAction)BitValue);Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11, (BitAction)BitValue);Delay_us(10);
}
//讀取的時候CLK是高電平
uint8_t MyI2C_R_SDA(void)
{uint8_t Bit;Bit = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);//這里主機讀取, 所以是外部輸入Delay_us(10);return Bit;
}void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for(i = 0;i<8;i++){MyI2C_W_SDA(!!(Byte & (0x80 >> i)));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}uint8_t MyI2C_ReceiveByte(void)
{uint8_t Data = 0x00;uint8_t i;MyI2C_W_SDA(1);//先釋放一次就可以了for(i = 0;i<8;i++){MyI2C_W_SCL(1);if(MyI2C_R_SDA()==1){Data |= (0x80 >> i);}MyI2C_W_SCL(0);}return Data;
}void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);//先釋放一次就可以了MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}

這里的發送和接收都是高位先行, 按照規定SCL低電平的時候進行寫數據, 高電平的時候讀數據

MPU6050簡介

?

?

I2C可以去進行軟件模擬, 因為I2C是同步時序

MPU6050讀取姿態信息代碼展示:

這里用一個MPU6050_Reg.h頭文件來去管理寄存器的宏定義

然后就按照指定地址讀寫進行完成讀寫函數

0xD0是MPU6050的地址, 同時最后一位給1或0 是我們要去讀還是寫的設定 , 給1 是讀, 給0 是寫

#include "MyI2C.h"
#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0xD0void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();//接收應答MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();//接收應答MyI2C_SendByte(Data);MyI2C_ReceiveAck();//接收應答MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();//接收應答MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();//接收應答MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS | 0x01);MyI2C_ReceiveAck();//接收應答Data =MyI2C_ReceiveByte();MyI2C_SendAck(1);//發送應答 , 主機接收之后要發送MyI2C_Stop();return Data;
}void MPU6050_Init(void)
{MyI2C_Init();MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);MPU6050_WriteReg(MPU6050_CONFIG, 0x06);MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}void MPU6050_GetDate(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{//接下來就讀取數據, 放到指針里面, 這樣就把數據傳出去了, 數據寄存器是16位的, 那就分別讀取高位和低位的值拼接起來uint8_t DataH, DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (DataH<<8) | DataL;         }

還有最后通過變量指針的方式將數據帶出函數?

代碼解釋:

硬件I2C

GPIO要配置復用開漏輸出模式

代碼展示:

因為硬件I2C是非阻塞的, 我們要去等待標志位, 如圖, 每一個操作后面都有事件, 我們要去捕捉那些事件, 等待那些事件到來之后再去進行下一步操作

同時這個等待也要進行超時檢測!!!,封裝一下就可以了

硬件I2C就是那些基本的發送, 接收, 起始, 結束, 等待都不需要我們自己去寫


#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0xD0void 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){break;}}
}void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS);
//	MyI2C_ReceiveAck();//接收應答
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReceiveAck();//接收應答
//	MyI2C_SendByte(Data);
//	MyI2C_ReceiveAck();//接收應答
//	MyI2C_Stop();//軟件I2C都是阻塞形式的, 有延時, 而硬件是非阻塞式的, 那么就要等待標志位I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//自帶應答, 就不用處理了MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, RegAddress);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);I2C_SendData(I2C2, Data);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTOP(I2C2, ENABLE);
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS);
//	MyI2C_ReceiveAck();//接收應答
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReceiveAck();//接收應答
//	
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
//	MyI2C_ReceiveAck();//接收應答
//	Data =MyI2C_ReceiveByte();
//	MyI2C_SendAck(1);//發送應答 , 主機接收之后要發送
//	MyI2C_Stop();
//	return Data;I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//同時給超時退出I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//自帶應答, 就不用處理了MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, RegAddress);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);//自帶應答, 就不用處理了MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//這是接收//接收一個字節有特殊設定, 提前配置ACK位, 和給停止I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);Data = I2C_ReceiveData(I2C2);I2C_AcknowledgeConfig(I2C2, ENABLE);//恢復給應答的形式, 以便多數據的代碼return Data;
}void MPU6050_Init(void)
{
//	MyI2C_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);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);I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = 50000;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//在快速模式下有用, 給不同的時鐘占空比I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_OwnAddress1 = 0x00;//這是stm32做從機才有用, 這里隨便填一個I2C_Init(I2C2, &I2C_InitStructure);I2C_Cmd(I2C2, ENABLE);MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);MPU6050_WriteReg(MPU6050_CONFIG, 0x06);MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}void MPU6050_GetDate(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{//接下來就讀取數據, 放到指針里面, 這樣就把數據傳出去了, 數據寄存器是16位的, 那就分別讀取高位和低位的值拼接起來uint8_t DataH, DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (DataH<<8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (DataH<<8) | DataL;         }

?

這幾個操作時發送一個字節特殊的操作

第十一章SPI通信

ss線, 主機要和哪個從機通信, 就將哪個ss線電平置0

因為SPI是全雙工, 所以可以使用推挽輸出, 那么電平的變化就可以很快, 那么通信速率就可以達到很快

這個模式是對應上面的移位示意圖

即SCK上升沿主機讀數據, 下降沿主機發送數據?

這些都是SCK上升沿或者是下降沿采樣的區別

W25Q60芯片介紹

像這個芯片可以儲存一些數據, 而且掉電不丟失, 有些項目中需要存儲數據時可以選擇

?

忙狀態就是芯片在Flash里面操作數據需要一點時間, 這時候芯片處于忙狀態, 不能去讀寫操作

重要的指令集

Write Enable 06h

Write Disable 04h

?Read Status Register 05h

Pag Program 02h

Sendor Erase 20h

JEDEC ID 9Fh

Read Data 03h?

寫入不能跨頁, 但是讀取可以跨頁, 即寫入到達頁尾的時候, 就會回到頁頭進行寫入

如果要寫多頁, 我們就要計算有多少頁, 然后封裝一個函數, 分批次寫入

軟件SPI

代碼展示:

MySPI.c

#include "stm32f10x.h"                  // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4 , (BitAction)BitValue);
}
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5 , (BitAction)BitValue);
}
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7 , (BitAction)BitValue);
}
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA, &GPIO_InitStructure);MySPI_W_SS(1);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}
void MySPI_Stop(void)
{MySPI_W_SS(1);
}
//模式0  --其他的模式都是對這個模式0稍微修改一下就可以了
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t ByteReceive = 0x00;uint8_t i;//因為是軟件模擬, SCK不可能和數據移入移出同時進行for(i = 0;i<8;i++){MySPI_W_MOSI(ByteSend & (0x80>>i));MySPI_W_SCK(1);if(MySPI_R_MISO()==1){ByteReceive |= (0x80>>i);}MySPI_W_SCK(0);}return ByteReceive;
}//改進之后, 更對應移位模型
//uint8_t MySPI_SwapByte(uint8_t ByteSend)
//{
//	uint8_t i;
//	for(i = 0;i<8;i++)
//	{
//		MySPI_W_MOSI(ByteSend);
//		ByteSend<<=1;
//		MySPI_W_SCK(1);
//		if(MySPI_R_MISO()==1)
//		{
//			ByteSend |= 0x01;
//		}
//		MySPI_W_SCK(0);
//	}
//	
//	return ByteSend;
//}

?W25Q64.c? : 這個上層模塊就是按照SPI通信的要實現寫使能, 等待繁忙, 頁寫入, 頁清除, 讀數據

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"
void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID<<=8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint16_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);Timeout = 10000;while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) ==1){Timeout--;if(Timeout==0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address, int8_t *DataArray, uint16_t Count)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);uint16_t i;for(i = 0;i<Count;i++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();
}
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}
//讀數據的時候就不用再去管是否繁忙了
void W25Q64_ReadData(uint32_t Address, int8_t *DataArray, uint32_t Count)
{MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);uint32_t i;for(i = 0;i<Count;i++){DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

?MySPI_Ins.h : 這個就是指令 集的封裝

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3#define W25Q64_DUMMY_BYTE							0xFF#endif

硬件SPI

串口是低位先行, I2C和SPI是高位先行

I2S是數字音頻傳輸協議

這里有連續傳輸和非連續傳輸

對于下面這些引腳, 需要解除引腳復用的設置才可以正常當作GPIO , 或者是其他從定義的功能

如何去解除可以看6-4的視頻

對于TXE 和 RXNE 這兩個DR寄存器為空標志位都不需要我們去清除, 硬件自己會清除(這是應為寫入的時候TXE硬件清除, 讀取的時候RXNE硬件清除)

代碼展示:

MySPI.c : 硬件和軟件的上層都一樣, 只是底層硬件會封裝好一些時序寫入讀出函數方便我們使用

#include "stm32f10x.h"                  // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4 , (BitAction)BitValue);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA, &GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//分頻SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//哪個邊沿開始采樣SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//空閑默認時鐘電平  --這兩個就是模式0SPI_InitStructure.SPI_CRCPolynomial = 7;//隨便填一個默認值7SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//通信模式, 一般全雙工SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位先行SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_Init(SPI1, &SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);MySPI_W_SS(1);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}
void MySPI_Stop(void)
{MySPI_W_SS(1);
}
//模式0  --其他的模式都是對這個模式0稍微修改一下就可以了
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)!=SET);//等待TXE為1SPI_I2S_SendData(SPI1, ByteSend);while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=SET);//等待RXNE為1return SPI_I2S_ReceiveData(SPI1);
}

這里發現一個問題, 用高級定時器輸出PWM的時候要打開這個開關, 要不然不能輸出PWM

第十二章Unix時間戳

BKT備份寄存器跟上一節的Flash閃存有一點類似, 只是Flash閃存是真正的掉電不丟失, 但是BKT備份寄存器是靠著備用電源供電的?

?分頻器其實就是一個計數器, 計幾個數溢出一次就是幾分頻, 重裝值是幾, 分頻值就是重裝值+1

下面是RTC配置的注意事項

讀寫備份寄存器

代碼展示:

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"uint16_t ArrayWrite[] = {0x1234, 0x4444};
uint16_t ArrayRead[2];
uint8_t KeyNum;
int main(void)
{OLED_Init();Key_Init();OLED_ShowString(1, 1, "W:");OLED_ShowString(2, 1, "R:");RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);while (1){KeyNum = Key_GetNum();if(KeyNum==1){ArrayWrite[0]++;ArrayWrite[1]++;OLED_ShowHexNum(1, 4, ArrayWrite[0], 4);OLED_ShowHexNum(1, 8, ArrayWrite[1], 4);BKP_WriteBackupRegister(BKP_DR1, ArrayWrite[0]);BKP_WriteBackupRegister(BKP_DR2, ArrayWrite[1]);}ArrayRead[0] = BKP_ReadBackupRegister(BKP_DR1);ArrayRead[1] = BKP_ReadBackupRegister(BKP_DR2);OLED_ShowHexNum(2, 4, ArrayRead[0], 4);OLED_ShowHexNum(2, 8, ArrayRead[1], 4);}
}

?代碼解釋:

實時時鐘

代碼展示:

MyRTC.c

#include "stm32f10x.h"                  // Device header
#include <time.h>
uint16_t MyRTC_Time[] = {2024, 1, 1, 15, 55, 59};
void MyRTC_SetTime(void);
void MyRTC_Init(void)
{//1使能PWR, PKBRCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);//避免重復初始化if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//2開啟LSE, 等待啟動完成RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);//3選擇時鐘源RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);//等待函數RTC_WaitForSynchro();//等待同步RTC_WaitForLastTask();//等待寫入完成//配置預分頻RTC_SetPrescaler(32768-1);//自己帶了進入和退出配置模式RTC_WaitForLastTask();//設置時間MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else{RTC_WaitForSynchro();RTC_WaitForLastTask();}
}void MyRTC_SetTime(void)
{time_t time_cnt;struct tm time_date;time_date.tm_year = MyRTC_Time[0]-1900;time_date.tm_mon = MyRTC_Time[1]-1;time_date.tm_mday = MyRTC_Time[2];time_date.tm_hour = MyRTC_Time[3];time_date.tm_min = MyRTC_Time[4];time_date.tm_sec = MyRTC_Time[5];time_cnt = mktime(&time_date);RTC_SetCounter(time_cnt);RTC_WaitForLastTask();
}void MyRTC_ReadTime(void)
{time_t time_cnt;struct tm time_date;time_cnt = RTC_GetCounter() + 8*60*60;time_date = *localtime(&time_cnt);MyRTC_Time[0] = time_date.tm_year+1900;MyRTC_Time[1] = time_date.tm_mon+1;MyRTC_Time[2] = time_date.tm_mday;MyRTC_Time[3] = time_date.tm_hour;MyRTC_Time[4] = time_date.tm_min;MyRTC_Time[5] = time_date.tm_sec;
}

代碼解釋:

設定時間和讀取時間都是秒計數器和日期時間的轉化

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89720.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89720.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89720.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringAI_Chat模型_DeepSeek模型--基礎對話

一、前言 Spring AI 提供跨 AI 供應商&#xff08;如 OpenAI、Hugging Face 等&#xff09;的一致性 API, 通過分裝的ChatModel或ChatClient即可輕松調動LLM進行流式或非流式對話。 本專欄主要圍繞著通過OpenAI方式調用各種大語言模型展開學習&#xff08;因為95%以上模型都…

數據結構:字符串(Strings)

目錄 第一性問題&#xff1a;計算機如何表示文字&#xff1f; ASCII&#xff1a;最早的字符編碼標準&#xff08;美國人寫的&#xff09; Unicode&#xff1a;解決全球語言的編碼方案 字符&#xff08;Character&#xff09; ?編輯 為什么字符常量必須加上單引號 &#…

【vue-5】Vue 3 中的 v-model:雙向數據綁定的全面指南

在 Vue 開發中&#xff0c;v-model 是實現表單輸入和應用狀態之間雙向綁定的關鍵指令。Vue 3 對 v-model 進行了重大改進&#xff0c;使其更加靈活和強大。本文將深入探討 Vue 3 中 v-model 的工作原理、新特性以及最佳實踐。 1. v-model 基礎 1.1 什么是 v-model v-model 是 V…

結合自身,制定一套明確的 Web3 學習路線和技術棧建議

目錄 ? 一、結合自身&#xff0c;明確方向和目的 ? 二、技術路線和建議 &#x1f9ed; 技術路線圖&#xff08;按階段劃分&#xff09; 第一階段&#xff1a;鞏固 Web3 基礎&#xff08;1-2 周&#xff09; 第二階段&#xff1a;NFT 平臺開發實戰&#xff08;4-6 周&…

SPARKLE:深度剖析強化學習如何提升語言模型推理能力

摘要&#xff1a;強化學習&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;已經成為賦予語言模型高級推理能力的主導范式。盡管基于 RL 的訓練方法&#xff08;例如 GRPO&#xff09;已經展示了顯著的經驗性收益&#xff0c;但對其優勢的細致理解仍然不足。為了填…

【Linux服務器】-MySQL數據庫參數調優

一、基礎配置 [mysqld] # 聲明以下配置屬于MySQL服務器&#xff08;mysqld&#xff09;[mysqld]&#xff1a;配置文件的模塊標識&#xff0c;表示這是 MySQL 服務器的配置段。 二、路徑與基礎設置 datadir/var/lib/mysql socket/var/lib/mysql/mysql.sock pid-file/var/run/mys…

sqli-labs靶場通關筆記:第32-33關 寬字節注入

第32關 寬字節注入查看一下本關的源代碼&#xff1a;function check_addslashes($string) // 定義一個用于過濾特殊字符的函數&#xff0c;目的是轉義可能用于注入的特殊符號 {$string preg_replace(/. preg_quote(\\) ./, "\\\\\\", $string); // 轉義…

基于Eureka和restTemple的負載均衡

在微服務架構中&#xff0c;基于 Eureka&#xff08;服務注冊中心&#xff09;和 RestTemplate&#xff08;HTTP 客戶端&#xff09;實現負載均衡是常見的方案&#xff0c;核心是通過 Eureka 獲取服務實例列表&#xff0c;再結合負載均衡策略選擇具體服務實例進行調用。以下是詳…

子線程不能直接 new Handler(),而主線程可以

在 Android 中&#xff0c;子線程不能直接 new Handler()&#xff0c;而主線程可以&#xff0c;原因在于 Looper 機制。下面詳細解釋&#xff1a;1. 為什么主線程可以直接 new Handler()&#xff1f; 主線程&#xff08;UI 線程&#xff09;在啟動時&#xff0c;系統會自動調用…

Android無需授權直接訪問Android/data目錄漏洞

從android11開始&#xff0c;訪問/sdcard/Android/data目錄需要URI授權&#xff0c;而從更高的版本開始甚至URI權限也被收回&#xff0c;返回“無法使用此文件夾”的提示&#xff0c;這里提供一種方法&#xff0c;可以越權強制訪問data目錄&#xff0c;當然也包括obb、media等目…

本地部署 Kimi K2 全指南(llama.cpp、vLLM、Docker 三法)

Kimi K2 是 Moonshot AI 于2025年7月11日發布的高性能多專家語言模型&#xff08;MoE&#xff09;&#xff0c;支持最大 128K 上下文&#xff0c;激活參數規模為 32B&#xff0c;具備極強的推理、代碼生成與多輪對話能力。自從其權重以多種格式開源以來&#xff0c;許多開發者希…

使用python的pillow模塊將圖片轉化為灰度圖和相關的操作

使用python的pillow模塊可以將圖片轉化為灰度圖&#xff0c; 可以獲取灰度圖的特定點值&#xff0c;區域值&#xff0c; 修改值并保存到圖片 圖片轉換為灰度圖 from PIL import Image# 打開圖片 image Image.open("d://python//2//1.jpg")gray_image image.convert…

【網絡安全】大型語言模型(LLMs)及其應用的紅隊演練指南

未經許可,不得轉載。 文章目錄 什么是紅隊演練? 為什么 RAI 紅隊演練是一項重要實踐? 如何開展和規劃 LLM 的紅隊演練 1.測試前的準備 規劃:由誰負責測試 規劃:測試內容 規劃:測試方式 規劃:數據記錄方式 2.測試過程中 3.每輪測試后 報告數據 區分“識別”與“測量” 本…

ROS2安裝ros-humble-usb-cam 404錯誤導致失敗的解決方法

ROS2安裝ros-humble-usb-cam遇到404錯誤導致安裝失敗&#xff0c;如圖&#xff1a;解決方法&#xff1a; 備份 sources.list sudo cp /etc/apt/sources.list.d/ros2.list /etc/apt/sources.list.d/ros2.list.bak替換為清華源 sudo sed -i s|http://packages.ros.org/ros2/ubunt…

OllyDbg技巧學習

1 嘗試在反匯編代碼中找到一個函數的二進制代碼 有的時候需要一個函數的二進制代碼&#xff0c;注入到另外的一些地方&#xff1b;以此程序為示例&#xff0c; 八叉樹的C實現與原理解析-CSDN博客 Ollydbg打開可執行文件&#xff0c;我想先找到此函數的二進制代碼體&#xff0…

數據分析智能體:讓AI成為你的數據科學家

數據分析智能體&#xff1a;讓AI成為你的數據科學家 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 ? 用代碼丈量世界&#xff0c…

K8s與Helm實戰:從入門到精通

Kubernetes 簡介 Kubernetes(簡稱 K8s)是一個開源的容器編排平臺,用于自動化部署、擴展和管理容器化應用。最初由 Google 設計并捐贈給云原生計算基金會(CNCF),現已成為容器編排領域的事實標準。 核心功能 自動化容器部署:支持聲明式配置和自動化部署,減少人工干預。…

根據ARM手冊,分析ARM架構中,原子操作的軟硬件實現的底層原理

目錄 1.問題背景&#xff1a; 2.原子操作 2.1 硬件操作 2.1.1 LDREX/LDXR指令 2.1.2 STREX/STXR指令 2.2 軟件操作 2.3 軟件硬件操作的各性能對比 3.總結 1.問題背景&#xff1a; 我們知道&#xff0c;RTOS的任務調度算法是搶占式優先級調度算法。 既然是搶占了&…

iOS 抓包工具選擇與配置指南 從零基礎到高效調試的完整流程

iOS 抓包&#xff1a;復雜網絡調試的必要技能 隨著移動端應用越來越依賴網絡交互&#xff0c;iOS 抓包作為核心調試工具之一&#xff0c;變得尤為重要。無論是調試 App 與后端的接口通信、排查 HTTPS 請求加密問題&#xff0c;還是定位網絡連接超時、請求異常&#xff0c;抓包都…

Java使用FastExcel實現Excel文件導入

依賴配置 (Maven pom.xml)<dependencies><!-- FastExcel 核心庫 --><dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version>1.0.0</version></dependency><!-- Apache POI…