单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机
W55MH32是WIZnet重磅推出的高性能以太网单片机,它为用户带来前所未有的集成化体验。这颗芯片将强大的组件集于一身,具体来说,一颗W55MH32内置高性能Arm® Cortex-M3核心,其主频最高可达216MHz;配备1024KB FLASH与96KB SRAM,满足存储与数据处理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP协议栈、内置MAC以及PHY,拥有独立的32KB以太网收发缓存,可供8个独立硬件socket使用。如此配置,真正实现了All-in-One解决方案,为开发者提供极大便利。
在封装规格上,W55MH32 提供了两种选择:QFN100和QFN68。
W55MH32L采用QFN100封装版本,尺寸为12x12mm,其资源丰富,专为各种复杂工控场景设计。它拥有66个GPIO、3个ADC、12通道DMA、17个定时器、2个I2C、5个串口、2个SPI接口(其中1个带I2S接口复用)、1个CAN、1个USB2.0以及1个SDIO接口。如此丰富的外设资源,能够轻松应对工业控制中多样化的连接需求,无论是与各类传感器、执行器的通信,还是对复杂工业协议的支持,都能游刃有余,成为复杂工控领域的理想选择。 同系列还有QFN68封装的W55MH32Q版本,该版本体积更小,仅为8x8mm,成本低,适合集成度高的网关模组等场景,软件使用方法一致。更多信息和资料请进入http://www.w5500.com/网站或者私信获取。
此外,本W55MH32支持硬件加密算法单元,WIZnet还推出TOE+SSL应用,涵盖TCP SSL、HTTP SSL以及 MQTT SSL等,为网络通信安全再添保障。
为助力开发者快速上手与深入开发,基于W55MH32L这颗芯片,WIZnet精心打造了配套开发板。开发板集成WIZ-Link芯片,借助一根USB C口数据线,就能轻松实现调试、下载以及串口打印日志等功能。开发板将所有外设全部引出,拓展功能也大幅提升,便于开发者全面评估芯片性能。
若您想获取芯片和开发板的更多详细信息,包括产品特性、技术参数以及价格等,欢迎访问官方网页:http://www.w5500.com/,我们期待与您共同探索W55MH32的无限可能。

第五章 GPIO输出——使用固件库点亮LED
本章参考资料:《W55MH32-参考手册》GPIO和RCC章节、库帮助文档。
利用库建立好的工程模板,就可以方便地使用标准库编写应用程序了,可以说从这一章我们真正开始迈入固件库开发的大门。LED灯的控制使用到GPIO外设的基本输出功能。
5.1 硬件设计
在本教程中W55MH32芯片与LED灯的连接见图LED硬件原理图 :

