您的位置:首页 > 编程语言 > C语言/C++

【程序】STM32F1单片机I2C中Packet Error Checking(PEC)校验功能和DMA的使用方法

2017-07-24 20:20 525 查看
在STM32F1系列的单片机中,当I2C_CR1_ENPEC=1时启用CRC自动校验功能。注意这是一个自动校验的功能。发送方和接收方可以不同时开启自动校验,但发送方必须要发送CRC校验码,接收方也必须接收CRC校验码。

如果经过硬件检验后,接收到的内容完全正确,则双方的I2C_SR2_PEC中的内容(也就是SR2寄存器第15~8位)都应该为,否则表明程序流程没有写对。

发送端将最后一字节数据送入DR后,等待TXE置1,然后将I2C_CR1_PEC置位,注意此时TXE位将不会被自动清除

接收端从DR取出最后一字节数据后,立即将PEC置1。同时,如果是主机端在接收的话,还必须关掉ACK并发送STOP请求。收到CRC校验码后RXNE会置位

请看下面的程序。

两片单片机都是用的I2C1。主机端使用的单片机是STM32F107VCT6,端口为PB8和PB9。从机端用的是STM32F103RET6,端口为PB6和PB7。注意必须要外接上拉电阻,否则会出现arbitration lost的错误!

主机端的串口波特率为115200。(36000000Hz / 115200 ≈ 312 = 0x138)

从机端的串口波特率也为115200。(72000000Hz / 115200 = 625 = 0x271)

【主机端程序(寄存器版)】

#include <stdio.h>
#include <stm32f10x.h>

const uint8_t sendbuf[] = {0x40, 0x45, 0x52, 0x67};
uint8_t recvbuf[100];

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while ((USART3->SR & USART_SR_TXE) == 0);
USART3->DR = '\r';
}
while ((USART3->SR & USART_SR_TXE) == 0);
USART3->DR = ch;
}
return ch;
}

void test_write(void)
{
uint8_t i;
I2C1->CR1 |= I2C_CR1_START;
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x06;
while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
if (I2C1->SR1 & I2C_SR1_AF) // 从机复位键按住不放, 可模拟没有应答的情况
{
I2C1->SR1 &= ~I2C_SR1_AF;
I2C1->CR1 |= I2C_CR1_STOP; // 找不到从机时主机必须发出停止信号, 使BUSY清零, 释放总线
while (I2C1->CR1 & I2C_CR1_STOP);
printf("No ack!\n");
return;
}
I2C1->SR2; // 清除ADDR

for (i = 0; i < sizeof(sendbuf); i++)
{
I2C1->DR = sendbuf[i];
while ((I2C1->SR1 & I2C_SR1_TXE) == 0);
}
I2C1->CR1 |= I2C_CR1_PEC; // TXE不能被PEC清零!
while ((I2C1->SR1 & I2C_SR1_BTF) == 0);

I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1);
}

void test_write_dma(void)
{
DMA1_Channel6->CNDTR = sizeof(sendbuf); // 不包括PEC
DMA1_Channel6->CCR |= DMA_CCR6_EN;

I2C1->CR1 |= I2C_CR1_START;
I2C1->CR2 |= I2C_CR2_DMAEN; // 这里无需置位LAST, 因为LAST仅用于接收模式
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x06; // 从机地址
while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
I2C1->CR2 &= ~I2C_CR2_DMAEN;
DMA1_Channel6->CCR &= ~DMA_CCR6_EN;
I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);
printf("No ack!\n");
return;
}
I2C1->SR2; // 清除ADDR

while ((DMA1->ISR & DMA_ISR_TCIF6) == 0); // 等待DMA送入数据结束
DMA1->IFCR = DMA_IFCR_CTCIF6; // 清除标志位
I2C1->CR2 &= ~I2C_CR2_DMAEN;
DMA1_Channel6->CCR &= ~DMA_CCR6_EN; // I2C传输结束后应关闭DMA

while ((I2C1->SR1 & I2C_SR1_BTF) == 0); // DMA向I2C送入数据结束并不代表传输已结束, 必须等待BTF置位
// 注意: DMA_ISR_TCIF6比I2C_SR1_BTF先置位
// 如果只等待TCIF6, 则最后I2C_SR2_PEC将无法清零! 且接收端将丢失最后一字节数据和PEC校验码

