筆記採子正點原子STM32F1開發指南
系統框架:
時鐘樹:
一般時鐘初始化:如103對應着上面時鐘樹看
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);//設置爲外部高速時鐘源
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1); //設置PCLK2的時鐘跟HCLK一樣,不分頻HCLK即APB2
/* PCLK1 = HCLK/2 */ APB1
RCC_PCLK1Config(RCC_HCLK_Div2);//設置PCLK1的時鐘爲HCLK二分頻,因爲最大爲HCLK二分一
/* Flash 2 wait state */
FLASH_SetLatency(FLASH_Latency_2);//設置FLASH的等待時間
STM32的CPU的最大速率已知爲72MHz,但FLASH無法達到這麼高的速度,因此要在CPU存取FLASH的過程中插入所謂的“等待週期”,顯然CPU速度越快,所要插入的等待週期個數越多,原則是:
- 當CPU速率爲0 ~ 24MHz時,不需要插入等待週期,即等到週期個數爲0;
- 當CPU速率爲24 ~ 48MHz時,插入1個等待週期;
- 當CPU速率爲48MHz ~ 72MHz時,插入2個等待週期;
/* Enable Prefetch Buffer */使能預取內存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
#ifdefine STM32F10X_CL
RCC_PREDIV2Config(RCC_PREDIV2_Div5);//25/5=5M
RCC_PLL2Config(RCC_PLL2Mul_8);//5X8=40M
RCC_PLL2Cmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET);
RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);40/5=8M
#endif
/* PLLCLK = 8MHz * 9 = 72 MHz */倍頻外部時鐘信號源得PLLCLK
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//設置系統時鐘爲倍頻後的時鐘(看上圖系統時鐘有多種選擇)
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
F107互聯型 MCU區別與F103,因爲他們具有USB OTG功能,因此需要特別的時鐘
所以使用外部25M的晶震,分頻5得8通過PLL2 X5得40,再分頻5得8M;
端口複用和重映射:
爲了使不同器件封裝的外設 IO 功能數量達到最優,可以把一 些複用重新映射其他功能數量達到最優,可以把一 些複用重新映射其他功能數量達到最優,可以把一 些複用重新映射其他些引腳上。 STM32 中有很多內置外設的輸入出引腳都具重映射 中有很多內置外設的輸入出引腳都具重映射 (remap) 的功能 。
詳細步驟爲:
開GPIO時鐘;使能對應複用功能時鐘;使能複用時鐘AFIO;開啓重映射。
而部分重映射有自己對應的設置;
中斷優先級:1.如果兩個中斷的搶佔優先級和響應都一樣,則看哪個中斷更早先執行;2.高優先級的搶佔優先級可以打斷低的搶佔優先級,而搶佔優先級相同,高優先級的響應優先級不可以打斷低響應優先級的中斷。
結合實例說明一下:假定設置中斷優先級組爲 2,然後設置中斷 ,然後設置中斷 3(RTC 3(RTC 中斷 )的搶佔優先級 爲 2,響應優先級爲1中斷 。中斷 6(外部中斷 (外部中斷 0)的搶佔優先級爲 3,,響應優先級爲 0。中斷 。中斷 7(外 部中斷 1)的搶佔優先級爲 的搶佔優先級爲 2,響應優先級爲 0。那麼這 。那麼這 3箇中斷 的優先級順序爲:箇中斷 的優先級順序爲:7> 中 斷 3> 中斷 6。
上面例子中的斷 3和中斷 和中斷 7都可以打斷 中斷6的中斷 。而7和中斷 和中斷 3卻不可以相互打斷。
中斷分組表:
中斷優先級分組函數:NVIC_PriorityGroupConfig其實就是對寄存器SCB->SIRCR設置,
NVIC_IRQChannelPreemptionPriority :定義該中斷的搶佔優先級;
NVIC_IRQChannelChnnel:定義是什麼中斷;
NVIC_IRQChannelSubPriority:定義中斷子優先級表(中斷優先級);
NVIC_IRQChannelCmd:使能中斷。
當中得delay.h文件
CM3內核處理器有一個SYSTICK時鐘
sys.h文件宏定義了IO口的操作,包括讀入和輸出。
usart文件對主要寫串口得操作
printf函數得支持,
STM32IO口:一共又8種模式
輸出:1.推輓輸出:可以輸出高、低電平,由IC決定 推輓電路是兩個參數相同的三極管或MOSFET,以推輓方式存在於電路中,各負責正負半周的波形放大任務,電路工作時,兩隻對稱的功率開關管每次只有一個導通,所以導通損耗小、效率高。輸出既可以向負載灌電流,也可以從負載抽取電流。推拉式輸出級既提高電路的負載能力,又提高開關速度。
2.開漏輸出:輸出端相當於三極管的集電極,要得到高電平狀態需要上拉電阻才行。適合於做電流型的驅動,其吸收電流的能力相對強(一般20mA以內)。
3浮空輸入:空輸入狀態下,IO的電平狀態是不確定的,完全由外部輸入決定,如果在該引腳懸空的情況下,讀取該端口的電平是不確定的。有可能被幹擾導致輸入不準確。
4上拉輸入/下拉輸入/模擬輸入
5:複用開漏輸出、複用推輓輸出:GPIO口被用作第二功能時的配置情況所需要設置。
具體軟件配置:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | B | C, ENABLE):使能APB2總線外設時鐘
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//輸入輸出類型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//響應速度如翻轉(只有在輸出有用)有5,10,50
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//根據表設置爲複用推輓
GPIO_Init(GPIOA , &GPIO_InitStructure);
//PA10作爲US1的RX端,負責接收數據
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//可以爲浮空或上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化
USART_InitTypeDef USART_InitStructure;
//將結構體設置爲缺省狀態
USART_StructInit(&USART_InitStructure);
//波特率設置爲115200
USART_InitStructure.USART_BaudRate = 115200;
//一幀數據的寬度設置爲8bits
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
//在幀結尾傳輸1個停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//奇偶失能模式,無奇偶校驗
USART_InitStructure.USART_Parity = USART_Parity_No;
//發送/接收使能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//硬件流控制失能
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
//設置串口1
USART_Init(USART1, &USART_InitStructure);
//打開串口1的中斷響應函數
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//打開串口1
USART_Cmd(USART1, ENABLE);
既然設置了中斷響應函數,那麼就要有中斷設置:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //選擇中斷分組2
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQChannel; //選擇串口1中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //搶佔式中斷優先級設置
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; //響應式中斷優先級設置爲0
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中斷
NVIC_Init(&NVIC_InitStructure);
void USART1_IRQHandler(void) //串口1中斷
{
char RX_dat; //定義字符變量
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判斷髮生接收中斷
{USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除中斷標誌
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)0x01); //開始傳輸
RX_dat=USART_ReceiveData(USART1) & 0x7F; //接收數據,整理除去前兩位
USART_SendData(USART1, RX_dat); //發送數據
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}//等待發送結束
}
}
配置
EXTI線,使中斷線和
IO
針腳線連接上
STM32的每一個IO口都可以作爲外部中盾的中斷輸入口,
配置EXTI線,使中斷線和IO針腳線連接上
1、 將EXTI線連接到IO端口上
將EXTI線4連接到端口GPIOD的第4個針腳上
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource4);
2、配置中斷邊沿
/*配置EXTI線4上出現下降沿,則產生中斷*/
EXTI_InitStructure.EXTI_Line = EXTI_Line2; 配置的4號針腳,
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式分爲中斷和事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發 (可以是上,下,上下)
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中斷線使能
EXTI_Init(&EXTI_InitStructure); //初始化中斷
EXTI_GenerateSWInterrupt(EXTI_Line4); //EXTI_Line4中斷使能
中斷函數:
void EXTI2_RQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)!= RESET)
{ 中斷實現邏輯
EXTI_ClearITPendingBit(EXTI_Line2); //清除標誌
}
}
同一條中斷線只能控制一個IO
定時器中斷實驗:
定時器分通用定時器(2,3,4,5)TIMx_CNT計數,TIMx_PSC預分頻(1-65535),每個定時器有4個獨立通道可以用來1.輸入捕獲,2.輸出比較,3.PWM生成,4.單脈衝模式輸出
可設置多種中斷模式:1.更行:計數器的上或者下一處,計數器初始化(通過軟件或內/外觸發)2.觸發時間(計數器啓動,停止,初始化或者由內,外觸發計數)3.輸入捕獲4.輸出比較。5支持針對定位的增量 (正交 )編碼器和霍爾傳感電路
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr; //設置自動重裝的值也就是計數的值
TIM_TimeBaseStructure.TIM_Prescaler = psc;//時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;//時鐘線的分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//模式爲向上計數
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //溢出中斷計算就是溢出多少次纔給你一個溢出中斷
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
//初始化TM2
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TM2
TIM_Cmd(TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 2;//分頻2 72M/(2+1)/2=24MHz
TIM_TimeBaseStructure.TIM_Period = 65535; //計數值65535
((1+TIM_Prescaler )/72M)*(1+TIM_Period )=((1+2)/72M)*(1+65535)=0.00273秒=366.2Hz */
void TIM2_IRQHandler(void)
{
u8 ReadValue;
//檢測是否發生溢出更新事件
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
//清除TIM2的中斷待處理位
實現邏輯
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);
}
}
PWM輸出:除了6,7定時器都可以用來輸出PWM,高級定時器1,8更可以同時產生7路PWM輸出。
定時器的相關設置功能一樣
PWM的設置
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置爲PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = CCR1;
TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High; //輸出極性爲高
TIM_OC1Init(TIM3, &TIM_OCInitStructure);//初始化
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_Cmd(TIM3,ENABLE);//使能定時器
PWM模式1- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1爲有效電平,否則爲
無效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1爲無效電平(OC1REF=0),否
則爲有效電平(OC1REF=1)。
PWM模式2- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1爲無效電平,否則爲
有效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1爲有效電平,否則爲無效電
平。
SPI:串行外圍設備接口
一共4條通訊線MISO,MOSI,SCLK,CS。SPI的特點是能同時收發串行數據提供頻率可編程時鐘,發送結束中斷標誌,寫保護等
【 CPHA相位】
首先說明一點,capture strobe = latch = read = sample,都是表示數據採樣,數據有效的時刻。
相位,對應着數據採樣是在第幾個邊沿(edge),是第一個邊沿還是第二個邊沿,0對應着第一個邊沿,1對應着第二個邊沿。
對於:
CPHA=0,表示第一個邊沿:
對於CPOL=0,idle時候的是低電平,第一個邊沿就是從低變到高,所以是上升沿;
對於CPOL=1,idle時候的是高電平,第一個邊沿就是從高變到低,所以是下降沿;
CPHA=1,表示第二個邊沿:
對於CPOL=0,idle時候的是低電平,第二個邊沿就是從高變到低,所以是下降沿;
對於CPOL=1,idle時候的是高電平,第一個邊沿就是從低變到高,所以是上升沿;
【 CPOL極性】
先說什麼是SCLK時鐘的空閒時刻,其就是當SCLK在數發送8個bit比特數據之前和之後的狀態,於此對應的,SCLK在發送數據的時候,就是正常的工作的時候,有效active的時刻了。
先說英文,其精簡解釋爲:Clock Polarity = IDLE state of SCK。
再用中文詳解:
SPI的CPOL,表示當SCLK空閒idle的時候,其電平的值是低電平0還是高電平1:
CPOL=0,時鐘空閒idle時候的電平是低電平,所以當SCLK有效的時候,就是高電平,就是所謂的active-high;
CPOL=1,時鐘空閒idle時候的電平是高電平,所以當SCLK有效的時候,就是低電平,就是所謂的active-l
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //通訊模式,半雙工,全雙工,串行發和收這裏是全雙工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//設置STM32爲主機
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//數據幀格式爲8位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//時鐘極性空閒爲高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//相位在第二個邊沿被採集
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//設置NSS管腳由硬件還是軟件控制(這裏是軟件)
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//SPI的波特率 頻率=時鐘/分頻
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//數據傳輸高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7;//設置CRC教研多項式
SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;//傳送速率
SPI_Init(SPI1, &SPI_InitStructure); //¸
SPI_Cmd(SPI1, ENABLE); //
SPI1_ReadWriteByte(0xff);//
}
對於原子的MISO設置爲複用推輓
對於STM32的這一類管腳來說(如USART_RX)即可以設置成爲輸入模式,也可以設置成爲複用的推輓輸出。其工作都是正常的,不過建議大家還是設置成爲輸入端口的好,容易理解。
具體產生這一問題的原因是:從功能上來說,MISO應該配置爲輸入模式纔對,但爲什麼也可以配置爲GPIO_Mode_AF_PP?請看下面的GPIO複用功能配置框圖。當一個GPIO端口配置爲GPIO_Mode_AF_PP是,這個端口的內部結構框圖如下:圖中可以看到,片上外設的複用功能輸出信號會連接到輸出控制電路,然後在端口上產生輸出信號。但是在芯片內部,MISO是SPI模塊的輸入引腳,而不是輸出引腳,也就是說圖中的"複用功能輸出信號"根本不存在,因此"輸出控制電路"不能對外產生輸出信號。
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI1, TxData); //
retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI1); //
}
一般的讀寫如下
u8 SPI_ReadSR(void)
{
u8 byte=0;
SPI_FLASH_CS=0; //置0片選使能器件
SPI1_ReadWriteByte(W25X_ReadStatusReg); // 向FLASH的寄存器發送讀命令
byte=SPI1_ReadWriteByte(0Xff); //發送一個無效的命令來讀取數據(應爲要發送時鐘)
SPI_FLASH_CS=1; //
return byte;
}
//
//
void SPI_Write_SR(u8 sr)
{
SPI_FLASH_CS=0; //置0片選使能器件
SPI1_ReadWriteByte(W25X_WriteStatusReg); // 向FLASH的寄存器發送寫命令
SPI1_ReadWriteByte(sr); //寫入數據
SPI_FLASH_CS=1; // 取消使能器件
}
MPU6050的初始化當中包含了MPU6050.h頭文件
mpu.setClockSource(MPU6050_CLOCK_PLL_ZGYRO); // Set Clock to ZGyro
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS); // Set Gyro Sensitivity to config.h
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // +- 2G
mpu.setDLPFMode(MPU6050_DLPF_BW_256); // Set Gyro Low Pass Filter
mpu.setRate(0); // 0=1kHz, 1=500Hz, 2=333Hz, 3=250Hz, 4=200Hz
mpu.setSleepEnabled(false);
mpu.i2cErrors = 0;
USB初始化:
Set_System();
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
I2C:(硬件I2C)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); 開啓I2C時鐘
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //
I2C工作模式配置:
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//設置爲I2C模式(還有其模式)
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//設置I2C的佔空比:注意該參數只有I2C工作在快速模式下(即SCK時鐘頻率高於100kHz)下才有意義。
I2C_InitStructure.I2C_OwnAddress1 =I2C1_OWN_ADDRESS7; //設置自己的地址
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;//設置爲有應答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//設置相連IC的地址位,與外設有關
在I2C通信時,只有在時鐘信號SCL爲高電平時,數據線上對應的數據纔是有效數據,所以若高電平持續時間短的話,讀寫時間短,數據可能沒有讀寫完,導致數據傳輸不準確;若高電平持續時間長的話,讀寫時間相應變長,可能導致時間的浪費,通信效率變低。所以合理的佔空比對I2C通信是很必要的!
void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
/* Send STRAT condition */
I2C_GenerateSTART(I2C1, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C1, WriteAddr);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send the byte to be written */
I2C_SendData(I2C1, *pBuffer);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send STOP condition */
I2C_GenerateSTOP(I2C1, ENABLE);
}
使用IO口軟件模擬I2C
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //2個IO口設置爲推輓輸出
void IIC_Start(void)
{
SDA_OUT(); //配置SCL口的輸出速率50
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;
}
void IIC_Stop(void)
{
SDA_OUT();//
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//
delay_us(4);
}
等待從機應答信號;
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //配置SDA口爲浮空輸入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
//產生應答;就是SDA輸出0
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不產生應答 就是SDA輸出1
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//
else
IIC_Ack(); //
return receive;
}
R/W(0爲寫1爲讀)
IIC寫的過程:
IIC讀的過程:
主機發送數據流程
(1)主機在檢測到總線爲“空閒狀態”(即 SDA、SCL 線均爲高電平)時,發送一個啓動信號“S”,開始一次通信的開始
(2)主機接着發送一個命令字節。該字節由 7 位的外圍器件地址和 1 位讀寫控制位 R/W組成(此時 R/W=0)
(3)相對應的從機收到命令字節後向主機回饋應答信號 ACK(ACK=0)
(4)主機收到從機的應答信號後開始發送第一個字節的數據
(5)從機收到數據後返回一個應答信號 ACK
(6)主機收到應答信號後再發送下一個數據字節
(7)當主機發送最後一個數據字節並收到從機的 ACK 後,通過向從機發送一個停止信號P結束本次通信並釋放總線。從機收到P信號後也退出與主機之間的通信
主機接收數據流程
(1)主機發送啓動信號後,接着發送命令字節(其中 R/W=1)
(2)對應的從機收到地址字節後,返回一個應答信號並向主機發送數據
(3)主機收到數據後向從機反饋一個應答信號
(4)從機收到應答信號後再向主機發送下一個數據
(5)當主機完成接收數據後,向從機發送一個“非應答信號(ACK=1)”,從機收到ASK=1 的非應答信號後便停止發送
(6)主機發送非應答信號後,再發送一個停止信號,釋放總線結束通信
注意都是要寫入命令和地址(都是寫入)
應答就是SDA口讀取,0爲成功1爲失敗。
USB部分
{
pInformation = &Device_Info;
pInformation->ControlState = 2;
pProperty = &Device_Property;
pUser_Standard_Requests = &User_Standard_Requests;
/* Initialize devices one by one */
pProperty->Init(); //從這裏進入開始進入到下面的程序
}
{
/* Update the serial number string descriptor with the data from the unique
ID*/
Get_SerialNum();//獲取要發送給設備當設備ID
pInformation->Current_Configuration = 0;
/* Connect the device */
PowerOn();
/* Perform basic device initialization operations */
USB_SIL_Init();
/* configure the USART to the default settings */
// USART_Config_Default();
bDeviceState = UNCONNECTED;
}
{
USB_Istr();
}
{
CTR_HP();
}
/* DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending */