电路连接:SCL和SDA分别接到PB6和PB7上,并都外接上10kΩ上拉电阻。
电路板如下图所示:
最左边的4个排针接的是电源和串口。
由于板上没有任何外部晶振,所以在Keil中建好工程后, 要将RTE/Device/STM32F103C8/system_stm32f10x.c中的SYSCLK_FREQ_72MHz的定义注释掉,防止SystemInit函数打开HSE晶振。
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
/* #define SYSCLK_FREQ_72MHz 72000000 */
#endif
板子有两种程序下载方式。一种是通过J-Link仿真器,在Keil中下载。另一种是按下板上的白色开关,将BOOT0拉高(BOOT1=PB2必须通过10kΩ的电阻接地),然后用STMicroelectronics的FlashLoader通过串口1下载编译好的hex文件。
板上LED灯串联的电阻是500Ω左右(由两个1kΩ的电阻并联而成),接到了PA8上,高电平点亮,本实验中没有用到。
复位电路所用的电容是0.1μF 50V的电解电容(104),是51/AVR的单片机所用的10μF的1%。
【程序1:普通方式】
#include
#include
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;
}
// 发送开始信号和从机地址(传输模式)
void start(void)
{
if (I2C1->SR1 || I2C1->SR2)
printf("error!\n");
restart:
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 等待开始信号发送完毕
I2C_SendData(I2C1, 0xa0); // 从机地址(传输模式)
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);
//printf("NACK!\n");
goto restart;
}
}
}
uint8_t read(uint8_t addr)
{
start();
I2C_SendData(I2C1, addr); // 发送存储器地址
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
I2C_GenerateSTART(I2C1, ENABLE); // RESTART
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_SendData(I2C1, 0xa1); // 从机地址(接收模式)
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);
I2C_GenerateSTOP(I2C1,ENABLE); // 接收最后一字节数据前先准备好STOP信号
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR); // 等待数据接收完毕
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待停止信号发送完毕
return I2C_ReceiveData(I2C1);
}
void write(uint8_t addr, uint8_t value)
{
start();
I2C_SendData(I2C1, addr); // 前两个数据可连发, 无需等待TXE置位
I2C_SendData(I2C1, value);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR); // 等待数据完全发送完毕
I2C_GenerateSTOP(I2C1,ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待停止信号发送完毕
}
void read_more(uint8_t addr, char *data, uint8_t len)
{
start();
I2C_SendData(I2C1, addr);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_SendData(I2C1, 0xa1);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);
I2C_AcknowledgeConfig(I2C1, ENABLE);
while (len--)
{
if (len == 0)
{
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_GenerateSTOP(I2C1,ENABLE); // 接收最后一字节数据前先准备好STOP信号
}
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR); // 等待当前数据接收完毕
*data++ = I2C_ReceiveData(I2C1);
}
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待停止信号发送完毕
}
void write_more(uint8_t addr, const char *data, uint8_t len)
{
start();
I2C_SendData(I2C1, addr);
while (len--)
{
I2C_SendData(I2C1, *data++);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR); // 等待TXE
}
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR); // 等待全部数据发送完毕
I2C_GenerateSTOP(I2C1,ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); // 等待停止信号发送完毕
}
int main(void)
{
char str[20];
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);
// I2C引脚设为复用开漏输出
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);
I2C_StructInit(&i2c);
i2c.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &i2c);
I2C_Cmd(I2C1, ENABLE);
write(17, 0x31);
printf("read: 0x%02x\n", read(17));
write_more(17, "Hello!", 7); // 区域16~23
read_more(17, str, sizeof(str));
printf("str: %s\n", str);
write_more(8, "12345678ABCDEFG", 16); // 先将前8个字符写入地址8~15处, 然后回到地址8处写入剩下的字符, 包括最后的\0
read_more(8, str, sizeof(str));
printf("str: %s\n", str);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2));
while (1)
__WFI();
}
【程序运行结果1】
read: 0x31
str: Hello!
str: ABCDEFG
I2C1->SR1=0x0000, I2C1->SR2=0x0000
两个SR寄存器的值都为0表明I2C正常工作。
【程序2:DMA方式】
#include
#include
DMA_InitTypeDef dma;
#define WRITE
#ifdef WRITE
const char eep_data[] = "The SDIO does not have an SPI-compatible communication mode. The SD memory card protocol is a superset of the MultiMediaCard protocol as defined in the MultiMediaCard system specification V2.11. Several commands required for SD memory devices are not supported by either SD I/O-only cards or the I/O portion of combo cards.";
#endif
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;
}
// 发送开始信号和从机地址(传输模式)
void start(void)
{
if (I2C1->SR1 || I2C1->SR2)
printf("error!\n");
restart:
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 等待开始信号发送完毕
I2C_SendData(I2C1, 0xa0); // 从机地址(传输模式)
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);
//printf("NACK!\n");
goto restart;
}
}
}
void read(uint8_t addr, char *data, uint16_t len)
{
dma.DMA_BufferSize = len;
dma.DMA_DIR = DMA_DIR_PeripheralSRC;
dma.DMA_MemoryBaseAddr = (uint32_t)data;
DMA_Init(DMA1_Channel7, &dma);
DMA_Cmd(DMA1_Channel7, ENABLE);
start();
I2C_SendData(I2C1, addr);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
I2C_GenerateSTART(I2C1, ENABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
I2C_SendData(I2C1, 0xa1);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);
I2C_DMACmd(I2C1, ENABLE);
I2C_DMALastTransferCmd(I2C1, ENABLE);
I2C_AcknowledgeConfig(I2C1, ENABLE);
while (DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC7);
DMA_Cmd(DMA1_Channel7, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE);
I2C_DMACmd(I2C1, DISABLE);
I2C_DMALastTransferCmd(I2C1, DISABLE);
I2C_AcknowledgeConfig(I2C1, DISABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
}
void write(uint8_t addr, const char *data, uint8_t len)
{
dma.DMA_BufferSize = len;
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_MemoryBaseAddr = (uint32_t)data;
DMA_Init(DMA1_Channel6, &dma);
DMA_Cmd(DMA1_Channel6, ENABLE);
start();
I2C_SendData(I2C1, addr);
I2C_DMACmd(I2C1, ENABLE);
while (DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC6);
DMA_Cmd(DMA1_Channel6, DISABLE);
while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR); // 等待全部数据发送完毕
I2C_GenerateSTOP(I2C1, ENABLE);
I2C_DMACmd(I2C1, DISABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
}
#ifdef WRITE
void write_all(void)
{
uint16_t i;
for (i = 0; i < 256; i += 8)
write(i, eep_data + i, 8);
}
#endif
int main(void)
{
char str[257];
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);
USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
// I2C引脚复位
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOB, &gpio);
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == Bit_RESET || GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) == Bit_RESET)
{
printf("I2C reset!\n");
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &gpio);
GPIO_WriteBit(GPIOB, GPIO_Pin_6, Bit_SET);
GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_SET);
}
// I2C引脚设为复用开漏输出
gpio.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &gpio);
I2C_StructInit(&i2c);
i2c.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &i2c);
I2C_Cmd(I2C1, ENABLE);
dma.DMA_M2M = DMA_M2M_Disable;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_Mode = DMA_Mode_Normal;
dma.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_Priority = DMA_Priority_High;
#ifdef WRITE
write_all();
#endif
read(0, str, 256);
str[256] = '\0';
printf("%s\n", str);
printf("I2C1->SR1=0x%04x, I2C1->SR2=0x%04x\n", I2C_ReadRegister(I2C1, I2C_Register_SR1), I2C_ReadRegister(I2C1, I2C_Register_SR2));
while (1)
__WFI();
}
【程序运行结果2】
The SDIO does not have an SPI-compatible communication mode. The SD memory card protocol is a superset of the MultiMediaCard protocol as defined in the MultiMediaCard system specification V2.11. Several commands required for SD memory devices are not suppo
I2C1->SR1=0x0000, I2C1->SR2=0x0000