I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP); // 必须等待该位清零后才允许后续程序对CR1的写操作
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1, DMA1->ISR);
}

void test_read(void)
{
uint8_t size;

I2C1->CR1 |= I2C_CR1_ACK | I2C_CR1_START;
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x07;
while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);
printf("No ack!\n");
return;
}
I2C1->SR2;

for (size = 0; size < 7; size++) // 范围为0~6, 其中0~5为数据, 6为PEC校验码
{
while ((I2C1->SR1 & I2C_SR1_RXNE) == 0);
recvbuf[size] = I2C1->DR;

if (size == 5)
{
I2C1->CR1 &= ~I2C_CR1_ACK;
I2C1->CR1 |= I2C_CR1_PEC | I2C_CR1_STOP; // 接收完最后一个字节后, 将PEC置位, 准备接收校验码 (此时已经正在接收PEC了)
}
}
while (I2C1->CR1 & I2C_CR1_STOP);

printf("Slave reception terminated! Data: ");
dump(recvbuf, 7);
if (I2C1->SR1 & I2C_SR1_PECERR)
{
I2C1->SR1 &= ~I2C_SR1_PECERR;
printf("Reception PEC Error! ");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1);
}

void test_read_dma(void)
{
DMA1_Channel7->CNDTR = 7; // 包括PEC
DMA1_Channel7->CCR |= DMA_CCR7_EN;

I2C1->CR1 |= I2C_CR1_ACK | I2C_CR1_START;
I2C1->CR2 |= I2C_CR2_LAST | I2C_CR2_DMAEN;
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x07; // 从机地址
while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
I2C1->CR2 &= ~(I2C_CR2_LAST | I2C_CR2_DMAEN);
DMA1_Channel7->CCR &= ~DMA_CCR7_EN;
I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);
printf("No ack!\n");
return;
}
I2C1->SR2;

while ((DMA1->ISR & DMA_ISR_TCIF7) == 0); // 等待DMA读取数据结束, 读取结束并不意味着数据传输结束
DMA1->IFCR = DMA_IFCR_CTCIF7;
I2C1->CR1 &= ~I2C_CR1_ACK; // 必须在接收PEC时关闭ACK!
I2C1->CR2 &= ~(I2C_CR2_LAST | I2C_CR2_DMAEN);
DMA1_Channel7->CCR &= ~DMA_CCR7_EN;

I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP); // 等待数据传输真正结束

printf("Slave reception terminated! Data: ");
dump(recvbuf, 7);
if (I2C1->SR1 & I2C_SR1_PECERR)
{
I2C1->SR1 &= ~I2C_SR1_PECERR;
printf("Reception PEC Error! ");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1, DMA1->ISR);
}

void test_write_wrongcrc(void)
{
uint8_t i;
I2C1->CR1 |= I2C_CR1_START;
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x06;
while ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);
printf("No ack!\n");
return;
}
I2C1->SR2;

for (i = 0; i < sizeof(sendbuf); i++)
{
I2C1->DR = sendbuf[i];
while ((I2C1->SR1 & I2C_SR1_TXE) == 0);
}

I2C1->DR = 0x00; // 发送一个错误的CRC校验码
while ((I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_BTF)) == 0); // 等待AF或BTF置1
I2C1->CR1 |= I2C_CR1_STOP;
while (I2C1->CR1 & I2C_CR1_STOP);

// 接收端发送非应答, 发送端知道接收端没有收到正确的数据
if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
printf("Not correctly received!\n");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1, DMA1->ISR);
// 发送端和接收端算出的I2C_SR2_PEC都是0x87(但发送端实际发送的校验码是0x00), 即数据和地址码的CRC校验码, PEC不为0表示出了错误
}

int main(void)
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // AHB寄存器有初值!
RCC->APB1ENR = RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_UART5EN;
RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_IOPEEN;

AFIO->MAPR = AFIO_MAPR_I2C1_REMAP;
GPIOB->CRH = 0x44444bff;
GPIOE->BSRR = GPIO_BSRR_BS15;
GPIOE->CRH = 0x34444444;

USART3->BRR = 312; // 波特率: 115200
USART3->CR1 = USART_CR1_UE | USART_CR1_TE;

