我用I2C2 DMA讀取從機寄存器,問(wèn)什么總是比我要讀取的多了一個(gè)字節?
我代碼里寫(xiě)了DMA?bufsize是1,但邏輯分析儀看到的是讀回來(lái)兩個(gè)字節,請幫忙看看是哪里配置出錯了么?
? ? logan_DMA_I2C2_RX_Init((u32)RxData,1);
? ? logan_i2c2_rx(0x5b, 0x12);
我用I2C2 DMA讀取從機寄存器,問(wèn)什么總是比我要讀取的多了一個(gè)字節?
我代碼里寫(xiě)了DMA?bufsize是1,但邏輯分析儀看到的是讀回來(lái)兩個(gè)字節,請幫忙看看是哪里配置出錯了么?
? ? logan_DMA_I2C2_RX_Init((u32)RxData,1);
? ? logan_i2c2_rx(0x5b, 0x12);
這是由于沒(méi)有提前關(guān)閉I2C的自動(dòng)應答ACK造成的。DMA配置的長(cháng)度只決定DMA會(huì )從I2C外設搬運幾個(gè)數據,而I2C接收多少數據是由I2C邏輯控制的。瀏覽代碼發(fā)現I2C接收第一個(gè)字節前并未關(guān)閉自動(dòng)應答ACK。這就造成從機收到主機的ACK從而認為主機需要再讀1字節而繼續發(fā)送,主機無(wú)法發(fā)出停止條件。
根據CH32FV2x_V3xRM的275頁(yè)所述:
“在接收模式時(shí),主設備需要在最后一個(gè)數據位的應答位置 NAK,接收到 NACK 后,從設備釋放對 SCL 和 SDA 線(xiàn)的控制”
故對于只讀1字節的情況,需要在讀取之前關(guān)閉自動(dòng)應答ACK。對于只讀取1字節的情況,代碼應如下修改:
while(?!I2C_CheckEvent(?I2C2,?I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED?)?);?//?原有代碼 I2C_AcknowledgeConfig(?I2C2,?DISABLE?);?//?新增代碼,提前關(guān)閉自動(dòng)應答ACK DMA_Cmd(?DMA1_Channel5,?ENABLE?);?//?原有代碼
而對于讀取2字節或更多的情況,自動(dòng)應答ACK應等到還剩1字節待讀取時(shí)再關(guān)閉。
另外附上一個(gè)X035系列I2C邏輯的狀態(tài)機實(shí)現代碼,樓主可以參考:(鏈接會(huì )直接跳轉到讀時(shí)序的接收階段,可以看到若只剩1字節需要接收時(shí),程序中關(guān)閉自動(dòng)應答ACK的功能)
https://github.com/WuxiProject-offical/CH32X035-HelperLibrary/blob/main/I2C/Master/i2c.c#L617
灰常感謝,那如果用DMA的話(huà)有什么方法可以處理這個(gè)“BUG”,總不能去一直循環(huán)查詢(xún)還剩多少個(gè)數據吧?
我用某業(yè)內主流32位MCU時(shí)遇到類(lèi)似的問(wèn)題,我的策略是DMA長(cháng)度比需要讀取的長(cháng)度少一個(gè),在DMA的傳輸完成中斷里關(guān)閉自動(dòng)應答ACK,然后最后一字節用中斷接收;如果只讀1字節,直接用中斷讀取而不開(kāi)DMA。
但V203上,似乎可以卡bug做一個(gè)非常騷的操作,即直接設置DMA長(cháng)度為比需要讀取的長(cháng)度少一個(gè),不在DMA傳輸完成中斷里添加代碼。按你的測試現象來(lái)看,如果最后一個(gè)字節沒(méi)有被DMA取走,主機會(huì )自動(dòng)發(fā)NACK并結束通信?但這一行為我不確定是否可靠,建議等周一官方工作人員回復下。
我在手冊里看到這樣一個(gè)寄存器,是解決這個(gè)問(wèn)題的,如下圖。
初始化的時(shí)候加上這一句就行了 I2C_DMALastTransferCmd(I2C2,ENABLE);
“如果最后一個(gè)字節沒(méi)有被DMA取走,主機會(huì )自動(dòng)發(fā)NACK并結束通信?” 我在DMA完成中斷里發(fā)送了STOP。
解決了上述問(wèn)題,依然有兩個(gè)BUG,幫忙看看是哪里的問(wèn)題:
1.紅色圈的地方,邏輯分析儀可以看到接受了4個(gè)數據,但print接受數字時(shí),第一個(gè)字節沒(méi)有正確輸出。
2.紫色圈出來(lái)的位置,從邏輯分析儀看發(fā)送了5個(gè)字節(包括寄存器),但程序里TX的長(cháng)度寫(xiě)成6才能正確發(fā)送4個(gè)字節。
附件是代碼。
1.紅色圈的地方,邏輯分析儀可以看到接受了4個(gè)數據,但print接受數字時(shí),第一個(gè)字節沒(méi)有正確輸出。
針對這個(gè)問(wèn)題如下代碼中加了一個(gè)延時(shí)解決了,但不明白為什么會(huì )這樣,如果不加延時(shí)該怎么處理?
void logan_i2c2_rx(u16 slave_add, u16 reg)
{
? ? while( I2C_GetFlagStatus( I2C2, I2C_FLAG_BUSY ) != RESET );
? ? I2C_GenerateSTART( I2C2, ENABLE);
? ? while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_MODE_SELECT ) );
? ? logan_send_addr( I2C2, slave_add, I2C_Direction_Transmitter);
? ? while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );
#if (Address_Lenth? == Address_8bit)
? ? I2C_SendData( I2C2, (u8) (reg & 0x00FF));
? ? while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
#elif (Address_Lenth? == Address_16bit)
? ? I2C_SendData( I2C1, (u8)(reg>>8) );
? ? while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
? ? I2C_SendData( I2C1, (u8)(reg&0x00FF) );
? ? while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
#endif
? ? I2C_GenerateSTART( I2C2, ENABLE);
? ? while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_MODE_SELECT ) );
? ? logan_send_addr( I2C2, slave_add, I2C_Direction_Receiver);
? ? while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) );
? ? Delay_Us(100);
? ? DMA_Cmd( DMA1_Channel5, ENABLE );
}
由于我手上并無(wú)V203的硬件,以下均為本人按照經(jīng)驗所做的推測,請酌情參考。
對于問(wèn)題1,我沒(méi)有什么頭緒,不確定是否與問(wèn)題2有關(guān),建議只保留讀取邏輯再次測試。
對于問(wèn)題2,我個(gè)人認為是因為DMA發(fā)出最后一字節后,該字節數據剛剛裝入I2C數據寄存器還未來(lái)得及發(fā)送,DMA就因數據已全部傳輸完畢而觸發(fā)TC中斷,而程序中TC中斷立即發(fā)出了STOP信號,故I2C外設未來(lái)得及發(fā)出該字節即產(chǎn)生了停止序列。故,可在只寫(xiě)入目標器件的操作時(shí),在DMA傳輸完畢的中斷內開(kāi)啟I2C的BTF中斷,并在I2C的BTF中斷內再發(fā)出STOP信號。
您好,@雷龍飛-Logan.lei,麻煩具體說(shuō)明一下你的程序如何測試使用,我這邊打開(kāi)你的程序發(fā)現沒(méi)有做主從的區分,下載到測試板邏輯分析儀并沒(méi)有采到波形,在收發(fā)通信時(shí)需要把另外收發(fā)代碼注釋掉么??梢愿遥╨zs@wch.cn)描述一下使用方法,我這邊具體測試一下?;蛭覀僔203 EVT有IIC DMA收發(fā)的例程,你也可以參考一下。
不用注釋掉,我是CH32V203做主機,通過(guò)I2C2(PB10,PB11)對從機AW9523BTQR進(jìn)行讀寫(xiě)的。
您好,附件是修改后的例程,關(guān)于發(fā)送長(cháng)度以及接收問(wèn)題這邊測試修改后是沒(méi)有問(wèn)題的,如下圖。由于沒(méi)有你所說(shuō)的從機模塊。例程是基于對EEPROM的讀寫(xiě)測試的,所以對設備地址進(jìn)行了修改。你拿回去測試時(shí)注意地址要改過(guò)來(lái)。
問(wèn)題一。 打印讀取到的I2C數據時(shí), 第一個(gè)數據丟失。是因為DMA還沒(méi)有傳輸完成??梢栽谠O置標志位等傳輸完成在打印。
I2C狀態(tài)轉換,全放在中斷里面進(jìn)行。
I2C DMA主機模式測試沒(méi)問(wèn)題 。從機 MP4247可以正常通訊 。
I2C DMA從機模式不完整未測試。
大佬們可以?xún)?yōu)化一下。我這寫(xiě)的有點(diǎn)爛。
#define I2C_OUTTIME 70000
uint8_t i2cDataTx[Size] = { 0 };
uint8_t i2cDataRx[Size];
void I2C1_EV_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DMA1_Channel7_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
//void DMA1_Channel6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void I2C1_ER_IRQHandler(void)? __attribute__((interrupt("WCH-Interrupt-fast")));
//波特率? 本機地址 (1
void IIC_Init(u32 bound, uint8_t myadr)
{
? ? GPIO_InitTypeDef GPIO_InitStructure={0};
? ? I2C_InitTypeDef I2C_InitTSturcture={0};
? ? RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
? ? RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );
? ? RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
? ? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
? ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
? ? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
? ? GPIO_Init( GPIOB, &GPIO_InitStructure );
? ? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
? ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
? ? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
? ? GPIO_Init( GPIOB, &GPIO_InitStructure );
? ? I2C_InitTSturcture.I2C_ClockSpeed = bound;
? ? I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
? ? I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_2;
? ? I2C_InitTSturcture.I2C_OwnAddress1 = myadr?myadr<<1:MYAdderss<<1;
? ? I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
? ? I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
? ? I2C_Init( I2C1, &I2C_InitTSturcture );
? ? DMA_InitTypeDef DMA_InitStructure={0};
? ? DMA_DeInit(DMA1_Channel7);
? ? DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )&I2C1->DATAR;
? ? DMA_InitStructure.DMA_MemoryBaseAddr = ( u32)&i2cDataRx;
? ? DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
? ? DMA_InitStructure.DMA_BufferSize = Size;
? ? DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
? ? DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
? ? DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
? ? DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
? ? DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
? ? DMA_InitStructure.DMA_Priority = DMA_Priority_High;
? ? DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
? ? DMA_Init(DMA1_Channel7, &DMA_InitStructure );
? ? DMA_DeInit(DMA1_Channel6);
? ? DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )&I2C1->DATAR;
? ? DMA_InitStructure.DMA_MemoryBaseAddr =( u32)&i2cDataTx;
? ? DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
? ? DMA_InitStructure.DMA_BufferSize = Tize;
? ? DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
? ? DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
? ? DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
? ? DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
? ? DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
? ? DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//DMA_Priority_High;DMA_Priority_VeryHigh
? ? DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
? ? DMA_Init( DMA1_Channel6, &DMA_InitStructure );
? ? I2C_DMACmd( I2C1, ENABLE );
? ? NVIC_InitTypeDef? NVIC_InitStructure = {0};
? ? NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;//事件中斷
? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
? ? NVIC_Init(&NVIC_InitStructure);
? ? NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
//? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
? ? NVIC_Init(&NVIC_InitStructure);
//? ? NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
? ? NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;//錯誤中斷
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority = 7;? ? ? ? ? ? ? ? ?//從優(yōu)先級
? ? NVIC_Init(&NVIC_InitStructure);
//? ? DMA_ITConfig(DMA1_Channel7, DMA1_IT_TC1, ENABLE);
? ? DMA1_Channel7->CFGR|=0x0002;//接收完成中斷
//? ? DMA1_Channel6->CFGR|=0x0002;//發(fā)送完成中斷
? ? I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);//I2C_IT_EVT 開(kāi)啟事件中斷 I? 2C_IT_BUF|接收發(fā)送緩沖器中斷? I2C_IT_ERR 錯誤中斷
? ? if(myadr)
? ? {
? ? ? ? I2C_GeneralCallCmd(I2C1,ENABLE);//響應廣播
? ? }
? ? I2C_Cmd( I2C1, ENABLE );
//? ? I2C_AcknowledgeConfig( I2C1, ENABLE );
? ? Rst_i2c_salve();
}
uint8_t I2C_READ_REG8(uint8_t addr, uint8_t reg, uint8_t* rbuf, uint16_t rsize)
{
//[狀態(tài)(bit7 1寫(xiě)讀數據 0僅寫(xiě)數據 其他位為狀態(tài)指示),發(fā)送數據尺寸H,發(fā)送數據尺寸L,讀取數據尺寸H,讀取數據尺寸L,發(fā)送地址? ?,(寫(xiě)寄存器|讀寄存器|指令|數據1),(數據2),...]
? ? static uint8_t data[6+2];
? ? uint32_t time_out=I2C_OUTTIME;
//? ? I2C1->CTLR1 |= (uint16_t)0x0001;
? ? while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ) //等待I2C空閑
? ? {
? ? ? ? time_out--;
? ? ? ? if(time_out==0)
? ? ? ? {
? ? ? ? ? ? Rst_i2c_salve();
? ? ? ? ? ? return I2C_BUSY;
? ? ? ? }
? ? }
? ? data[0] = 0x80;//寫(xiě)讀
? ? data[1] = 0;//發(fā)送數據字節數 高位
? ? data[2] = 1;//發(fā)送數據字節數 低位
? ? data[3] = (rsize>>8)&0xFF;//接收字節數 高位
? ? data[4] = rsize&0xFF;//接收字節數 低位
? ? data[5] = addr<<1;//從機地址
? ? data[6] = reg;//寫(xiě)數據
? ? //DMA接收設置
? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? DMA1_Channel7->CNTR = 0;
? ? DMA1_Channel7->MADDR = (uint32_t)(rbuf);
? ? //DMA發(fā)送設置
? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? DMA1_Channel6->CNTR = 7;
? ? DMA1_Channel6->MADDR = (uint32_t)(data);
? ? I2C_GenerateSTART(I2C1, ENABLE);//轉換到主機模式? 發(fā)起起始條件成功 SB狀態(tài)置1? MSL狀態(tài)置1表明當前為主機模式
? ? time_out = I2C_OUTTIME;
? ? while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ))//等待傳輸完成 BTF=1 TXE=1? ?MSL=1 TRA=1 BUSY=1
? ? {
? ? ? ? time_out--;
? ? ? ? if(time_out==0)
? ? ? ? {
? ? ? ? ? ? Rst_i2c_salve();
? ? ? ? ? ? return I2C_ERROR;
? ? ? ? }
? ? }
? ? return 0;
}
uint8_t I2C_WRITE_REG8(uint8_t addr, uint8_t reg, uint8_t*wbuf, uint16_t wsize)
{
//[狀態(tài)(bit7 1寫(xiě)讀數據 0僅寫(xiě)數據 其他位為狀態(tài)指示),發(fā)送數據尺寸H,發(fā)送數據尺寸L,讀取數據尺寸H,讀取數據尺寸L,發(fā)送地址? ?,(寫(xiě)寄存器|讀寄存器|指令|數據1),(數據2),...]
? ? static uint8_t data[6+8];
? ? uint32_t i;
? ? uint32_t time_out=I2C_OUTTIME;
? ? wsize=wsize+1;
//? ? I2C1->CTLR1 |= (uint16_t)0x0001;
? ? while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ) //等待I2C空閑
? ? {
? ? ? ? time_out--;
? ? ? ? if(time_out==0)
? ? ? ? {
//? ? ? ? ? ? if(DMA1_Channel6->CNTR==0 && DMA1_Channel7->CNTR==0)
//? ? ? ? ? ? {
? ? ? ? ? ? Rst_i2c_salve();
//? ? ? ? ? ? }
? ? ? ? ? ? return I2C_BUSY;
? ? ? ? }
? ? }
? ? if(wsize>8)
? ? {
? ? ? ? return 8;
? ? }
? ? data[0] = 0x00;//僅寫(xiě)
? ? data[1] = (wsize>>8)&0xFF;//發(fā)送數據字節數 高位
? ? data[2] = wsize&0xFF;//發(fā)送數據字節數 低位
? ? data[3] = 0;//接收字節數 高位
? ? data[4] = 0;//接收字節數 低位
? ? data[5] = addr<<1;//從機地址
? ? data[6] = reg;//從機寄存器地址或指令
? ? for (i = 0; i < wsize; ++i) {
? ? ? ? data[i+7] = wbuf[i];
? ? }
? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? DMA1_Channel6->CNTR = wsize;
? ? DMA1_Channel6->MADDR = (uint32_t)(data);
? ? I2C_GenerateSTART(I2C1, ENABLE);
? ? return 0;
}
void I2C1_EV_IRQHandler(void)
{
? ? volatile uint32_t temp = 0;
? ? ///////////////DMA///////////////////
? ? static uint8_t *_status;
? ? volatile uint32_t temp1 = 0;
? ? volatile uint32_t temp2 = 0;
? ? temp1 = I2C1->STAR1;//讀狀態(tài)1寄存器
? ? temp2 = I2C1->STAR2;//讀狀態(tài)2寄存器
? ? temp2 = temp2<<16;
? ? temp = (temp1|temp2);
? ? if((temp&I2C_FLAG_MSL)==I2C_FLAG_MSL)//主機模式
? ? {
? ? ? ? if((temp&I2C_IT_SB))//起始條件已發(fā)出 開(kāi)始發(fā)送從機地址
? ? ? ? {
? ? ? ? ? ? if(DMA1_Channel6->CNTR)//首次發(fā)送數據 更新保存 數據基址 讀從機寄存器操作(先寫(xiě)指令再讀數據)第二次起始條件發(fā)出時(shí) 不應該再觸發(fā)該處
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _status = (uint8_t *)DMA1_Channel6->MADDR;
? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 藍色
? ? ? ? ? ? }
? ? ? ? ? ? I2C1->DATAR = _status[5];//bit0 0發(fā)送或讀取地址bit0 1 寫(xiě)地址后會(huì )清除SB
? ? ? ? ? ? _status[0]=(_status[0]&0x80)|1;//更新?tīng)顟B(tài) 發(fā)送地址
? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_14, 0);//LED2 綠色
? ? ? ? ? ? // GPIO_WriteBit(GPIOB, GPIO_Pin_15, 0);//LED3 紅燈
? ? ? ? }
//? ? ? ? ? ADDR 用戶(hù)讀取狀態(tài)寄存器 1 后,對狀態(tài)寄存器 2 的讀操作將會(huì )清除此位
? ? ? ? if(temp&I2C_IT_ADDR)//從機地址匹配(應答) 開(kāi)始寫(xiě)或者讀數據
? ? ? ? {
? ? ? ? ? ? //開(kāi)始DMA傳輸
? ? ? ? ? ? if(DMA1_Channel6->CNTR)//發(fā)送
? ? ? ? ? ? {
//? ? ? ? ? ? ? ? [狀態(tài)(bit7 1寫(xiě)讀數據 0僅寫(xiě)數據 其他位為狀態(tài)指示),發(fā)送數據尺寸H,發(fā)送數據尺寸L,讀取數據尺寸H,讀取數據尺寸L,發(fā)送地址,(寫(xiě)寄存器|讀寄存器|指令|數據1),(數據2),...]
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? ? ? ? ? ? ? if((_status[0]&0x80))//寫(xiě)讀
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? _status[5] = _status[5]|0x0001;//轉換為讀地址
? ? ? ? ? ? ? ? ? ? _status[0] = (_status[0]&0x80)|2;//更新?tīng)顟B(tài)為讀 等待數據傳輸完成
? ? ? ? ? ? ? ? ? ? DMA1_Channel7->CNTR = (_status[3]<<8)|_status[4];
? ? ? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 0);//LED1 藍色
? ? ? ? ? ? ? ? }else {//僅寫(xiě)
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|3;//更新?tīng)顟B(tài)為僅寫(xiě)? 等待數據傳輸完成
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? DMA1_Channel6->CNTR = (_status[1]<<8)|_status[2];
? ? ? ? ? ? ? ? DMA1_Channel6->MADDR = (uint32_t)(_status+6);
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//開(kāi)啟DMA指定通道
? ? ? ? ? ? }else if(DMA1_Channel7->CNTR)//接收
? ? ? ? ? ? {
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
//? ? ? ? ? ? ? ? I2C1->CTLR1 = ((uint16_t)0x0401);//開(kāi)啟應答
? ? ? ? ? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0400);//開(kāi)啟應答
? ? ? ? ? ? ? ? I2C1->CTLR2 |= ((uint16_t)0x1000);//主機接收時(shí) DMA傳輸 最后一位NACK DMA接收不觸發(fā)BTF 需要開(kāi)啟DMA傳輸完成中斷處理發(fā)送停止事件
? ? ? ? ? ? ? ? DMA1_Channel7->CFGR |= DMA_CFGR1_EN;//開(kāi)啟DMA指定通道
? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
? ? ? ? ? ? }
? ? ? ? }
//? ? ? ? BTF字節發(fā)送結束標志位,用戶(hù)讀取狀態(tài)寄存器 1后,對數據寄存器的讀寫(xiě)將清除此位;
? ? ? ? if(temp&I2C_IT_BTF)//數據傳輸完成 ->? 一個(gè)數據傳輸完成 (硬件延時(shí)超時(shí)) (發(fā)送 數據寄存器空) (接收 數據寄存器有數據未被讀取)觸發(fā)完成事件
? ? ? ? {
? ? ? ? ? ? (void)I2C1->DATAR;
? ? ? ? ? ? switch (_status[0]&0x7F) {
? ? ? ? ? ? ? ? case 1://地址發(fā)送完成 等待地址匹配 在此不做任何事情
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case 2://寫(xiě)讀數據狀態(tài) 開(kāi)始讀
? ? ? ? ? ? ? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
//? ? ? ? ? ? ? ? ? ? I2C1->CTLR1 = ((uint16_t)0x0101);//再次發(fā)出開(kāi)始信號 準備讀取
? ? ? ? ? ? ? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0100);//再次發(fā)出開(kāi)始信號 準備讀取
? ? ? ? ? ? ? ? ? ? DMA1_Channel6->CNTR = (_status[3]<<8)|_status[4];//讀取長(cháng)度
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case 3://僅寫(xiě)數據? ?完成
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
//? ? ? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 藍色
? ? ? ? ? ? ? ? case 4:// 讀寫(xiě)完成
//? ? ? ? ? ? ? ? ? ? ?I2C1->CTLR1 = ((uint16_t)0x0201);
? ? ? ? ? ? ? ? ? ? ?I2C1->CTLR1 ^=((uint16_t)0x0400);// 取消應答
? ? ? ? ? ? ? ? ? ? ?I2C1->CTLR1 |= ((uint16_t)0x0200);// 發(fā)起停止事件 轉變?yōu)閺臋C模式
? ? ? ? ? ? ? ? ? ? ?_status[0]=(_status[0]&0x80)|5;
? ? ? ? ? ? ? ? ? ? ?DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? ? ? ? ? ? ? ? ? ?DMA1_Channel6->CNTR = 0;
//? ? ? ? ? ? ? ? ? ? ?DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
//? ? ? ? ? ? ? ? ? ? ?if((_status[0]&0x7F)==4)GPIO_WriteBit(GPIOB, GPIO_Pin_14, 1);//LED2 綠燈
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
? ? }else {//從機模式 I2C_IT_ADDR? I2C_IT_BTF? I2C_IT_STOPF
//? ? ? ? if(temp&I2C_IT_BTF)
//? ? ? ? {
//
//? ? ? ? }
? ? ? ? if((temp&I2C_IT_STOPF)||(temp&I2C_IT_BTF))//從機檢測到停止事件 或 發(fā)送完成標志
? ? ? ? {
? ? ? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? ? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? ? ? }
? ? ? ? if(temp&I2C_IT_ADDR)
? ? ? ? {
? ? ? ? ? ? //主機請求數據 重復的起始條件、停止條件會(huì )清除 仲裁丟失、關(guān)閉I2C 該位硬件清零
? ? ? ? ? ? if(temp&I2C_FLAG_TRA)//主機請求數據 根據接收的值準備數據上傳
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //從機 DMA發(fā)送數據 外部設置對應數據源
//? ? ? ? ? ? ? ?DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
//? ? ? ? ? ? ? ?DMA1_Channel6->MADDR = ( u32)&i2cDataTx;
//? ? ? ? ? ? ? ?DMA1_Channel6->CNTR = Tize;
? ? ? ? ? ? ? ?DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//開(kāi)啟DMA指定通道
? ? ? ? ? ? }else//主機寫(xiě)數據 從機接收
? ? ? ? ? ? {
? ? ? ? ? ? ? ? /* 準備接收數據 設置大一點(diǎn)的緩沖區 */
? ? ? ? ? ? ? ? DMA1_Channel7->MADDR = ( u32)&i2cDataRx;
? ? ? ? ? ? ? ? DMA1_Channel7->CNTR = Size;
? ? ? ? ? ? ? ? DMA1_Channel7->CFGR |= DMA_CFGR1_EN;
? ? ? ? ? ? }
? ? ? ? }
? ? }
//////////////////DMA///////////////////
}
//SCL為高電平時(shí),SDA由高變低表示起始信號;
//
//SCL為高電平時(shí),SDA由低變高表示停止信號;
//
//起始信號和停止信號都是由主機發(fā)出,起始信號產(chǎn)生后總線(xiàn)處于占用狀態(tài),停止信號產(chǎn)生后總線(xiàn)被釋放,處于空閑狀態(tài)。
// 錯誤中斷
void I2C1_ER_IRQHandler(void)
{
? ? uint32_t temp = 0;
? ? I2C_GenerateSTOP(I2C1, ENABLE);
? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉DMA指定通道
? ? temp = I2C1->STAR1;
? ? I2C_Cmd(I2C1, DISABLE);
? ? if(temp&0x0400)//應答錯誤
? ? {
? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 0);//LED1 藍色
? ? }else if (temp&0x0100)//仲裁丟失
? ? {
? ? }else if (temp&0x4000)//超時(shí)
? ? {
? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_15, 1);//LED3 紅燈
? ? }else if (temp&0x0200)//過(guò)載? 禁止時(shí)鐘延長(cháng)條件下
? ? {
? ? }else {
? ? }
? ? I2C_Cmd(I2C1, ENABLE);
}
//傳輸完成的回調函數
//extern void I2C_SlaveDMARxCpltCallback();
//I2C DMA接收
void DMA1_Channel7_IRQHandler(void)
{
? ? if(DMA_GetITStatus(DMA1_IT_TC7)==SET)
? ? {
? ? ? ? //? ? ? ? 從機 狀態(tài)下 應該解析更新數據
? ? ? ? //? ? ? ? I2C_SlaveDMARxCpltCallback();
? ? ? ? I2C1->CTLR1 ^=((uint16_t)0x0400);// 取消應答
? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0200);// 發(fā)起停止事件 轉換為從機模式? 靜默
? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉dma指定通道
? ? ? ? DMA_ClearITPendingBit(DMA1_IT_TC7);
? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_14, 1);//LED2 綠色
? ? }
}
//I2C DMA 發(fā)送完成
//void DMA1_Channel6_IRQHandler(void)
//{
//? ? if(DMA_GetITStatus(DMA1_IT_TC6)==SET)
//? ? {
//? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關(guān)閉dma指定通道
//? ? ? ? DMA_ClearITPendingBit(DMA1_IT_TC6);
//
//? ? }
//}
//主機模式 嘗試復位IIC
void Rst_i2c_salve()
{
? ? uint8_t n = 9;
//? ? GPIOB->CFGLR &= 0x00FFFFFF;// 復位
//? ? GPIOB->CFGLR |= (uint32_t)0x44000000;//輸入模式 PB6 PB7
//? ? printf("IDR:0x%08X? GPIOB:0x%08X\n",GPIOB->INDR,GPIOB->CFGLR);
? ? if((GPIOB->INDR&0x00000080)==0)//讀取SDA 總線(xiàn) 如果被拉低
? ? {
? ? ? ? I2C_GenerateSTART( I2C1, ENABLE );
? ? ? ? Delay_Ms(5);
? ? ? ? I2C_GenerateSTOP( I2C1, ENABLE );
? ? ? ? Delay_Ms(20);
? ? ? ? if((GPIOB->INDR&0x00000080))// SDA 為高 退出
? ? ? ? {
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? I2C_Cmd( I2C1, DISABLE );
? ? ? ? GPIOB->CFGLR &= 0x00FFFFFF;// 復位
//? ? ? ? GPIOB->CFGLR |= (uint32_t)0x33000000;//輸出模式 PB6 PB7 通用推挽輸出 50 MHz
? ? ? ? GPIOB->CFGLR |= (uint32_t)0x77000000;//輸出模式 PB6 PB7 通用開(kāi)漏輸出 50 MHz
//? ? ? ? GPIOB->OUTDR |= 0x000000C0;
? ? ? ? while(n>0)//發(fā)送9個(gè)時(shí)鐘信號
? ? ? ? {
? ? ? ? ? ? GPIOB->OUTDR |= 0x00000040;//PB6 SCL 拉高
? ? ? ? ? ? Delay_Ms(5);
? ? ? ? ? ? GPIOB->OUTDR &= ~0x00000040;// SCL 拉低
? ? ? ? ? ? n--;
? ? ? ? ? ? if((GPIOB->INDR&0x00000080))// SDA 為高 退出
? ? ? ? ? ? {
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
//? ? ? ? GPIOB->OUTDR &= ~0x00000080; //PB7 SDA 拉低
//? ? ? ? GPIOB->OUTDR |= 0x00000040;//SCL 拉高
//? ? ? ? Delay_Ms(20);
//? ? ? ? GPIOB->OUTDR |= 0x00000080; //PB7 SDA 拉高
? ? //? ? GPIOB->CFGLR &= 0x00FFFFFF;// 復位
? ? ? ? GPIOB->CFGLR |= 0xFF000000;// 復用 開(kāi)漏輸出 50 MHz
? ? ? ? I2C1->STAR1 = 0;
? ? ? ? I2C1->STAR2 = 0;
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }else if ((GPIOB->INDR&0x00000040)==0){//SCL 總線(xiàn)被拉低 嘗試 恢復
? ? ? ? I2C_GenerateSTOP( I2C1, ENABLE );
? ? ? ? Delay_Ms(10);
? ? ? ? I2C_Cmd( I2C1, DISABLE );
? ? ? ? I2C1->STAR1 = 0;
? ? ? ? I2C1->STAR2 = 0;
? ? ? ? while((GPIOB->INDR&0x00000040)==0);//等待SCL總線(xiàn) 拉起
//? ? ? ? Delay_Ms(500);
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }else {
? ? ? ? I2C_Cmd( I2C1, DISABLE );
? ? ? ? I2C1->DATAR = 0;
? ? ? ? Delay_Ms(100);
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }
}