【程序】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)
【主机端程序(寄存器版)】
下载程序后,在电脑上把两个单片机的串口输出都打开。在主机端的串口上发送以下字母可启动通信:
a:主机端普通方式,主发从收
b:主机端DMA方式,主发从收
c:主机端普通方式,主收从发
d:主机端DMA方式,主收从发
e:主机端普通方式,主发从收,但故意发送一个错误的CRC校验码
若在从机端程序中打开TEST_CRCWRONG的定义,则可在c的基础上使用错误的CRC校验码
以下为程序的运行结果,每幅图从a~e依次发送命令。
【从机普通方式】
![](http://img.blog.csdn.net/20170724201809565?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvWkxLMTIxNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
【从机普通方式且定义TEST_CRCWRONG】
![](http://img.blog.csdn.net/20170724201840803?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvWkxLMTIxNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
【从机DMA方式】
![](http://img.blog.csdn.net/20170724201921802?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvWkxLMTIxNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
STM32并不是我们想象中的那样只计算数据部分的CRC校验码,然后与收到的校验码比较,判断是否相等,而是把收到的CRC校验码和数据一起计算,判断最终算出来的值(也就是余数)是不是等于0,如果等于0就表明传输正确。这样的话硬件实现起来要简单一些。发送和接收都是如此,正常情况下传输完毕后SR2的高8位一定为0。
【主机端程序(库函数版)】
【从机端程序:普通方式(库函数版)】
【从机端程序:DMA方式(库函数版)】
如果经过硬件检验后,接收到的内容完全正确,则双方的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("*"); }
相关文章推荐
- ATS程序功能和使用方法详解
- 使用CMS方法开发功能强大的Web程序
- ATS程序功能和使用方法详解
- C#程序中使用DataView的ToTable方法实现distinct功能
- 关于android程序中使用bitmap放大功能时的OOM问题解决方法
- STM32使用DMA功能导致程序运行不正常
- 使用native方法扩展Java程序的功能详解
- SpringMVC(26):简单的json数据校验示例(实现功能:使用jQuery框架的ajax()方法来实现 userCode的校验)
- 使用native方法扩展Java程序的功能
- 使用addFieldError方法和s:fieldError标签简单处理数据校验
- STC单片机使用RS485下载程序方法
- ATS程序功能和使用方法详解
- 【程序】STM32F103单片机使用定时器DMA进行全自动8位数码管动态扫描
- 心跳防护程序的功能及使用方法
- SQL*Plus 使用技巧--编辑功能使用方法
- Atlas M3: ErrorTemplate正确使用方法
- 一个简单的方法:找出哪个的程序使用了哪个端口
- 专门针对功能强大的内核级后门设计的手杀工具 --icesword使用方法
- C# 2.0:使用匿名方法、迭代程序和局部类来创建优雅的代码
- Blog的TrackBack功能的使用方法