UART5->BRR = 312; // 波特率: 115200
UART5->CR1 = USART_CR1_UE | USART_CR1_RE | USART_CR1_RXNEIE;
NVIC_EnableIRQ(UART5_IRQn);

I2C1->CR2 = 36;
I2C1->CCR = I2C_CCR_FS | 30;
I2C1->TRISE = 11;
I2C1->CR1 = I2C_CR1_ENPEC | I2C_CR1_PE;

DMA1_Channel6->CCR = DMA_CCR6_DIR | DMA_CCR6_MINC;
DMA1_Channel6->CMAR = (uint32_t)sendbuf;
DMA1_Channel6->CPAR = (uint32_t)&I2C1->DR;

DMA1_Channel7->CCR = DMA_CCR7_MINC;
DMA1_Channel7->CMAR = (uint32_t)recvbuf;
DMA1_Channel7->CPAR = (uint32_t)&I2C1->DR;

printf("I2C Master!\n");
while (1)
__WFI();
}

void UART5_IRQHandler(void)
{
uint8_t data = UART5->DR;
GPIOE->BRR = GPIO_BRR_BR15; // 灯亮
if (data == 'a')
test_write();
else if (data == 'b')
test_write_dma();
else if (data == 'c')
test_read();
else if (data == 'd')
test_read_dma();
else if (data == 'e')
test_write_wrongcrc();
GPIOE->BSRR = GPIO_BSRR_BS15; // 灯灭
}
【从机端程序:普通方式(寄存器版)】

#include <stdio.h>
#include <stm32f10x.h>

// 调试程序时, 先不使用PEC位, 检查程序是否能正常收到所有数据和PEC校验码
// 然后再开PEC, 若I2C总线释放后, I2C_SR2_PEC为0, 则表明程序流程正确

//#define TEST_CRCWRONG

const uint8_t sendbuf[] = {0xb1, 0x45, 0x98, 0xa3, 0x26, 0x77};
uint8_t recvbuf[100];
uint8_t size;

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while ((USART1->SR & USART_SR_TXE) == 0);
USART1->DR = '\r';
}
while ((USART1->SR & USART_SR_TXE) == 0);
USART1->DR = ch;
}
return ch;
}

int main(void)
{
RCC->APB1ENR = RCC_APB1ENR_I2C1EN;
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_USART1EN;

GPIOA->CRH = 0x888444b4;
GPIOB->CRL = 0xff484444;

USART1->BRR = 625; // 波特率: 115200
USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
printf("I2C Slave!\n");

I2C1->OAR1 = 0x06;
I2C1->CR1 = I2C_CR1_ENPEC | I2C_CR1_ACK | I2C_CR1_PE;
I2C1->CR2 = I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
NVIC_EnableIRQ(I2C1_EV_IRQn);
NVIC_EnableIRQ(I2C1_ER_IRQn);

while (1)
__WFI();
}

void I2C1_EV_IRQHandler(void)
{
if (I2C1->SR1 & I2C_SR1_ADDR)
{
I2C1->SR2;  // 读取SR2的目的是清除ADDR位
size = 0;
}

if (I2C1->SR2 & I2C_SR2_TRA)
{
// 发送
if (I2C1->SR1 & I2C_SR1_TXE)
{
if (size < sizeof(sendbuf))
{
I2C1->DR = sendbuf[size];
size++;
}
else if (size == sizeof(sendbuf))
{
#ifdef TEST_CRCWRONG
I2C1->DR = 0x00; // 发送一个错误的CRC码, 看接收端能不能检查到
// 注意: 由于Master-Receiver端必须在PEC校验码后返回一个NACK, 所以从机发送端无法知道主机端到底有没有正确收到数据
#else
I2C1->CR1 |= I2C_CR1_PEC; // 发送正确的CRC码 (TXE将不会清零)
#endif
size++;

// 开始发送倒数第二个字节时, TXE置位, DR送入最后一个字节, TXE被清零
// 开始发送最后一个字节时, TXE置位, PEC置1后TXE仍为1
// PEC发送完后, 收到NACK请求停止发送数据, AF将置位
I2C1->CR2 &= ~I2C_CR2_ITBUFEN; // 关闭TXE和RXNE中断, 防止卡死 (BTF中断不会被关闭)
}
else
{
// 正常情况下不应该进入此分支
if (I2C1->SR1 & I2C_SR1_BTF)
printf("error! BTF!\n");
else
printf("error 1!\n");
}
}
else
printf("error 2!\n");
}
else
{
// 接收
if ((I2C1->SR1 & (I2C_SR1_RXNE | I2C_SR1_STOPF)) == 0 && size != 0) // size=0时表示处理的是ADDR位
printf("error 3! I2C1->SR1=0x%04x\n", I2C1->SR1);

if (I2C1->SR1 & I2C_SR1_RXNE) // 注意: 接收PEC时, RXNE仍要置位!
{
recvbuf[size] = I2C1->DR;
size++;

if (size == 4)
I2C1->CR1 |= I2C_CR1_PEC; // 最后一个字节收到后, 下一个字节应为PEC (此时已经正在接收PEC了)
}

if (I2C1->SR1 & I2C_SR1_STOPF)
{
I2C1->CR1 = I2C1->CR1; // 清除STOPF
printf("Slave reception terminated! Data: ");
dump(recvbuf, size);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1);
}
}
}