此电路为参考电路,这个LED灯的阴极都是连接到W55MH32的GPIO引脚,只要我们控制GPIO引脚的电平输出状态,即可控制LED灯的亮灭。若您使用的实验板LED灯的连接方式或引脚不一样, 只需根据我们的工程修改引脚即可,程序的控制原理相同。
5.2 软件设计
5.2.1 编程要点
1.使能GPIO端口时钟;
2.初始化GPIO目标引脚为推挽输出模式;
3.编写简单测试程序,控制GPIO引脚输出高、低电平。
5.2.2 代码分析
1. 头文件与宏定义
#include < stdlib.h > #include < string.h > #include < stdio.h > #include "delay.h" #include "w55mh32.h" #define GPIO_GROUP_TEST GPIOB // 使用GPIOB #define GPIO_MODE_TEST GPIO_Mode_Out_PP // 推挽输出模式 #define GPIO_SPEED_TEST GPIO_Speed_50MHz // 50MHz速度 #define GPIO_PIN1_TEST GPIO_Pin_0 // 引脚0 #define GPIO_PIN2_TEST GPIO_Pin_2 // 引脚2 #define GPIO_PIN3_TEST GPIO_Pin_3 // 引脚3 USART_TypeDef *USART_TEST = USART1; // 使用USART1
头文件:包含标准库和自定义头文件(如延时和芯片相关库)。
宏定义:
GPIO_GROUP_TEST:指定使用 GPIOB。
GPIO_MODE_TEST:设置为推挽输出(GPIO_Mode_Out_PP),用于驱动外部设备(如 LED)。
GPIO_PIN1_TEST/Pin2/Pin3:定义三个输出引脚(PB0、PB2、PB3)。
串口配置:使用 USART1 进行通信。
2. 函数声明
void UART_Configuration(uint32_t bound); // 串口配置函数 void GPIO_Configuration(void); // GPIO配置函数
3. main 函数(主逻辑)
int main(void) { RCC_ClocksTypeDef clocks; delay_init(); // 初始化延时 UART_Configuration(115200); // 配置串口(波特率115200) RCC_GetClocksFreq(&clocks); // 获取系统时钟频率 // 打印系统时钟信息 printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhzn", (float)clocks.SYSCLK_Frequency / 1000000, ...); printf("GPIO IO Out Tset.n"); // 打印测试信息 GPIO_Configuration(); // 配置GPIO为输出 // 无限循环:流水灯效果 while (1) { // 依次点亮PB0 → PB2 → PB3 GPIO_SetBits(GPIOB, GPIO_Pin_0); delay_ms(200); GPIO_SetBits(GPIOB, GPIO_Pin_2); delay_ms(200); GPIO_SetBits(GPIOB, GPIO_Pin_3); delay_ms(200); // 依次熄灭PB0 → PB2 → PB3 GPIO_ResetBits(GPIOB, GPIO_Pin_0); delay_ms(200); GPIO_ResetBits(GPIOB, GPIO_Pin_2); delay_ms(200); GPIO_ResetBits(GPIOB, GPIO_Pin_3); delay_ms(200); } }
初始化:延时、串口、时钟频率获取。
串口输出:打印系统时钟信息和测试提示。
GPIO 配置:设置 GPIOB 的三个引脚为输出。
流水灯逻辑:
GPIO_SetBits:置高电平(熄灭 LED)。
GPIO_ResetBits:置低电平(点亮 LED)。
delay_ms(200):每个动作间隔 200ms,形成循环闪烁。
4. GPIO_Configuration 函数(GPIO 初始化)
void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟 // 配置PB0、PB2、PB3为推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIOB }
时钟使能:启用 GPIOB 的时钟。
引脚配置:
引脚:PB0、PB2、PB3(通过位或运算同时配置)。
模式:推挽输出(GPIO_Mode_Out_PP),适合驱动 LED 等外设。
速度:50MHz(满足高频操作需求)。
5. UART_Configuration 函数(串口初始化)
void UART_Configuration(uint32_t bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能USART1和GPIOA时钟(PA9/TX, PA10/RX) RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX引脚(PA9):复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置RX引脚(PA10):浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1参数 USART_InitStructure.USART_BaudRate = bound; // 波特率 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_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发模式 USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); // 使能USART1 }
引脚配置:
PA9(TX):复用推挽输出(用于发送数据)。
PA10(RX):浮空输入(用于接收数据)。
串口参数:115200 波特率、8 位数据、1 位停止位、无校验。
6. 串口输出函数(SER_PutChar 和 fputc)
int SER_PutChar(int ch) { while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC)); // 等待发送完成 USART_SendData(USART_TEST, (uint8_t)ch); // 发送字符 return ch; } int fputc(int c, FILE *f) { if (c == 'n') SER_PutChar('r'); // 换行时添加回车(适配终端) return SER_PutChar(c); // 重定向printf到串口 }
SER_PutChar:通过 USART 发送单个字符,等待发送完成标志(USART_FLAG_TC)。
fputc:重定向 C 库的printf函数到串口,支持换行符(n)自动添加回车(r)。
5.2.3 下载验证
把编译好的程序下载到开发板并复位,可看到LED亮灭交互。
5.3 W55MH32标准库补充知识
5.3.1 SystemInit函数去哪了?
这个函数在W55MH32标准库的“system_w55mh32.c”文件中定义了,而我们的工程已经包含该文件。标准库中的SystemInit函数把芯片的系统时钟设置成了72MHz, 即此时AHB时钟频率为72MHz,APB2为72MHz,APB1为36MHz。当W55MH32芯片上电后,执行启动文件中的指令后,会调用该函数,设置系统时钟为以上状态。
5.3.2 断言
5.3.2.1 断言的作用
在 W55MH32 开发过程中,开发者编写的代码需要对各种参数和状态进行检查。断言提供了一种简单有效的方式,用于在代码中插入检查点,验证某些条件是否满足。如果条件不满足,断言会触发错误,帮助开发者快速定位和解决问题。具体见代码清单:GPIO输出-1 :
代码清单:GPIO输出-1 GPIO_Init函数的断言部分
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) { uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00; /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_MODE(GPIO_InitStruct- >GPIO_Mode)); assert_param(IS_GPIO_PIN(GPIO_InitStruct- >GPIO_Pin)); /* ------- 以下内容省略,跟前面我们定义的函数内容相同----- */
基本上每个库函数的开头都会有这样类似的内容,这里的“assert_param”实际是一个宏,在库函数中它用于检查输入参数是否符合要求, 若不符合要求则执行某个函数输出警告,“assert_param”的定义见代码清单:GPIO输出-2 :
代码清单:GPIO输出-2 w55mh32_conf.h文件中关于断言的定义
#ifdef USE_FULL_ASSERT /** * @brief assert_param 宏用于函数的输入参数检查 * @param expr:若expr值为假,则调用assert_failed函数 * 报告文件名及错误行号 * 若expr值为真,则不执行操作 */ #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) /* 错误输出函数 ------------------------------------------------------- */ void assert_failed(uint8_t* file, uint32_t line); #else #define assert_param(expr) ((void)0) #endif
这段代码的意思是,假如我们不定义“USE_FULL_ASSERT”宏,那么“assert_param”就是一个空的宏(#else与#endif之间的语句生效),没有任何操作。 从而所有库函数中的assert_param实际上都无意义,我们就当看不见好了。
假如我们定义了“USE_FULL_ASSERT”宏,那么“assert_param”就是一个有操作的语句(#if与#else之间的语句生效), 该宏对参数expr使用C语言中的问号表达式进行判断,若expr值为真,则无操作(void 0),若表达式的值为假, 则调用“assert_failed”函数,且该函数的输入参数为“__FILE__”及“__LINE__”, 这两个参数分别代表 “assert_param”宏被调用时所在的“文件名”及“行号”。
但库文件只对“assert_failed”写了函数声明,没有写函数定义,实际用时需要用户来定义, 我们一般会用printf函数来输出这些信息,见代码清单:GPIO输出-3 :
代码清单:GPIO输出-3 assert_failed 输出错误信息
void assert_failed(uint8_t * file, uint32_t line) { printf(“rn 输入参数错误,错误文件名=%s,行号=%s”,file,line); }
那么为什么函数输入参数不对的时候,assert_param宏中的expr参数值会是假呢?这要回到GPIO_Init函数,看它对assert_param宏的调用, 它被调用时分别以“IS_GPIO_ALL_PERIPH(GPIOx)”、“IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)”等作为输入参数,也就是说被调用时, expr实际上是一条针对输入参数的判断表达式。例如“IS_GPIO_PIN”的宏定义:
#define IS_GPIO_PIN(PIN) ((PIN) != (uint32_t)0x00)
若它的输入参数 PIN 值为0,则表达式的值为假,PIN非0时表达式的值为真。 我们知道用于选择GPIO引脚号的宏“GPIO_Pin_x”的值至少有一个数据位为1, 这样的输入参数才有意义,若GPIO_InitStruct->GPIO_Pin的值为0,输入参数就无效了。配合“IS_GPIO_PIN”这句表达式, “assert_param”就实现了检查输入参数的功能。对assert_param宏的其它调用方式类似,大家可以自己看库源码来研究一下。
5.3.3 Doxygen注释规范
在W55MH32标准库以及我们自己编写的文件中,可以看到一些比较特别的注释,类似代码清单:GPIO输出-4:
代码清单:GPIO输出-4 Doxygen注释规范
/** * @brief 初始化控制LED的IO * @param 无 * @retval 无 */
这是一种名为“Doxygen”的注释规范,如果在工程文件中按照这种规范去注释,可以使用Doxygen软件自动根据注释生成帮助文档。 我们所说非常重要的库帮助文档
5.3.4 防止头文件重复包含
在W55MH32标准库的所有头文件以及我们自己编写的.h头文件中,可看到类似代码清单:GPIO输出-5的宏定义。 它的功能是防止头文件被重复包含,避免引起编译错误。
代码清单:GPIO输出-5 防止头文件重复包含的宏
#ifndef __LED_H #define __LED_H /*此处省略头文件的具体内容*/ #endif /* end of __LED_H */
在头文件的开头,使用“#ifndef”关键字,判断标号“__LED_H”是否被定义,若没有被定义,则从“#ifndef”至“#endif”关键字之间的内容都有效, 也就是说,这个头文件若被其它文件“#include”,它就会被包含到其该文件中了,且头文件中紧接着使用“#define”关键字定义上面判断的标号“__LED_H”。 当这个头文件被同一个文件第二次“#include”包含的时候, 由于有了第一次包含中的“#define __LED_H”定义,这时再判断“#ifndef__LED_H”, 判断的结果就是假了,从“#ifndef”至“#endif”之间的内容都无效,从而防止了同一个头文件被包含多次,编译时就不会出现“redefine(重复定义)”的错误了。
一般来说,我们不会直接在C的源文件写两个“#include”来包含同一个头文件,但可能因为头文件内部的包含导致重复,这种代码主要是避免这样的问题。
WIZnet 是一家无晶圆厂半导体公司,成立于 1998 年。产品包括互联网处理器 iMCU™,它采用 TOE(TCP/IP 卸载引擎)技术,基于独特的专利全硬连线 TCP/IP。iMCU™ 面向各种应用中的嵌入式互联网设备。
WIZnet 在全球拥有 70 多家分销商,在香港、韩国、美国设有办事处,提供技术支持和产品营销。
香港办事处管理的区域包括:澳大利亚、印度、土耳其、亚洲(韩国和日本除外)。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !