此文章是基于正点原子阿波罗F429开发版的,
利用STM32CubeMX新建一个项目带串口printf输出的,请查看我前面的文章,这里跳过新建项目的那些.
点击跳转
1.ETH配置.,除了标记的地方,其它都是默认值
2.lwip配置.除了标记的地方,其它都是默认值.
3.生成代码.打开项目.
新建四个文件,分别为:pcf8574.c,pcf8574.h,myiic.c,myiic.h.这四个文件是基于正点原子代码改的,主要是为了复位LAN8720用的.因为LAN8720的复位脚不是直接MCU上的,而是接在PCF8574那里的.但是正点原子的代码是适合在他的教程代码的.不适合我们STM32CubeMX直接生成的代码.我稍作了修改.修改后代码如下.
#include "pcf8574.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F429开发板
//PCF8574驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/13
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//初始化PCF8574
uint8_t PCF8574_Init(void)
{
uint8_t temp=0;
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
GPIO_Initure.Pin=GPIO_PIN_12; //PB12
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化
IIC_Init(); //IIC初始化
//检查PCF8574是否在位
IIC_Start();
IIC_Send_Byte(PCF8574_ADDR); //写地址
temp=IIC_Wait_Ack(); //等待应答,通过判断是否有ACK应答,来判断PCF8574的状态
IIC_Stop(); //产生一个停止条件
PCF8574_WriteOneByte(0XFF); //默认情况下所有IO输出高电平
return temp;
}
//读取PCF8574的8位IO值
//返回值:读到的数据
uint8_t PCF8574_ReadOneByte(void)
{
uint8_t temp=0;
IIC_Start();
IIC_Send_Byte(PCF8574_ADDR|0X01); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop(); //产生一个停止条件
return temp;
}
//向PCF8574写入8位IO值
//DataToWrite:要写入的数据
void PCF8574_WriteOneByte(uint8_t DataToWrite)
{
IIC_Start();
IIC_Send_Byte(PCF8574_ADDR|0X00); //发送器件地址0X40,写数据
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop(); //产生一个停止条件
HAL_Delay(10);
}
//设置PCF8574某个IO的高低电平
//bit:要设置的IO编号,0~7
//sta:IO的状态;0或1
void PCF8574_WriteBit(uint8_t bit,uint8_t sta)
{
__IO uint8_t data;
data=PCF8574_ReadOneByte(); //先读出原来的设置
if(sta==0)data&=~(1<<bit);
else data|=1<<bit;
PCF8574_WriteOneByte(data); //写入新的数据
}
//读取PCF8574的某个IO的值
//bit:要读取的IO编号,0~7
//返回值:此IO的值,0或1
uint8_t PCF8574_ReadBit(uint8_t bit)
{
uint8_t data;
data=PCF8574_ReadOneByte(); //先读取这个8位IO的值
if(data&(1<<bit))return 1;
else return 0;
}
#ifndef __PCF8574_H
#define __PCF8574_H
#include "myiic.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F429开发板
//PCF8574驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/13
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
#define PCF8574_INT PBin(12) //PCF8574 INT脚
#define PCF8574_ADDR 0X40 //PCF8574地址(左移了一位)
//PCF8574各个IO的功能
#define BEEP_IO 0 //蜂鸣器控制引脚 P0
#define AP_INT_IO 1 //AP3216C中断引脚 P1
#define DCMI_PWDN_IO 2 //DCMI的电源控制引脚 P2
#define USB_PWR_IO 3 //USB电源控制引脚 P3
#define EX_IO 4 //扩展IO,自定义使用 P4
#define MPU_INT_IO 5 //MPU9250中断引脚 P5
#define RS485_RE_IO 6 //RS485_RE引脚 P6
#define ETH_RESET_IO 7 //以太网复位引脚 P7
uint8_t PCF8574_Init(void);
uint8_t PCF8574_ReadOneByte(void);
void PCF8574_WriteOneByte(uint8_t DataToWrite);
void PCF8574_WriteBit(uint8_t bit,uint8_t sta);
uint8_t PCF8574_ReadBit(uint8_t bit);
#endif
#include "myiic.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F429开发板
//IIC驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/13
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD; //LOAD的值
ticks=nus*180; //需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
}
//IIC初始化
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOH_CLK_ENABLE(); //使能GPIOH时钟
//PH4,5初始化设置
GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5;
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
IIC_SDA(1);
IIC_SCL(1);
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA(1);
IIC_SCL(1);
delay_us(4);
IIC_SDA(0);//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL(0);
IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL(1);
IIC_SDA(1);//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA(1);delay_us(1);
IIC_SCL(1);delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL(0);//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(0);
delay_us(2);
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(1);
delay_us(2);
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL(0);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA( (txd&0x80)>>7 );
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL(1);
delay_us(2);
IIC_SCL(0);
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL(0);
delay_us(2);
IIC_SCL(1);
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
#ifndef _MYIIC_H
#define _MYIIC_H
#include "main.h"
#include "lwip.h"
#include "usart.h"
#include "gpio.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F429开发板
//IIC驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/13
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//IO方向设置
#define SDA_IN() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=0<<5*2;} //PH5输入模式
#define SDA_OUT() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=1<<5*2;} //PH5输出模式
//IO操作
#define IIC_SCL(a) if(a) HAL_GPIO_WritePin(GPIOH,GPIO_PIN_4,GPIO_PIN_SET); else HAL_GPIO_WritePin(GPIOH,GPIO_PIN_4,GPIO_PIN_RESET);//SCL
#define IIC_SDA(a) if(a) HAL_GPIO_WritePin(GPIOH,GPIO_PIN_5,GPIO_PIN_SET); else HAL_GPIO_WritePin(GPIOH,GPIO_PIN_5,GPIO_PIN_RESET);//SCL
#define READ_SDA HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_5)
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(uint8_t txd); //IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(uint8_t daddr,uint8_t addr,uint8_t data);
uint8_t IIC_Read_One_Byte(uint8_t daddr,uint8_t addr);
#endif
在main.c中添加头文件:#include “pcf8574.h”
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "pcf8574.h"
/* USER CODE END Includes */
在main函数中添加PCF8574的初始化函数:PCF8574_Init();
/* USER CODE BEGIN SysInit */
PCF8574_Init();
/* USER CODE END SysInit */
打开ethernetif.c
在ethernetif.c添加头文件:#include “pcf8574.h”
在==static void low_level_init(struct netif *netif)==函数中添加下面代码:
/* USER CODE BEGIN MACADDRESS */
PCF8574_WriteBit(ETH_RESET_IO,1); //硬件复位
HAL_Delay(50);
PCF8574_WriteBit(ETH_RESET_IO,0); //复位结束
HAL_Delay(50);
/* USER CODE END MACADDRESS */
打开stm32f4xx_hal_eth.c找到==HAL_StatusTypeDef HAL_ETH_Init(ETH_HandleTypeDef *heth)==函数修改里面的两端代码(不修改也没事,只是开机有时候会判断为连接错误,具体原因没找.)
if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_LINKED_STATE)
{
// /* In case of write timeout */
// err = ETH_ERROR;
//
// /* Config MAC and DMA */
// ETH_MACDMAConfig(heth, err);
//
// heth->State= HAL_ETH_STATE_READY;
//
// /* Process Unlocked */
// __HAL_UNLOCK(heth);
//
// return HAL_TIMEOUT;
break;
}
} while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));
if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_AUTONEGO_COMPLETED)
{
// /* In case of write timeout */
// err = ETH_ERROR;
//
// /* Config MAC and DMA */
// ETH_MACDMAConfig(heth, err);
//
// heth->State= HAL_ETH_STATE_READY;
//
// /* Process Unlocked */
// __HAL_UNLOCK(heth);
//
// return HAL_TIMEOUT;
break;
}
} while (((phyreg & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));
新建use_lwip.c和use_lwip.h.为了处理网络热插拔.
#include "use_lwip.H"
extern struct netif *netif_default;
extern struct netif gnetif;
extern ETH_HandleTypeDef heth;
static uint8_t b_ip_state = 0; //ip状态
static uint8_t b_network_cable = 0; //网线状态
/*插入网线的话重新获取IP操作*/
void ethernetif_notify_conn_changed(struct netif *netif)
{
if(netif_is_link_up(netif) && !netif_is_up(netif))
{
netif_set_up(netif);
extern err_t dhcp_start(struct netif *netif);
dhcp_start(netif);
}
}
/*
读取网线状态
读取IP值
*/
void use_read_network_cable(void)
{
uint32_t phy_data = 0;
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &phy_data); //读取PHY数据
if(((phy_data & PHY_LINKED_STATUS) != PHY_LINKED_STATUS)) //
{
if(!b_network_cable) return;
b_network_cable = 0;
printf("未插网线\r\n");
b_ip_state = 0;
gnetif.ip_addr.addr = 0;
return;
}
if(!b_network_cable)
{
b_network_cable = 1;
printf("已插网线\r\n");
return;
}
if(b_ip_state)return;
if(gnetif.ip_addr.addr)
{
b_ip_state = 1;
uint8_t ip4_number[6];
ip4_number[0] = gnetif.hwaddr[0];
ip4_number[1] = gnetif.hwaddr[1];
ip4_number[2] = gnetif.hwaddr[2];
ip4_number[3] = gnetif.hwaddr[3];
ip4_number[4] = gnetif.hwaddr[4];
ip4_number[5] = gnetif.hwaddr[5];
printf("MAC地址..............%x.%x.%x.%x.%x.%x\r\n",ip4_number[0],ip4_number[1],ip4_number[2],ip4_number[3],ip4_number[4],ip4_number[5]);
ip4_number[3] = gnetif.ip_addr.addr >> 24;
ip4_number[2] = gnetif.ip_addr.addr >> 16;
ip4_number[1] = gnetif.ip_addr.addr >> 8;
ip4_number[0] = gnetif.ip_addr.addr >> 0;
printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",ip4_number[0],ip4_number[1],ip4_number[2],ip4_number[3]);
ip4_number[3] = gnetif.netmask.addr >> 24;
ip4_number[2] = gnetif.netmask.addr >> 16;
ip4_number[1] = gnetif.netmask.addr >> 8;
ip4_number[0] = gnetif.netmask.addr >> 0;
printf("通过DHCP获取到子网掩码..............%d.%d.%d.%d\r\n",ip4_number[0],ip4_number[1],ip4_number[2],ip4_number[3]);
ip4_number[3] = gnetif.gw.addr >> 24;
ip4_number[2] = gnetif.gw.addr >> 16;
ip4_number[1] = gnetif.gw.addr >> 8;
ip4_number[0] = gnetif.gw.addr >> 0;
printf("通过DHCP获取到默认网关..............%d.%d.%d.%d\r\n",ip4_number[0],ip4_number[1],ip4_number[2],ip4_number[3]);
}
}
void use_lwip_process(void)
{
MX_LWIP_Process(); //LWIP轮询任务
ethernetif_set_link(netif_default); //重新插网线进行获取IP的任务
use_read_network_cable();
}
#ifndef __use_lwip_h_
#define __use_lwip_h_
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip.h"
#include "usart.h"
#include "gpio.h"
#include "pcf8574.h"
#include
void use_lwip_process(void);
#endif
在main.c中添加头文件:#include “use_lwip.h”
在main函数里添加一段如下代码:
/* USER CODE BEGIN WHILE */
printf("程序开始\r\n");
while (1)
{
use_lwip_process();
/* USER CODE END WHILE */