void I2C1_ER_IRQHandler(void)
{
if ((I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_PECERR)) == 0)
printf("error 4!\n");

if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
while (I2C1->SR1 & I2C_SR1_TXE); // 等待TXE回到0, 防止进入错误的error3分支
I2C1->CR2 |= I2C_CR2_ITBUFEN; // 重开TXE和RXNE中断
printf("Slave transmission terminated!\n");
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1);
}

if (I2C1->SR1 & I2C_SR1_PECERR)
{
I2C1->SR1 &= ~I2C_SR1_PECERR;
printf("Reception PEC error!\n");
}
}
【从机端程序:DMA方式(寄存器版)】

#include <stdio.h>
#include <stm32f10x.h>

// 调试程序时, 先不使用PEC位, 检查程序是否能正常收到所有数据和PEC校验码
// 然后再开PEC, 若I2C总线释放后, I2C_SR2_PEC为0, 则表明程序流程正确

const uint8_t sendbuf[] = {0xb1, 0x45, 0x98, 0xa3, 0x26, 0x77};
uint8_t recvbuf[100];

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while ((USART1->SR & USART_SR_TXE) == 0);
USART1->DR = '\r';
}
while ((USART1->SR & USART_SR_TXE) == 0);
USART1->DR = ch;
}
return ch;
}

int main(void)
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APB1ENR = RCC_APB1ENR_I2C1EN;
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_USART1EN;

GPIOA->CRH = 0x888444b4;
GPIOB->CRL = 0xff484444;

USART1->BRR = 625; // 波特率: 115200
USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
printf("I2C Slave!\n");

I2C1->OAR1 = 0x06;
I2C1->CR1 = I2C_CR1_ENPEC | I2C_CR1_ACK | I2C_CR1_PE;
I2C1->CR2 = I2C_CR2_ITEVTEN | I2C_CR2_ITERREN; // 使用DMA时不可开ITBUFEN中断
NVIC_EnableIRQ(I2C1_EV_IRQn);
NVIC_EnableIRQ(I2C1_ER_IRQn);

DMA1_Channel6->CCR = DMA_CCR6_DIR | DMA_CCR6_MINC | DMA_CCR6_TCIE; // 要打开传输完成中断
DMA1_Channel6->CMAR = (uint32_t)sendbuf;
DMA1_Channel6->CPAR = (uint32_t)&I2C1->DR;
NVIC_EnableIRQ(DMA1_Channel6_IRQn);

DMA1_Channel7->CCR = DMA_CCR7_MINC | DMA_CCR7_TCIE; // 要打开传输完成中断
DMA1_Channel7->CMAR = (uint32_t)recvbuf;
DMA1_Channel7->CPAR = (uint32_t)&I2C1->DR;
NVIC_EnableIRQ(DMA1_Channel7_IRQn);

while (1)
__WFI();
}

void DMA1_Channel6_IRQHandler(void)
{
DMA1->IFCR = DMA_IFCR_CTCIF6; // 清除标志位
I2C1->CR2 &= ~I2C_CR2_DMAEN;
DMA1_Channel6->CCR &= ~DMA_CCR6_EN; // I2C传输结束后应关闭DMA
}

