找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2621|回复: 1
打印 上一主题 下一主题
收起左侧

stm32f407芯片模拟IIC

[复制链接]
跳转到指定楼层
楼主
ID:954547 发表于 2021-7-29 17:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"
#include <string.h>

static GPIO_InitTypeDef                GPIO_InitStruct;
static NVIC_InitTypeDef                NVIC_InitStructure;
static USART_InitTypeDef         USART_InitStructure;

void delay_us(uint32_t nus)
{

        SysTick->CTRL = 0;                                                        // 关闭系统定时器
        SysTick->LOAD = nus*(SystemCoreClock/1000000);         // nus *1us延时,这个计数值
        SysTick->VAL = 0;                                                         // 清空标志位
        SysTick->CTRL = 5;                                                         // 使能系统定时器工作,时钟源为系统时钟168MHz
        while ((SysTick->CTRL & 0x00010000)==0);        // 检查CTRL寄存器的16bit是否置1,若置1,就代表计数完毕
        SysTick->CTRL = 0;                                                         // 关闭系统定时器       
}

void delay_ms(uint32_t nms)
{

        while(nms--)
        {
       
                SysTick->CTRL = 0;                                                        // 关闭系统定时器
                SysTick->LOAD = (SystemCoreClock/1000);         // 1ms延时,这个计数值
                SysTick->VAL = 0;                                                         // 清空标志位
                SysTick->CTRL = 5;                                                         // 使能系统定时器工作,时钟源为系统时钟168MHz
                while ((SysTick->CTRL & 0x00010000)==0);        // 检查CTRL寄存器的16bit是否置1,若置1,就代表计数完毕
                SysTick->CTRL = 0;                                                         // 关闭系统定时器       
       
        }
}

int fputc(int ch,FILE *file)
{
        USART_SendData(USART1,ch);
        //等待发送完毕
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);

        return ch;
}

void usart1_init(uint32_t baud)
{

        //端口A硬件时钟使能
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);       
       
        //串口1硬件时钟使能
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
       


        //配置硬件,配置GPIO,端口A,第9 10引脚
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;//第9 10引脚
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;                        //复用功能模式
        GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;                //引脚高速工作,收到指令立即工作;缺点:功耗高
        GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;                //增加输出电流的能力
        GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;                //不需要上下拉电阻
        GPIO_Init(GPIOA,&GPIO_InitStruct);


        //将PA9引脚连接到串口1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);

        //将PA10引脚连接到串口1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);



        USART_InitStructure.USART_BaudRate = baud;                                        //波特率
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;        //8位数据位
        USART_InitStructure.USART_StopBits = USART_StopBits_1;                //1个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;                        //无校验位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不需要流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //允许串口接收和发送数据
        USART_Init(USART1, &USART_InitStructure);

        //接收数据后触发中断
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

        /* 配置串口1 */
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);

        USART_Cmd(USART1, ENABLE);
}

#define SCL_W        PBout(8)
#define SDA_W        PBout(9)
#define SDA_R        PBin(9)

void at24c02_init(void)
{

        //使能端口B的硬件时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);       


        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9;                        //第 8 9 个引脚
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;                //输出模式
        GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;                //引脚高速工作,收到指令立即工作;缺点:功耗高
        GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;                //增加输出电流的能力
        GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;                //不需要上下拉电阻
        GPIO_Init(GPIOB,&GPIO_InitStruct);
       
       
        //只要是输出模式,肯定会有初始电平状态,看时序图,空闲状态为高电平
        SCL_W=1;
        SDA_W=1;

}

void sda_pin_mode(GPIOMode_TypeDef pin_mode)
{
        //配置硬件,配置GPIO,端口F,第9个引脚
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;                        //第9 个引脚
        GPIO_InitStruct.GPIO_Mode=pin_mode;                //输出模式
        GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;                //引脚高速工作,收到指令立即工作;缺点:功耗高
        GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;                //增加输出电流的能力
        GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;                //不需要上下拉电阻
        GPIO_Init(GPIOB,&GPIO_InitStruct);


}


void i2c_start(void)
{       
        //保证SDA引脚为输出模式
        sda_pin_mode(GPIO_Mode_OUT);
       
        SDA_W=1;
        SCL_W=1;
        delay_us(5);
       
        SDA_W=0;
        delay_us(5);
       
        SCL_W=0;//总线进入忙状态
        delay_us(5);
}


void i2c_stop(void)
{
        //保证SDA引脚为输出模式
        sda_pin_mode(GPIO_Mode_OUT);
       
        SDA_W=0;
        SCL_W=1;
        delay_us(5);
       
        SDA_W=1;
        delay_us(5);

}


void i2c_send_byte(uint8_t byte)
{
        int32_t i=0;
       
        //保证SDA引脚为输出模式
        sda_pin_mode(GPIO_Mode_OUT);

        SDA_W=0;
        SCL_W=0;
        delay_us(5);
       
        //最高有效位优先传输,通过时序图观察到
        for(i=7; i>=0; i--)
        {
                //检测对应的bit位是1还是0
                if(byte & (1<<i))
                        SDA_W=1;
                else
                        SDA_W=0;
       
                delay_us(5);
       
                //时钟线拉高,数据有效
                SCL_W=1;
                delay_us(5);

       
                //时钟线拉低,数据变更
                SCL_W=0;
                delay_us(5);               
       
        }
}


