C++例程:使用I/O模拟IIC接口(6)
完整的STM32F405代码工程I2C驱动源代码跟踪
一)myiic.c
#include "myiic.h"
#include "delay.h"
#include "stm32f4xx_rcc.h"
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
//SCL_1->GPIOA0,SDA_1->GPIOA1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
SCL_1=1;
SDA_1=1;
}
//产生IIC1起始信号
void IIC1_Start(void)
{
SDA1_OUT(); //sda线输出
SDA_1=1;
SCL_1=1;
delay_us(4);
delay_us(4);
SDA_1=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
delay_us(4);
SCL_1=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC1_Stop(void)
{
SDA1_OUT();//sda线输出
SCL_1=0;
SDA_1=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
delay_us(4);
SCL_1=1;
delay_us(4);
delay_us(4);
SDA_1=1;//发送I2C总线结束信号
delay_us(4);
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC1_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA1_IN(); //SDA设置为输入
SDA_1=1;delay_us(1);
SCL_1=1;delay_us(1);
while(READ_SDA1)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC1_Stop();
return 1;
}
}
SCL_1=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC1_Ack(void)
{
SCL_1=0;
SDA1_OUT();
SDA_1=0;
delay_us(2);
delay_us(2);
SCL_1=1;
delay_us(2);
delay_us(2);
SCL_1=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC1_Send_Byte(u8 txd)
{
u8 t;
SDA1_OUT();
SCL_1=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
SDA_1=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
delay_us(2);
SCL_1=1;
delay_us(2);
delay_us(2);
SCL_1=0;
delay_us(2);
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC1_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA1_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL_1=0;
delay_us(2);
delay_us(2);
SCL_1=1;
receive<<=1;
if(READ_SDA1)receive++;
delay_us(1);
delay_us(1);
}
if (!ack)
IIC1_NAck();//发送nACK
else
IIC1_Ack(); //发送ACK
return receive;
}
二) myiic.h
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//
//PA1输入模式 输出模式
#define SDA1_IN() {GPIOA->MODER&=~(3<<(1*2));GPIOA->MODER|=0<<1*2;}
#define SDA1_OUT() {GPIOA->MODER&=~(3<<(1*2));GPIOA->MODER|=1<<1*2;}
//IO操作函数
#define SCL_1 PAout(0) //SCL
#define SDA_1 PAout(1) //SDA
#define READ_SDA1 PAin(1) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC1_Start(void); //发送IIC开始信号
void IIC1_Stop(void); //发送IIC停止信号
void IIC1_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC1_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC1_Wait_Ack(void); //IIC等待ACK信号
void IIC1_Ack(void); //IIC发送ACK信号
void IIC1_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
三) sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f4xx.h"
//
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
#define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#endif
四)stm32f4xx.h
#ifndef __STM32F4xx_H
#define __STM32F4xx_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct
{
/*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t MODER;
/*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OTYPER;
/*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t OSPEEDR;
/*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t PUPDR;
/*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t IDR;
/*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t ODR;
/*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRL;
/*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint16_t BSRRH;
/*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t LCKR;
/*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
__IO uint32_t AFR[2];
} GPIO_TypeDef;
/*!< Peripheral base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000)
/*!< Peripheral memory map */
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
/*!< AHB1 peripherals */
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __STM32F4xx_H */
五)stm32f4xx_rcc.h
#ifndef __STM32F4xx_RCC_H
#define __STM32F4xx_RCC_H
#ifdef __cplusplus
extern "C" {
#endif
#define RCC_AHB1Periph_GPIOD ((uint32_t)0x00000008)
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_RCC_H */
六)stm32f4xx_rcc.c
#include "stm32f4xx_rcc.h"
/**
* @brief Enables or disables the AHB1 peripheral clock.
* @note After reset, the peripheral clock (used for registers read/write access)
* is disabled and the application software has to enable this clock before
* using it.
* @param RCC_AHBPeriph: specifies the AHB1 peripheral to gates its clock.
* This parameter can be any combination of the following values:
* @arg RCC_AHB1Periph_GPIOA: GPIOA clock
* @arg RCC_AHB1Periph_GPIOB: GPIOB clock
* @arg RCC_AHB1Periph_GPIOC: GPIOC clock
* @arg RCC_AHB1Periph_GPIOD: GPIOD clock
* @arg RCC_AHB1Periph_GPIOE: GPIOE clock
* @arg RCC_AHB1Periph_GPIOF: GPIOF clock
* @arg RCC_AHB1Periph_GPIOG: GPIOG clock
* @arg RCC_AHB1Periph_GPIOG: GPIOG clock
* @arg RCC_AHB1Periph_GPIOI: GPIOI clock
* @arg RCC_AHB1Periph_GPIOJ: GPIOJ clock (STM32F42xxx/43xxx devices)
* @arg RCC_AHB1Periph_GPIOK: GPIOK clock (STM32F42xxx/43xxx devices)
* @arg RCC_AHB1Periph_CRC: CRC clock
* @arg RCC_AHB1Periph_BKPSRAM: BKPSRAM interface clock
* @arg RCC_AHB1Periph_CCMDATARAMEN CCM data RAM interface clock
* @arg RCC_AHB1Periph_DMA1: DMA1 clock
* @arg RCC_AHB1Periph_DMA2: DMA2 clock
* @arg RCC_AHB1Periph_DMA2D: DMA2D clock (STM32F429xx/439xx devices)
* @arg RCC_AHB1Periph_ETH_MAC: Ethernet MAC clock
* @arg RCC_AHB1Periph_ETH_MAC_Tx: Ethernet Transmission clock
* @arg RCC_AHB1Periph_ETH_MAC_Rx: Ethernet Reception clock
* @arg RCC_AHB1Periph_ETH_MAC_PTP: Ethernet PTP clock
* @arg RCC_AHB1Periph_OTG_HS: USB OTG HS clock
* @arg RCC_AHB1Periph_OTG_HS_ULPI: USB OTG HS ULPI clock
* @param NewState: new state of the specified peripheral clock.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
RCC->AHB1ENR |= RCC_AHB1Periph;
}
else
{
RCC->AHB1ENR &= ~RCC_AHB1Periph;
}
}
七)delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include <sys.h>
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif