我用I2C2 DMA讀取從機寄存器,問什么總是比我要讀取的多了一個字節(jié)?
我代碼里寫了DMA?bufsize是1,但邏輯分析儀看到的是讀回來兩個字節(jié),請幫忙看看是哪里配置出錯了么?
? ? logan_DMA_I2C2_RX_Init((u32)RxData,1);
? ? logan_i2c2_rx(0x5b, 0x12);
我用I2C2 DMA讀取從機寄存器,問什么總是比我要讀取的多了一個字節(jié)?
我代碼里寫了DMA?bufsize是1,但邏輯分析儀看到的是讀回來兩個字節(jié),請幫忙看看是哪里配置出錯了么?
? ? logan_DMA_I2C2_RX_Init((u32)RxData,1);
? ? logan_i2c2_rx(0x5b, 0x12);
這是由于沒有提前關閉I2C的自動應答ACK造成的。DMA配置的長度只決定DMA會從I2C外設搬運幾個數(shù)據(jù),而I2C接收多少數(shù)據(jù)是由I2C邏輯控制的。瀏覽代碼發(fā)現(xiàn)I2C接收第一個字節(jié)前并未關閉自動應答ACK。這就造成從機收到主機的ACK從而認為主機需要再讀1字節(jié)而繼續(xù)發(fā)送,主機無法發(fā)出停止條件。
根據(jù)CH32FV2x_V3xRM的275頁所述:
“在接收模式時,主設備需要在最后一個數(shù)據(jù)位的應答位置 NAK,接收到 NACK 后,從設備釋放對 SCL 和 SDA 線的控制”
故對于只讀1字節(jié)的情況,需要在讀取之前關閉自動應答ACK。對于只讀取1字節(jié)的情況,代碼應如下修改:
while(?!I2C_CheckEvent(?I2C2,?I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED?)?);?//?原有代碼 I2C_AcknowledgeConfig(?I2C2,?DISABLE?);?//?新增代碼,提前關閉自動應答ACK DMA_Cmd(?DMA1_Channel5,?ENABLE?);?//?原有代碼
而對于讀取2字節(jié)或更多的情況,自動應答ACK應等到還剩1字節(jié)待讀取時再關閉。
另外附上一個X035系列I2C邏輯的狀態(tài)機實現(xiàn)代碼,樓主可以參考:(鏈接會直接跳轉到讀時序的接收階段,可以看到若只剩1字節(jié)需要接收時,程序中關閉自動應答ACK的功能)
https://github.com/WuxiProject-offical/CH32X035-HelperLibrary/blob/main/I2C/Master/i2c.c#L617
灰常感謝,那如果用DMA的話有什么方法可以處理這個“BUG”,總不能去一直循環(huán)查詢還剩多少個數(shù)據(jù)吧?
我用某業(yè)內主流32位MCU時遇到類似的問題,我的策略是DMA長度比需要讀取的長度少一個,在DMA的傳輸完成中斷里關閉自動應答ACK,然后最后一字節(jié)用中斷接收;如果只讀1字節(jié),直接用中斷讀取而不開DMA。
但V203上,似乎可以卡bug做一個非常騷的操作,即直接設置DMA長度為比需要讀取的長度少一個,不在DMA傳輸完成中斷里添加代碼。按你的測試現(xiàn)象來看,如果最后一個字節(jié)沒有被DMA取走,主機會自動發(fā)NACK并結束通信?但這一行為我不確定是否可靠,建議等周一官方工作人員回復下。
我在手冊里看到這樣一個寄存器,是解決這個問題的,如下圖。
初始化的時候加上這一句就行了 I2C_DMALastTransferCmd(I2C2,ENABLE);
“如果最后一個字節(jié)沒有被DMA取走,主機會自動發(fā)NACK并結束通信?” 我在DMA完成中斷里發(fā)送了STOP。
解決了上述問題,依然有兩個BUG,幫忙看看是哪里的問題:
1.紅色圈的地方,邏輯分析儀可以看到接受了4個數(shù)據(jù),但print接受數(shù)字時,第一個字節(jié)沒有正確輸出。
2.紫色圈出來的位置,從邏輯分析儀看發(fā)送了5個字節(jié)(包括寄存器),但程序里TX的長度寫成6才能正確發(fā)送4個字節(jié)。
附件是代碼。
1.紅色圈的地方,邏輯分析儀可以看到接受了4個數(shù)據(jù),但print接受數(shù)字時,第一個字節(jié)沒有正確輸出。
針對這個問題如下代碼中加了一個延時解決了,但不明白為什么會這樣,如果不加延時該怎么處理?
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 );
}
由于我手上并無V203的硬件,以下均為本人按照經驗所做的推測,請酌情參考。
對于問題1,我沒有什么頭緒,不確定是否與問題2有關,建議只保留讀取邏輯再次測試。
對于問題2,我個人認為是因為DMA發(fā)出最后一字節(jié)后,該字節(jié)數(shù)據(jù)剛剛裝入I2C數(shù)據(jù)寄存器還未來得及發(fā)送,DMA就因數(shù)據(jù)已全部傳輸完畢而觸發(fā)TC中斷,而程序中TC中斷立即發(fā)出了STOP信號,故I2C外設未來得及發(fā)出該字節(jié)即產生了停止序列。故,可在只寫入目標器件的操作時,在DMA傳輸完畢的中斷內開啟I2C的BTF中斷,并在I2C的BTF中斷內再發(fā)出STOP信號。
您好,@雷龍飛-Logan.lei,麻煩具體說明一下你的程序如何測試使用,我這邊打開你的程序發(fā)現(xiàn)沒有做主從的區(qū)分,下載到測試板邏輯分析儀并沒有采到波形,在收發(fā)通信時需要把另外收發(fā)代碼注釋掉么??梢愿遥╨zs@wch.cn)描述一下使用方法,我這邊具體測試一下?;蛭覀僔203 EVT有IIC DMA收發(fā)的例程,你也可以參考一下。
不用注釋掉,我是CH32V203做主機,通過I2C2(PB10,PB11)對從機AW9523BTQR進行讀寫的。
您好,附件是修改后的例程,關于發(fā)送長度以及接收問題這邊測試修改后是沒有問題的,如下圖。由于沒有你所說的從機模塊。例程是基于對EEPROM的讀寫測試的,所以對設備地址進行了修改。你拿回去測試時注意地址要改過來。
問題一。 打印讀取到的I2C數(shù)據(jù)時, 第一個數(shù)據(jù)丟失。是因為DMA還沒有傳輸完成??梢栽谠O置標志位等傳輸完成在打印。
I2C狀態(tài)轉換,全放在中斷里面進行。
I2C DMA主機模式測試沒問題 。從機 MP4247可以正常通訊 。
I2C DMA從機模式不完整未測試。
大佬們可以優(yōu)化一下。我這寫的有點爛。
#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 開啟事件中斷 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寫讀數(shù)據(jù) 0僅寫數(shù)據(jù) 其他位為狀態(tài)指示),發(fā)送數(shù)據(jù)尺寸H,發(fā)送數(shù)據(jù)尺寸L,讀取數(shù)據(jù)尺寸H,讀取數(shù)據(jù)尺寸L,發(fā)送地址? ?,(寫寄存器|讀寄存器|指令|數(shù)據(jù)1),(數(shù)據(jù)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;//寫讀
? ? data[1] = 0;//發(fā)送數(shù)據(jù)字節(jié)數(shù) 高位
? ? data[2] = 1;//發(fā)送數(shù)據(jù)字節(jié)數(shù) 低位
? ? data[3] = (rsize>>8)&0xFF;//接收字節(jié)數(shù) 高位
? ? data[4] = rsize&0xFF;//接收字節(jié)數(shù) 低位
? ? data[5] = addr<<1;//從機地址
? ? data[6] = reg;//寫數(shù)據(jù)
? ? //DMA接收設置
? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
? ? DMA1_Channel7->CNTR = 0;
? ? DMA1_Channel7->MADDR = (uint32_t)(rbuf);
? ? //DMA發(fā)送設置
? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉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寫讀數(shù)據(jù) 0僅寫數(shù)據(jù) 其他位為狀態(tài)指示),發(fā)送數(shù)據(jù)尺寸H,發(fā)送數(shù)據(jù)尺寸L,讀取數(shù)據(jù)尺寸H,讀取數(shù)據(jù)尺寸L,發(fā)送地址? ?,(寫寄存器|讀寄存器|指令|數(shù)據(jù)1),(數(shù)據(jù)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;//僅寫
? ? data[1] = (wsize>>8)&0xFF;//發(fā)送數(shù)據(jù)字節(jié)數(shù) 高位
? ? data[2] = wsize&0xFF;//發(fā)送數(shù)據(jù)字節(jié)數(shù) 低位
? ? data[3] = 0;//接收字節(jié)數(shù) 高位
? ? data[4] = 0;//接收字節(jié)數(shù) 低位
? ? 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);//關閉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ā)出 開始發(fā)送從機地址
? ? ? ? {
? ? ? ? ? ? if(DMA1_Channel6->CNTR)//首次發(fā)送數(shù)據(jù) 更新保存 數(shù)據(jù)基址 讀從機寄存器操作(先寫指令再讀數(shù)據(jù))第二次起始條件發(fā)出時 不應該再觸發(fā)該處
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _status = (uint8_t *)DMA1_Channel6->MADDR;
? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 藍色
? ? ? ? ? ? }
? ? ? ? ? ? I2C1->DATAR = _status[5];//bit0 0發(fā)送或讀取地址bit0 1 寫地址后會清除SB
? ? ? ? ? ? _status[0]=(_status[0]&0x80)|1;//更新狀態(tài) 發(fā)送地址
? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_14, 0);//LED2 綠色
? ? ? ? ? ? // GPIO_WriteBit(GPIOB, GPIO_Pin_15, 0);//LED3 紅燈
? ? ? ? }
//? ? ? ? ? ADDR 用戶讀取狀態(tài)寄存器 1 后,對狀態(tài)寄存器 2 的讀操作將會清除此位
? ? ? ? if(temp&I2C_IT_ADDR)//從機地址匹配(應答) 開始寫或者讀數(shù)據(jù)
? ? ? ? {
? ? ? ? ? ? //開始DMA傳輸
? ? ? ? ? ? if(DMA1_Channel6->CNTR)//發(fā)送
? ? ? ? ? ? {
//? ? ? ? ? ? ? ? [狀態(tài)(bit7 1寫讀數(shù)據(jù) 0僅寫數(shù)據(jù) 其他位為狀態(tài)指示),發(fā)送數(shù)據(jù)尺寸H,發(fā)送數(shù)據(jù)尺寸L,讀取數(shù)據(jù)尺寸H,讀取數(shù)據(jù)尺寸L,發(fā)送地址,(寫寄存器|讀寄存器|指令|數(shù)據(jù)1),(數(shù)據(jù)2),...]
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
? ? ? ? ? ? ? ? if((_status[0]&0x80))//寫讀
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? _status[5] = _status[5]|0x0001;//轉換為讀地址
? ? ? ? ? ? ? ? ? ? _status[0] = (_status[0]&0x80)|2;//更新狀態(tài)為讀 等待數(shù)據(jù)傳輸完成
? ? ? ? ? ? ? ? ? ? DMA1_Channel7->CNTR = (_status[3]<<8)|_status[4];
? ? ? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 0);//LED1 藍色
? ? ? ? ? ? ? ? }else {//僅寫
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|3;//更新狀態(tài)為僅寫? 等待數(shù)據(jù)傳輸完成
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? DMA1_Channel6->CNTR = (_status[1]<<8)|_status[2];
? ? ? ? ? ? ? ? DMA1_Channel6->MADDR = (uint32_t)(_status+6);
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//開啟DMA指定通道
? ? ? ? ? ? }else if(DMA1_Channel7->CNTR)//接收
? ? ? ? ? ? {
? ? ? ? ? ? ? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
//? ? ? ? ? ? ? ? I2C1->CTLR1 = ((uint16_t)0x0401);//開啟應答
? ? ? ? ? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0400);//開啟應答
? ? ? ? ? ? ? ? I2C1->CTLR2 |= ((uint16_t)0x1000);//主機接收時 DMA傳輸 最后一位NACK DMA接收不觸發(fā)BTF 需要開啟DMA傳輸完成中斷處理發(fā)送停止事件
? ? ? ? ? ? ? ? DMA1_Channel7->CFGR |= DMA_CFGR1_EN;//開啟DMA指定通道
? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
? ? ? ? ? ? }
? ? ? ? }
//? ? ? ? BTF字節(jié)發(fā)送結束標志位,用戶讀取狀態(tài)寄存器 1后,對數(shù)據(jù)寄存器的讀寫將清除此位;
? ? ? ? if(temp&I2C_IT_BTF)//數(shù)據(jù)傳輸完成 ->? 一個數(shù)據(jù)傳輸完成 (硬件延時超時) (發(fā)送 數(shù)據(jù)寄存器空) (接收 數(shù)據(jù)寄存器有數(shù)據(jù)未被讀取)觸發(fā)完成事件
? ? ? ? {
? ? ? ? ? ? (void)I2C1->DATAR;
? ? ? ? ? ? switch (_status[0]&0x7F) {
? ? ? ? ? ? ? ? case 1://地址發(fā)送完成 等待地址匹配 在此不做任何事情
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case 2://寫讀數(shù)據(jù)狀態(tài) 開始讀
? ? ? ? ? ? ? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
//? ? ? ? ? ? ? ? ? ? I2C1->CTLR1 = ((uint16_t)0x0101);//再次發(fā)出開始信號 準備讀取
? ? ? ? ? ? ? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0100);//再次發(fā)出開始信號 準備讀取
? ? ? ? ? ? ? ? ? ? DMA1_Channel6->CNTR = (_status[3]<<8)|_status[4];//讀取長度
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case 3://僅寫數(shù)據(jù)? ?完成
? ? ? ? ? ? ? ? ? ? _status[0]=(_status[0]&0x80)|4;
//? ? ? ? ? ? ? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 藍色
? ? ? ? ? ? ? ? case 4:// 讀寫完成
//? ? ? ? ? ? ? ? ? ? ?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);//關閉DMA指定通道
? ? ? ? ? ? ? ? ? ? ?DMA1_Channel6->CNTR = 0;
//? ? ? ? ? ? ? ? ? ? ?DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉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);//關閉DMA指定通道
? ? ? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
? ? ? ? }
? ? ? ? if(temp&I2C_IT_ADDR)
? ? ? ? {
? ? ? ? ? ? //主機請求數(shù)據(jù) 重復的起始條件、停止條件會清除 仲裁丟失、關閉I2C 該位硬件清零
? ? ? ? ? ? if(temp&I2C_FLAG_TRA)//主機請求數(shù)據(jù) 根據(jù)接收的值準備數(shù)據(jù)上傳
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //從機 DMA發(fā)送數(shù)據(jù) 外部設置對應數(shù)據(jù)源
//? ? ? ? ? ? ? ?DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
//? ? ? ? ? ? ? ?DMA1_Channel6->MADDR = ( u32)&i2cDataTx;
//? ? ? ? ? ? ? ?DMA1_Channel6->CNTR = Tize;
? ? ? ? ? ? ? ?DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//開啟DMA指定通道
? ? ? ? ? ? }else//主機寫數(shù)據(jù) 從機接收
? ? ? ? ? ? {
? ? ? ? ? ? ? ? /* 準備接收數(shù)據(jù) 設置大一點的緩沖區(qū) */
? ? ? ? ? ? ? ? DMA1_Channel7->MADDR = ( u32)&i2cDataRx;
? ? ? ? ? ? ? ? DMA1_Channel7->CNTR = Size;
? ? ? ? ? ? ? ? DMA1_Channel7->CFGR |= DMA_CFGR1_EN;
? ? ? ? ? ? }
? ? ? ? }
? ? }
//////////////////DMA///////////////////
}
//SCL為高電平時,SDA由高變低表示起始信號;
//
//SCL為高電平時,SDA由低變高表示停止信號;
//
//起始信號和停止信號都是由主機發(fā)出,起始信號產生后總線處于占用狀態(tài),停止信號產生后總線被釋放,處于空閑狀態(tài)。
// 錯誤中斷
void I2C1_ER_IRQHandler(void)
{
? ? uint32_t temp = 0;
? ? I2C_GenerateSTOP(I2C1, ENABLE);
? ? DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉DMA指定通道
? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉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)//超時
? ? {
? ? ? ? GPIO_WriteBit(GPIOB, GPIO_Pin_15, 1);//LED3 紅燈
? ? }else if (temp&0x0200)//過載? 禁止時鐘延長條件下
? ? {
? ? }else {
? ? }
? ? I2C_Cmd(I2C1, ENABLE);
}
//傳輸完成的回調函數(shù)
//extern void I2C_SlaveDMARxCpltCallback();
//I2C DMA接收
void DMA1_Channel7_IRQHandler(void)
{
? ? if(DMA_GetITStatus(DMA1_IT_TC7)==SET)
? ? {
? ? ? ? //? ? ? ? 從機 狀態(tài)下 應該解析更新數(shù)據(jù)
? ? ? ? //? ? ? ? I2C_SlaveDMARxCpltCallback();
? ? ? ? I2C1->CTLR1 ^=((uint16_t)0x0400);// 取消應答
? ? ? ? I2C1->CTLR1 |= ((uint16_t)0x0200);// 發(fā)起停止事件 轉換為從機模式? 靜默
? ? ? ? DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//關閉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);//關閉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 總線 如果被拉低
? ? {
? ? ? ? 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 通用開漏輸出 50 MHz
//? ? ? ? GPIOB->OUTDR |= 0x000000C0;
? ? ? ? while(n>0)//發(fā)送9個時鐘信號
? ? ? ? {
? ? ? ? ? ? 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;// 復用 開漏輸出 50 MHz
? ? ? ? I2C1->STAR1 = 0;
? ? ? ? I2C1->STAR2 = 0;
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }else if ((GPIOB->INDR&0x00000040)==0){//SCL 總線被拉低 嘗試 恢復
? ? ? ? I2C_GenerateSTOP( I2C1, ENABLE );
? ? ? ? Delay_Ms(10);
? ? ? ? I2C_Cmd( I2C1, DISABLE );
? ? ? ? I2C1->STAR1 = 0;
? ? ? ? I2C1->STAR2 = 0;
? ? ? ? while((GPIOB->INDR&0x00000040)==0);//等待SCL總線 拉起
//? ? ? ? Delay_Ms(500);
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }else {
? ? ? ? I2C_Cmd( I2C1, DISABLE );
? ? ? ? I2C1->DATAR = 0;
? ? ? ? Delay_Ms(100);
? ? ? ? I2C_Cmd( I2C1, ENABLE );
? ? }
}