uint8_t i2c_recv_byte(void)
{
        uint8_t d=0;
        int32_t i;
       
        //保证SDA引脚为输入模式
        sda_pin_mode(GPIO_Mode_IN);

        for(i=7; i>=0; i--)
        {
                //时钟线拉高,数据有效
                SCL_W=1;
                delay_us(5);
               
                //读取SDA引脚电平
                if(SDA_R)
                        d|=1<<i;


                //时钟线拉低,保持占用总线,总线是忙状态
                SCL_W=0;
                delay_us(5);       
       
        }

        return d;
}


void i2c_ack(uint8_t ack)
{

        //保证SDA引脚为输出模式
        sda_pin_mode(GPIO_Mode_OUT);

        SDA_W=0;
        SCL_W=0;
        delay_us(5);
       

        if(ack)
                SDA_W=1;
        else
                SDA_W=0;

        delay_us(5);

        //时钟线拉高,数据有效
        SCL_W=1;
        delay_us(5);


        //时钟线拉低,数据变更
        SCL_W=0;
        delay_us(5);               
}

uint8_t i2c_wait_ack(void)
{
        uint8_t ack=0;
        //保证SDA引脚为输入模式
        sda_pin_mode(GPIO_Mode_IN);

        //时钟线拉高,数据有效
        SCL_W=1;
        delay_us(5);
       
        //读取SDA引脚电平
        if(SDA_R)
                ack=1;//无应答
        else
                ack=0;//有应答

        //时钟线拉低,保持占用总线,总线是忙状态
        SCL_W=0;
        delay_us(5);


        return ack;
}


int32_t at24c02_write(uint8_t word_address,uint8_t *data,uint8_t len)
{
        uint8_t ack=0;
       
        uint8_t *p=data;
       
        //发送起始信号
        i2c_start();
       
        //发送设备寻址,从机地址写访问地址0xA0
        i2c_send_byte(0xA0);
       
        //等待对方应答
        ack=i2c_wait_ack();
        if(ack)
        {
                printf("device address error\r\n");
               
                return -1;
        }


        //发送访问数据存储地址
        i2c_send_byte(word_address);
       
        //等待对方应答
        ack=i2c_wait_ack();
        if(ack)
        {
                printf("word address error\r\n");
               
                return -2;
        }
       
        //连续写入数据
        while(len--)
        {
                //发送要写入的数据
                i2c_send_byte(*p++);
               
                //等待对方应答
                ack=i2c_wait_ack();
                if(ack)
                {
                        printf("write data error\r\n");
                       
                        return -3;
                }       
       
        }
       
        //结束通信
        i2c_stop();
       
        printf("write success\r\n");
       
        return 0;

}

int32_t at24c02_read(uint8_t word_address,uint8_t *data,uint8_t len)
{
        uint8_t ack=0;
       
        uint8_t *p=data;
       
        //发送起始信号
        i2c_start();
       
        //发送设备寻址,从机地址写访问地址0xA0
        i2c_send_byte(0xA0);
       
        //等待对方应答
        ack=i2c_wait_ack();
        if(ack)
        {
                printf("device address with write error\r\n");
               
                return -1;
        }


        //发送访问数据存储地址
        i2c_send_byte(word_address);
       
        //等待对方应答
        ack=i2c_wait_ack();
        if(ack)
        {
                printf("word address error\r\n");
               
                return -2;
        }
       
       
        //重新发送起始信号
        i2c_start();
       
        //发送设备寻址,从机地址读访问地址0xA1
        i2c_send_byte(0xA1);
       
        //等待对方应答
        ack=i2c_wait_ack();
        if(ack)
        {
                printf("device address with read error\r\n");
               
                return -3;
        }       
       
        len=len-1;
       
        //连续写入数据
        while(len--)
        {
                //读取数据
                *p++=i2c_recv_byte();
               
                //主动发应答信号
                i2c_ack(0);
       
       
        }
       
        //读取最后一个数据
        *p=i2c_recv_byte();       
       
        //主动发无应答信号
        i2c_ack(1);       
       
        //结束通信
        i2c_stop();
       
        printf("read success\r\n");
       
        return 0;

}


//主函数
int main(void)
{
        uint8_t buf[8]={1,2,3,4,5,6,7,8};
        uint32_t i;
       
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
       
        //使能端口F的硬件时钟,端口F才能工作,说白了就是对端口F上电
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);       

        //配置硬件,配置GPIO,端口F,第9个引脚
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;                        //第9 个引脚
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;                //输出模式
        GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;                //引脚高速工作,收到指令立即工作;缺点:功耗高
        GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;                //增加输出电流的能力
        GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;                //不需要上下拉电阻
        GPIO_Init(GPIOF,&GPIO_InitStruct);

        //配置串口1波特率为115200bps
        usart1_init(115200);
       
        at24c02_init();
       
        printf("This is at24c02 test\r\n");
       
        printf("at24c02:write addr at 0,data is 1 ~ 8\r\n");
        at24c02_write(0,buf,8);

        delay_ms(10);
        memset(buf,0,sizeof buf);
       
        at24c02_read(0,buf,8);       
        printf("read data is:");
        for(i=0; i<8; i++)
                printf("%02X ",buf[i]);
               
        printf("\r\n");
       
        while(1)
        {

               
        }
}


void USART1_IRQHandler(void)
{
        uint8_t d;
       
        //检查串口1是否接收到数据
        if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
        {
                //读取数据
                d = USART_ReceiveData(USART1);
               
                //清空标志位
                USART_ClearITPendingBit(USART1,USART_IT_RXNE);       
       
        }
}



评分

参与人数 1黑币 +20 收起 理由
admin + 20 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:632424 发表于 2021-7-29 22:27 | 只看该作者
优秀,感谢分享,刚学,我模仿学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表