void DMA1_Channel7_IRQHandler(void)
{
// 这里不需要关ACK, 校验失败时自动发NACK
DMA1->IFCR = DMA_IFCR_CTCIF7;
I2C1->CR2 &= ~(I2C_CR2_LAST | I2C_CR2_DMAEN);
DMA1_Channel7->CCR &= ~DMA_CCR7_EN;
}

void I2C1_EV_IRQHandler(void)
{
if ((I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_STOPF)) == 0)
printf("error 5!\n");

if (I2C1->SR1 & I2C_SR1_ADDR)
{
// 开DMA
if (I2C1->SR2 & I2C_SR2_TRA) // 读取SR2可清除ADDR位
{
DMA1_Channel6->CNDTR = sizeof(sendbuf); // 不包括PEC
DMA1_Channel6->CCR |= DMA_CCR6_EN;
}
else
{
DMA1_Channel7->CNDTR = 5; // 包括PEC
DMA1_Channel7->CCR |= DMA_CCR7_EN;
I2C1->CR2 |= I2C_CR2_LAST; // 接收完最后一个数据不应答
}
I2C1->CR2 |= I2C_CR2_DMAEN;
}

if (I2C1->SR1 & I2C_SR1_STOPF)
{
I2C1->CR1 = I2C1->CR1; // 清除STOPF
printf("Slave reception terminated! Data: ");
dump(recvbuf, 5);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1, DMA1->ISR);
}
}

void I2C1_ER_IRQHandler(void)
{
if ((I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_PECERR)) == 0)
printf("error 6!\n");

if (I2C1->SR1 & I2C_SR1_AF)
{
I2C1->SR1 &= ~I2C_SR1_AF;
printf("Slave transmission terminated!\n");
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C1->SR1, I2C1->SR2, I2C1->CR1, DMA1->ISR);
}

if (I2C1->SR1 & I2C_SR1_PECERR)
{
I2C1->SR1 &= ~I2C_SR1_PECERR;
printf("Reception PEC error!\n");
}
}


下载程序后,在电脑上把两个单片机的串口输出都打开。在主机端的串口上发送以下字母可启动通信:

a:主机端普通方式,主发从收

b:主机端DMA方式,主发从收

c:主机端普通方式,主收从发

d:主机端DMA方式,主收从发

e:主机端普通方式,主发从收,但故意发送一个错误的CRC校验码

若在从机端程序中打开TEST_CRCWRONG的定义,则可在c的基础上使用错误的CRC校验码

以下为程序的运行结果,每幅图从a~e依次发送命令。

【从机普通方式】



【从机普通方式且定义TEST_CRCWRONG】



【从机DMA方式】



STM32并不是我们想象中的那样只计算数据部分的CRC校验码,然后与收到的校验码比较,判断是否相等,而是把收到的CRC校验码和数据一起计算,判断最终算出来的值(也就是余数)是不是等于0,如果等于0就表明传输正确。这样的话硬件实现起来要简单一些。发送和接收都是如此,正常情况下传输完毕后SR2的高8位一定为0。

【主机端程序(库函数版)】

#include <stdio.h>
#include <stm32f10x.h>

const uint8_t sendbuf[] = {0x40, 0x45, 0x52, 0x67};
uint8_t recvbuf[100];

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
USART_SendData(USART3, '\r');
}
while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
USART_SendData(USART3, ch);
}
return ch;
}

void test_write(void)
{
uint8_t i;
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_Send7bitAddress(I2C1, 0x06, I2C_Direction_Transmitter);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
{
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET) // 从机复位键按住不放, 可模拟没有应答的情况
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 找不到从机时主机必须发出停止信号, 使BUSY清零, 释放总线
printf("No ack!\n");
return;
}
}

for (i = 0; i < sizeof(sendbuf); i++)
{
I2C_SendData(I2C1, sendbuf[i]);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR);
}
I2C_TransmitPEC(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);

I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1));
}

void test_write_dma(void)
{
DMA_SetCurrDataCounter(DMA1_Channel6, sizeof(sendbuf)); // 不包括PEC
DMA_Cmd(DMA1_Channel6, ENABLE);
I2C_DMACmd(I2C1, ENABLE);

I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_Send7bitAddress(I2C1, 0x06, I2C_Direction_Transmitter);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
{
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel6, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("No ack!\n");
return;
}
}

while (DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET); // 等待DMA送入数据结束
DMA_ClearFlag(DMA1_FLAG_TC6); // 清除标志位
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel6, DISABLE); // I2C传输结束后应关闭DMA

while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR); // 等待传输真正结束
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1), DMA1->ISR);
}

void test_read(void)
{
uint8_t size;
I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_Send7bitAddress(I2C1, 0x06, I2C_Direction_Receiver);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR)
{
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("No ack!\n");
return;
}
}

for (size = 0; size < 7; size++) // 范围为0~6, 其中0~5为数据, 6为PEC校验码
{
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR);
recvbuf[size] = I2C_ReceiveData(I2C1);

if (size == 5)
{
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_TransmitPEC(I2C1, ENABLE); // 接收完最后一个字节后, 将PEC置位, 准备接收校验码 (此时已经正在接收PEC了)
I2C_GenerateSTOP(I2C1, ENABLE);
}
}
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);

printf("Slave reception terminated! Data: ");
dump(recvbuf, 7);
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_PECERR) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_PECERR);
printf("Reception PEC Error! ");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1));
}

void test_read_dma(void)
{
DMA_SetCurrDataCounter(DMA1_Channel7, 7); // 包括PEC
DMA_Cmd(DMA1_Channel7, ENABLE);
I2C_DMALastTransferCmd(I2C1, ENABLE);
I2C_DMACmd(I2C1, ENABLE);

I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_Send7bitAddress(I2C1, 0x06, I2C_Direction_Receiver);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR)
{
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_DMALastTransferCmd(I2C1, DISABLE);
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("No ack!\n");
return;
}
}

while (DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET); // 等待DMA读取数据结束, 读取结束并不意味着数据传输结束
DMA_ClearFlag(DMA1_FLAG_TC7);
I2C_AcknowledgeConfig(I2C1, DISABLE); // 必须在接收PEC时关闭ACK!
I2C_DMALastTransferCmd(I2C1, DISABLE);
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);

I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待数据传输真正结束

printf("Slave reception terminated! Data: ");
dump(recvbuf, 7);
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_PECERR) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_PECERR);
printf("Reception PEC Error! ");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1), DMA1->ISR);
}

void test_write_wrongcrc(void)
{
uint8_t i;
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_Send7bitAddress(I2C1, 0x06, I2C_Direction_Transmitter);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
{
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET) // 从机复位键按住不放, 可模拟没有应答的情况
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
printf("No ack!\n");
return;
}
}

for (i = 0; i < sizeof(sendbuf); i++)
{
I2C_SendData(I2C1, sendbuf[i]);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR);
}

I2C_SendData(I2C1, 0x00); // 发送一个错误的CRC校验码
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR && I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == RESET); // 等待AF或BTF置1
I2C_GenerateSTOP(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);

// 接收端发送非应答, 发送端知道接收端没有收到正确的数据
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
printf("Not correctly received!\n");
}
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1));
// 发送端和接收端算出的I2C_SR2_PEC都是0x87(但发送端实际发送的校验码是0x00), 即数据和地址码的CRC校验码, PEC不为0表示出了错误
}

int main(void)
{
DMA_InitTypeDef dma;
GPIO_InitTypeDef gpio;
I2C_InitTypeDef i2c;
USART_InitTypeDef usart;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_USART3 | RCC_APB1Periph_UART5, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE);

GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio);

gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &gpio);

GPIO_WriteBit(GPIOE, GPIO_Pin_15, Bit_SET);
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOE, &gpio);

USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_Mode = USART_Mode_Tx;
USART_Init(USART3, &usart);
USART_Cmd(USART3, ENABLE);

usart.USART_Mode = USART_Mode_Rx;
USART_Init(UART5, &usart);
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
USART_Cmd(UART5, ENABLE);
NVIC_EnableIRQ(UART5_IRQn);

I2C_StructInit(&i2c);
i2c.I2C_ClockSpeed = 400000;
I2C_Init(I2C1, &i2c);
I2C_CalculatePEC(I2C1, ENABLE);
I2C_Cmd(I2C1, ENABLE);

DMA_StructInit(&dma);
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_MemoryBaseAddr = (uint32_t)sendbuf;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
DMA_Init(DMA1_Channel6, &dma);

dma.DMA_DIR = DMA_DIR_PeripheralSRC;
dma.DMA_MemoryBaseAddr = (uint32_t)recvbuf;
DMA_Init(DMA1_Channel7, &dma);

printf("I2C Master!\n");
while (1)
__WFI();
}

void UART5_IRQHandler(void)
{
uint8_t data = USART_ReceiveData(UART5);
GPIO_WriteBit(GPIOE, GPIO_Pin_15, Bit_RESET); // 灯亮
if (data == 'a')
test_write();
else if (data == 'b')
test_write_dma();
else if (data == 'c')
test_read();
else if (data == 'd')
test_read_dma();
else if (data == 'e')
test_write_wrongcrc();
GPIO_WriteBit(GPIOE, GPIO_Pin_15, Bit_SET); // 灯灭
}


【从机端程序:普通方式(库函数版)】

#include <stdio.h>
#include <stm32f10x.h>

// 调试程序时, 先不使用PEC位, 检查程序是否能正常收到所有数据和PEC校验码
// 然后再开PEC, 若I2C总线释放后, I2C_SR2_PEC为0, 则表明程序流程正确

//#define TEST_CRCWRONG

const uint8_t sendbuf[] = {0xb1, 0x45, 0x98, 0xa3, 0x26, 0x77};
uint8_t recvbuf[100];
uint8_t size;

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, '\r');
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
return ch;
}

int main(void)
{
GPIO_InitTypeDef gpio;
I2C_InitTypeDef i2c;
USART_InitTypeDef usart;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_USART1, ENABLE);

gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);

gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOB, &gpio);

USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
printf("I2C Slave!\n");

I2C_StructInit(&i2c);
i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_ClockSpeed = 400000;
i2c.I2C_OwnAddress1 = 0x06;
I2C_Init(I2C1, &i2c);
I2C_CalculatePEC(I2C1, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_BUF | I2C_IT_EVT | I2C_IT_ERR, ENABLE);
I2C_Cmd(I2C1, ENABLE);
NVIC_EnableIRQ(I2C1_EV_IRQn);
NVIC_EnableIRQ(I2C1_ER_IRQn);

while (1)
__WFI();
}

void I2C1_EV_IRQHandler(void)
{
// ADDR位检测, 执行后会清除ADDR位, 因此只能检测一次
if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED) == SUCCESS) // 无论是发送模式还是接收模式都能检测到
size = 0;

if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTING) == SUCCESS)
{
// 发送
if (size < sizeof(sendbuf))
I2C_SendData(I2C1, sendbuf[size++]);
else if (size == sizeof(sendbuf))
{
#ifdef TEST_CRCWRONG
I2C_SendData(I2C1, 0x00); // 发送一个错误的CRC码, 看接收端能不能检查到
// 注意: 由于Master-Receiver端必须在PEC校验码后返回一个NACK, 所以从机发送端无法知道主机端到底有没有正确收到数据
#else
I2C_TransmitPEC(I2C1, ENABLE); // 发送正确的CRC码 (TXE将不会清零)
#endif
size++;

// 开始发送倒数第二个字节时, TXE置位, DR送入最后一个字节, TXE被清零
// 开始发送最后一个字节时, TXE置位, PEC置1后TXE仍为1
// PEC发送完后, 收到NACK请求停止发送数据, AF将置位
I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE); // 关闭TXE和RXNE中断, 防止卡死 (BTF中断不会被关闭)
}
}
else
{
// 接收
if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_RECEIVED) == SUCCESS) // 注意: 接收PEC时, RXNE仍要置位!
{
recvbuf[size++] = I2C_ReceiveData(I2C1);
if (size == 4)
I2C_TransmitPEC(I2C1, ENABLE); // 最后一个字节收到后, 下一个字节应为PEC (此时已经正在接收PEC了)
}

if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_STOP_DETECTED) == SUCCESS)
{
I2C_Cmd(I2C1, ENABLE); // 清除STOPF
printf("Slave reception terminated! Data: ");
dump(recvbuf, size);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1));
}
}

printf("*");
}

void I2C1_ER_IRQHandler(void)
{
if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_ACK_FAILURE) == SUCCESS)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == SET); // 等待TXE回到0
I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE); // 重开TXE和RXNE中断
printf("Slave transmission terminated!\n");
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1));
}

if (I2C_GetFlagStatus(I2C1, I2C_FLAG_PECERR) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_PECERR);
printf("Reception PEC error!\n");
}

printf("*");
}

