STM32H743 RL-TCP(一)基于RTX环境移植硬汉大哥例程(使用阿波罗开发板)
STM32H743 RL-TCP(一)基于RTX环境移植硬汉大哥例程(使用阿波罗开发板)
由于最近开始学习TCP/IP协议栈,从网上查询资料发现嵌入式端的主流协议栈包括lwip、RL-TCP等。在安富莱电子论坛上能找到lwip和RL-TCP的教程,综合来看,lwip用的人更多,但arm近来主推RL-TCP,在MDK5中使用较为方便,仅需要简单配置。
最终本人选择了RL-TCP。硬汉大哥对RL-TCP有相关的教程,十分详细,但目前来说其教程都是基于安富莱v7开发板。由于手头只有阿波罗板,故需要对此进行适配,折腾了两天,遇到无数的坑,最终简单运行硬汉的demo,故以此文记录如何移植。
1、准备工作
1、硬件:正点原子阿波罗开发板,核心板型号为STM32H743IIT6、烧录器等
2、软件:MDK5.31(目前本人的环境,没尝试过其他版本)、来自硬汉大哥的工程移植模板
其余环境配置如下
软件包汇总下载地址
http://www.armbbs.cn/forum.php?mod=viewthread&tid=95609
2、移植
打开工程目录文件,由于之前的步骤按照硬汉大哥的教程即可实现,此处省略,着眼于阿波罗开发板的适配。
2.1 修改网卡驱动
阿波罗板载PHY芯片型号为LAN8720A,但安富莱v7开发板为DM9161。并且根据阿波罗原理图可知,LAN8270A的复位引脚接在了IO扩展芯片PCF8574上,初始化时需要对PCF8574 的 D7 口写0复位。此处很容易犯错!!!!
同时,二者的引脚接法也有所不同。
首先,删除原有网卡DM9161驱动,将PHY_LAN8720.c 和 PHY_LAN8720.h添加进工程。这两个文件在这里。
随后添加复位部分文件,这里需要iic驱动PCF8574,故具有四个文件。pcf8574.c
/ pcf8574.h
/ myiic.c
/ myiic.h
myiic.c
(修改自原子的myiic.c)
#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)
{
for(int i; i<480*nus; i++)
{
}
// 采用此方式是为避免出现systick卡死情况
}
//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;
}
myiic.h
(修改自原子)
#ifndef _MYIIC_H
#define _MYIIC_H
#include "bsp.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)
void delay_us(uint32_t nus);
//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
···
pcf8574.c
#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输出高电平
PCF8574_WriteBit(ETH_RESET_IO,0);
HAL_Delay(100);
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;
}
pcf8574.h
#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
将此四个文件添加至工程,如下图所示
2.2 修改引脚约束
二者的引脚约束不同,需重新对应并修改。
2.2.1 网卡引脚
在ETH_Init.c文件中,将函数HAL_ETH_MspInit
修改为如下所示,注意几个引脚初始化位置的改动。
/*
*********************************************************************************************************
* 函 数 名: HAL_ETH_MspInit
* 功能说明: 以太网初始化调用的底层回调,用于初始化IO,时钟和中断
* 形 参: ---
* 返 回 值: 无
*********************************************************************************************************
*/
void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
{
GPIO_InitTypeDef GPIO_InitStruct;
/*
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
if(heth->Instance==ETH)
{
/* 使能外设时钟 */
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
__HAL_RCC_ETH1RX_CLK_ENABLE();
/* 使能时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/* 配置PA1, PA2 , PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL ;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置PB11 */
GPIO_InitStruct.Pin = GPIO_PIN_11;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* 配置PC1, PC4和PC5 */
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* 配置G13和PG14 */
GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_14;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* 设置中断优先级 */
HAL_NVIC_SetPriority(ETH_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
}
}
2.2.2 其他冲突引脚
初始化时如果发生冲突,引脚将无法正常工作。此部分包含 按键(key) 串口3 。
2.2.2.1 KEY
打开bsp_key.c, 注意到按键的K3与iic引脚PH4冲突,需要修改成如下所示,注意,由于阿波罗板没有JOY摇杆,需要将其注释并修改按键数目。
#include "bsp.h"
#define HARD_KEY_NUM 3 /* 实体按键个数 */
#define KEY_COUNT (HARD_KEY_NUM + 2) /* 8个独立建 + 2个组合按键 */
/* 使能GPIO时钟 */
//#define ALL_KEY_GPIO_CLK_ENABLE() { \
// __HAL_RCC_GPIOB_CLK_ENABLE(); \
// __HAL_RCC_GPIOC_CLK_ENABLE(); \
// __HAL_RCC_GPIOG_CLK_ENABLE(); \
// __HAL_RCC_GPIOH_CLK_ENABLE(); \
// __HAL_RCC_GPIOI_CLK_ENABLE(); \
// };
#define ALL_KEY_GPIO_CLK_ENABLE() { \
__HAL_RCC_GPIOC_CLK_ENABLE(); \
__HAL_RCC_GPIOH_CLK_ENABLE(); \
};
/* 依次定义GPIO */
typedef struct
{
GPIO_TypeDef* gpio;
uint16_t pin;
uint8_t ActiveLevel; /* 激活电平 */
}X_GPIO_T;
/* GPIO和PIN定义 */
static const X_GPIO_T s_gpio_list[HARD_KEY_NUM] = {
{GPIOH, GPIO_PIN_3, 0}, /* K1 */
{GPIOH, GPIO_PIN_2, 0}, /* K2 */
{GPIOC, GPIO_PIN_13, 0}, /* K3 */
// {GPIOG, GPIO_PIN_2, 0}, /* JOY_U */
// {GPIOB, GPIO_PIN_0, 0}, /* JOY_D */
// {GPIOG, GPIO_PIN_3, 0}, /* JOY_L */
// {GPIOG, GPIO_PIN_7, 0}, /* JOY_R */
// {GPIOI, GPIO_PIN_11, 0}, /* JOY_OK */
};
还有一个地方需要修改否则无法正常使用按键,就是在函数bsp_InitKeyVar
中需要注释如下几行
/* 摇杆上下左右,支持长按1秒后,自动连发 */
// bsp_SetKeyParam(KID_JOY_U, 100, 6);
// bsp_SetKeyParam(KID_JOY_D, 100, 6);
// bsp_SetKeyParam(KID_JOY_L, 100, 6);
// bsp_SetKeyParam(KID_JOY_R, 100, 6);
2.2.2.2 串口
此处串口默认打开USART3,与PB11
冲突,需要将头文件 bsp_uart_fifo.h
中的 宏定义UART3_FIFO_EN
改为0
#define UART3_FIFO_EN 0
3、添加网口复位代码
打开bsp.h
,添加头文件引用#include "pcf8574.h"
打开bsp.c
,找到函数bsp_Init
,修改为
void bsp_Init(void)
{
bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitUart(); /* 初始化串口 */
PCF8574_Init();
bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitTimer(); /* 初始化滴答定时器 */
}
随后打开pcf8574.c
在函数PCF8574_Init
末尾添加如下代码,将P7拉低
PCF8574_WriteBit(ETH_RESET_IO,0);
HAL_Delay(100);
4、测试
编译下载到板上,可以正常ping通。注意计算机网口如果没开DHCP服务器,需要将开发板设置为静态IP。具体参见安富莱_STM32-V7开发板_RL-TCPnet V7.X网络教程(V0.4).pdf
最终结果
致谢
十分感谢原子哥和硬汉哥对于嵌入式开发入门的帮助。
本文地址:https://blog.csdn.net/Onelasttime/article/details/107937124
上一篇: 抓包分析华为IOT平台CoAP对接流程