在之前的文章中分析过在52上的IIC时序,也测试过stm32的自带IIC功能,这里大致写下如何模拟stm32上的IIC。实验硬件基于stm32f103c8t6
废话不多说,先直接上代码。
头文件
-
- #include "stdint.h"
- #include "gpio.h"
-
- #ifndef __IIC_H__
- #define __IIC_H__
-
-
- /*********************引脚和主频根据情况更换*****************************/
- #define CPU_FREQUENCY_MHZ 72 // STM32时钟主频
-
- #define IIC_GPIO_PORT GPIOB // GPIO端口
- #define IIC_SCL_PIN GPIO_PIN_9 // 连接到SCL时钟线的GPIO引脚
- #define IIC_SDA_PIN GPIO_PIN_8 // 连接到SDA数据线的GPIO引脚
-
- #define IIC_SCL_1() IIC_GPIO_PORT->BSRR = IIC_SCL_PIN // SCL = 1
- #define IIC_SCL_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U // SCL = 0
-
- #define IIC_SDA_1() IIC_GPIO_PORT->BSRR = IIC_SDA_PIN // SDA = 1
- #define IIC_SDA_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U // SDA = 0
-
- #define IIC_SDA_READ() (IIC_GPIO_PORT->IDR & IIC_SDA_PIN) // 读SDA引脚
- #define IIC_SCL_READ() (IIC_GPIO_PORT->IDR & IIC_SCL_PIN) // 读SCL引脚
-
-
- void iic_start(void); //开始信号
-
- void iic_stop(void); //停止信号
-
- void iic_send_byte(uint8_t _ucByte); //发送一个字节
-
- void iic_ack(void); //应答信号
-
- uint8_t iic_read_byte(void); //读取一个字节
-
- uint8_t iic_wait_ack(void); //等待应答信号
-
-
- #endif
.m文件
- #include "iic.h"
-
- void delay_us(__IO uint32_t delay) {
- int last, curr, val;
- int temp;
-
- while (delay != 0) {
- temp = delay > 900 ? 900 : delay;
- last = SysTick->VAL;
- curr = last - CPU_FREQUENCY_MHZ * temp;
- if (curr >= 0){
- do{
- val = SysTick->VAL;
- }
- while ((val < last) && (val >= curr));
- } else{
- curr += CPU_FREQUENCY_MHZ * 1000;
- do{
- val = SysTick->VAL;
- }
- while ((val <= last) || (val > curr));
- }
- delay -= temp;
- }
- }
-
- // IIC 至少0.6us,1us即可
- void iic_delay_1us(void) {
- delay_us(1);
- }
-
- // SCL保持高电平期间,SDA由高电平变成低电平,产生一个下降沿。
- void iic_start(void) {
- IIC_SDA_1();
- IIC_SCL_1();
- iic_delay_1us();
- IIC_SDA_0();
- iic_delay_1us();
- }
-
- // SCL保持高电平期间,SDA由低电平变成高电平,产生一个上升沿。
- void iic_stop(void){
- IIC_SDA_0();
- IIC_SCL_1();
- iic_delay_1us();
- IIC_SDA_1();
- iic_delay_1us();
- }
-
- void iic_send_byte(uint8_t _ucByte){
- uint8_t i;
- for (i = 0; i < 8; i++) {
- IIC_SCL_0();
- if (_ucByte & 0x80){
- IIC_SDA_1();
- } else{
- IIC_SDA_0();
- }
- iic_delay_1us();
- IIC_SCL_1();
- iic_delay_1us();
- IIC_SCL_0();
- // 发送最后一位
- if (i == 7) {
- IIC_SDA_1(); // 释放总线
- }
- _ucByte <<= 1; // 左移一个bit
- iic_delay_1us();
- }
- }
-
- uint8_t iic_read_byte(void){
- uint8_t i;
- uint8_t value = 0;
- for (i = 0; i < 8; i++) {
- value <<= 1;
- IIC_SCL_1();
- iic_delay_1us();
- if (IIC_SDA_READ()) {
- value++;
- }
- IIC_SCL_0();
- iic_delay_1us();
- }
- return value;
- }
-
- uint8_t iic_wait_ack(void){
- uint8_t result;
-
- IIC_SCL_1();
- iic_delay_1us();
- result = IIC_SDA_READ() ? 1 :0;
- IIC_SCL_0();
- iic_delay_1us();
- return result;
- }
-
- void iic_ack(void){
- IIC_SDA_0();
- iic_delay_1us();
- IIC_SCL_1();
- iic_delay_1us();
- IIC_SCL_0();
- iic_delay_1us();
- IIC_SDA_1();
- }
-
-
- uint8_t iic_CheckDevice(uint8_t _Address){
- uint8_t ucAck;
-
- if (IIC_SDA_READ() && IIC_SCL_READ()){
- iic_start(); /* 发送启动信号 */
-
- /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
- iic_send_byte(_Address | 0);
- ucAck = iic_wait_ack();
-
- iic_stop();
-
- return ucAck;
- }
- return 1;
- }
-
-
- void Device_WriteData(uint8_t DeciveAddr,uint8_t *Data,int size){
- int count=size;
- uint8_t *pData=Data;
- //起始信号
- iic_start();
- //发送器件地址
- iic_send_byte(DeciveAddr);
- //等待应答
- iic_wait_ack();
- while(count--)
- {
- //发送数据
- iic_send_byte(*pData++);
- //等待应答
- iic_wait_ack();
- }
-
- //结束信号
- iic_stop();
- }
-
-
-

实验用了PB8和PB9,设置的都是开漏输出式,因为在这个模式下有如下说明:

在配置成开漏输出时,可以读IDR寄存器。
整个源码的格式写法和之前手撸51中的IIC的类似,包括start,stop, ack等的时序。
这里是直接操作的寄存器来输出/输入数据。输出时比如SDA,设置BSRR,读取状态时,比如SDA中的ack,直接读取IDR寄存器。
- #define IIC_SCL_1() IIC_GPIO_PORT->BSRR = IIC_SCL_PIN // SCL = 1
- #define IIC_SCL_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U // SCL = 0
-
- #define IIC_SDA_1() IIC_GPIO_PORT->BSRR = IIC_SDA_PIN // SDA = 1
- #define IIC_SDA_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U // SDA = 0
-
- #define IIC_SDA_READ() (IIC_GPIO_PORT->IDR & IIC_SDA_PIN) // 读SDA引脚
- #define IIC_SCL_READ() (IIC_GPIO_PORT->IDR & IIC_SCL_PIN) // 读SCL引脚
其它地方就没什么分析的了,和51的代码都一致,O(∩_∩)O哈哈~