【从机端程序:DMA方式(库函数版)】

#include <stdio.h>
#include <stm32f10x.h>

// 调试程序时, 先不使用PEC位, 检查程序是否能正常收到所有数据和PEC校验码
// 然后再开PEC, 若I2C总线释放后, I2C_SR2_PEC为0, 则表明程序流程正确

const uint8_t sendbuf[] = {0xb1, 0x45, 0x98, 0xa3, 0x26, 0x77};
uint8_t recvbuf[100];

void dump(const uint8_t *data, uint16_t size)
{
while (size--)
printf("%02x", *data++);
printf("\n");
}

int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, '\r');
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
return ch;
}

int main(void)
{
DMA_InitTypeDef dma;
GPIO_InitTypeDef gpio;
I2C_InitTypeDef i2c;
USART_InitTypeDef usart;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_USART1, ENABLE);

gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);

gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOB, &gpio);

USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
printf("I2C Slave!\n");

I2C_StructInit(&i2c);
i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_ClockSpeed = 400000;
i2c.I2C_OwnAddress1 = 0x06;
I2C_Init(I2C1, &i2c);
I2C_CalculatePEC(I2C1, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // 使用DMA时不可开ITBUFEN中断
I2C_Cmd(I2C1, ENABLE);
NVIC_EnableIRQ(I2C1_EV_IRQn);
NVIC_EnableIRQ(I2C1_ER_IRQn);

DMA_StructInit(&dma);
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_MemoryBaseAddr = (uint32_t)sendbuf;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
DMA_Init(DMA1_Channel6, &dma);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE); // 要打开传输完成中断
NVIC_EnableIRQ(DMA1_Channel6_IRQn);

dma.DMA_DIR = DMA_DIR_PeripheralSRC;
dma.DMA_MemoryBaseAddr = (uint32_t)recvbuf;
DMA_Init(DMA1_Channel7, &dma);
DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, ENABLE); // 要打开传输完成中断
NVIC_EnableIRQ(DMA1_Channel7_IRQn);

while (1)
__WFI();
}

void DMA1_Channel6_IRQHandler(void)
{
DMA_ClearITPendingBit(DMA1_IT_TC6); // 清除标志位
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel6, DISABLE); // I2C传输结束后应关闭DMA
}

void DMA1_Channel7_IRQHandler(void)
{
// 这里不需要关ACK, 校验失败时自动发NACK
DMA_ClearITPendingBit(DMA1_IT_TC7);
I2C_DMALastTransferCmd(I2C1, DISABLE);
I2C_DMACmd(I2C1, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);
}

void I2C1_EV_IRQHandler(void)
{
// 执行I2C_CheckEvent函数会将ADDR位清除, 所以必须单独检测
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == SET)
{
// 开DMA
if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED) == SUCCESS)
{
// 发送模式
DMA_SetCurrDataCounter(DMA1_Channel6, sizeof(sendbuf)); // 不包括PEC
DMA_Cmd(DMA1_Channel6, ENABLE);
}
else // 此时ADDR位已被清除, 因此不可以检测I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED事件
{
// 接收模式
DMA_SetCurrDataCounter(DMA1_Channel7, 5); // 包括PEC
DMA_Cmd(DMA1_Channel7, ENABLE);
I2C_DMALastTransferCmd(I2C1, ENABLE); // 接收完最后一个数据不应答
}
I2C_DMACmd(I2C1, ENABLE);
}

if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_STOP_DETECTED) == SUCCESS)
{
I2C_Cmd(I2C1, ENABLE); // 清除STOPF
printf("Slave reception terminated! Data: ");
dump(recvbuf, 5);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1), DMA1->ISR);
}

printf("*");
}

void I2C1_ER_IRQHandler(void)
{
if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_ACK_FAILURE) == SUCCESS)
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
printf("Slave transmission terminated!\n");
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x, I2C1->CR1=0x%04x, DMA1->ISR=0x%08x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2), I2C_ReadRegister(I2C1, I2C_Register_CR1), DMA1->ISR);
}

if (I2C_GetFlagStatus(I2C1, I2C_FLAG_PECERR) == SET)
{
I2C_ClearFlag(I2C1, I2C_FLAG_PECERR);
printf("Reception PEC error!\n");
}

printf("*");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单片机 stm32 c语言 I2C CRC