STM32第三十二课(Freertos, HAL, Cubemx, Ethernet, LWIP)

通过在cubemx中配置ethernet和lwip,可以方便的使用网络功能。

首先是要配置ethernet。
++++++++++++++++++++++++++++
ethernet MAC----配置MAC地址。
PHY addr----配置PHY的个数。这里设置为1。
Rx Mode----配置为interrupt Mode。
Tx Mode----配置为Hardware Checksum。

control register ---- 地址设置为0x00。
status register ---- 地址设置为0x01。
special control / status register offset ----地址偏移设置为0x10。

PHY reset mask----设置为0x8000,
Select Loopback mode mask ----设置为0x4000,
set full deplex mode at 100Mbps mask----设置为0x2100,
set half deplex mode at 100Mbps mask----设置为0x2000,
set full deplex mode at 10Mbps mask----设置为0x0100,
set half deplex mode at 10Mbps mask----设置为0x0000,
enable AN mask----设置为0x1000,
restart AN mask----设置为0x0200,
select powerdown mode mask----设置为0x0800,
Isolate PHY from MII mask----设置为0x0400,
AN complete mask----设置为0x0020,
valid link established mask----设置为0x0004,
Jabber condition mask----设置为0x0002,

PHY speed mask ----设置为0x0004,
PHY duplex mask----设置为0x0010,

+++++++++++++++++++++++++++++
根据标准,SMI访问的PHY的register为0~31。data为16bits。
其中0~15为固定用途。
其中,
0x00----CR,
0x01----SR,
0x02,0x03----PHY Identifier.
0x04----AN advertisement,
0x05----ANLP base page ability,
0x06----AN expansion,

0x07----AN next page,
0x08----ANLP received next page,
0x09----Master Slave Control Register,
0x0A----Master Slave Status Register,

0x0F----extended status

从0x10到0x1F,是留给vendor的地址空间。

++++++++++++++++++++++++++++++++++++
来看看CR。
bit15----Reset,1为PHY复位,0为常规操作。
bit14----Loopback,1为enable loopback,0为disable loopback。

bit6----speed selection (MSB),
bit13----speed selection(LSB),
SS的值,
11----Reserved
10----1000Mbps
01----100Mbps
00----10Mbps

bit12----AN Enable, 1为enable AN,

bit11----power down,1为powerdown,0为常规操作。
bit10----isolate PHY from MII,1为isolate,0为normal。
bit9----restart AN,1为restart,0为normal,
bit8----duplex mode,1为full, 0 为 half

bit7----collision test,1为enable COL,0为disable COL。
bit6~bit0----Reserved。
++++++++++++++++++++++++++++++++++++++
来看看SR。
bit15----100BASE-T4 ability
bit14----100BASE-X Full Duplex ability
bit13----100BASE-X Half Duplex ability
bit12----10Mbps Full Duplex ability
bit11----10Mbps Half Duplex ability
bit10----100BASE-T2 Full Duplex ability
bit9----100BASE-T2 Half Duplex ability
bit8----extended status ability
bit6----MF preamble surpression
bit5----AN complete
bit4----remote fault
bit3----AN ability
bit2----link status
bit1----jabber condition detect
bit0----extended register ability

++++++++++++++++++++++++++++++++++++++++
再来配置LWIP。
首先是general setting.
DHCP----设置为enable。
ICMP----设置为enable。
IGMP----设置为disable。
DNS----设置为disable。
UDP----设置为enable。
MEMP_NUM_UDP_PCB----设置为4。表示静态分配4个UDP的PCB。
TCP ----设置为enable。
MEMP_NUM_TCP_PCB----设置为5。表示静态分配5个TCP的PCB。

然后是key option。
SYS----设置为OS used。
TIMERS----设置为sys_timeout。即systick。
MEMORY_PROTECT----设置为enable。
HEAP SIZE----设置为16384bytes。
MEMP_NUM_PBUF----设置为16。表示静态分配16个PBUF。
MEMP_NUM_RAW_PCB----设置为4.表示静态分配4个RAW_PCB。
MEMP_NUM_TCP_PCB_LISTEN----设置为8.表示静态分配8个listentcp。
MEMP_NUM_TCP_SEG_QUEUED----设置为16.表示静态分配16个segment。
MEMP_NUM_LOCALHOSTLIST----设置为1.表示静态分配1个localhostlist的条目。因为STM32只有一个MAC,所以只会有一个网口。

PBUF_POOL_SIZE----设置为16.表示静态分配16个PBUF。
PBUF_POOL_BUFSIZE----设置为592bytes,表示每个PBUF的大小。
ARP----设置为enable。

TCP_TTL----设置为255nodes.
TCP_RECEIVE_WINDOW----设置为2144bytes。
TCP_QUEUE_OOSEQ----设置为enable。
TCP_SACK_OUT----设置为disable。
TCP_MAX_SEG_SIZE----设置为536bytes。
TCP_SEND_BUFF----设置为1072bytes。
TCP_SEND_QUEUE_LEN----设置为9,表示允许最多9个PBUF作为TCP的发送队列。

NETIF_STATUS_CALLBACK----设置为disable。
NETIF_EXTEND_STATUS_CALLBACK----设置为disable。
NETIF_LINK_CALLBACK----设置为enable。当link状态发生变化时,会调用这个callback。

NETIF_LOOPBACK----设置为disable。

TCPIP_THREAD_NAME----设置TASK的名称。“tcpip_thread”
TCPIP_THREAD_STACKSIZE----设置为1024bytes。
TCPIP_THREAD_PRIO----设置为3。
TCPIP_MBOX_SIZE----设置为6。

DEFAULT_THREAD_NAME----设置TASK的名称。“lwip_thread”
DEFAULT_THREAD_STACKSIZE----设置为1024bytes。
DEFAULT_THREAD_PRIO----设置为3。
DEFAULT_RAW_RECVMBOX_SIZE----设置为0.
DEFAULT_TCP_RECVMBOX_SIZE----设置为6.
DEFAULT_ACCEPTMBOX_SIZE----设置为6.

NETCONN_API----设置为enable。

SOCKET_API----设置为enable。
SOCKET_COMPAT_BSD----设置为1.
SOCKET_OFFSET----设置为0。
SOCKET_SELECT_FUNC----设置为enable。
SOCKET_POLL_FUNC----设置为enable。

再来看看PPP,设置为disable。
IPV6,设置为disable。
HTTPD,设置为disable。
SNMP,设置为disable。
SNTP,设置为disable。
SMTP,设置为disable。

再来看看debug。
DEBUG_LEVEL,设置为all。

checksum_by_hardware,设置为enable。

statistic,设置为disable。

perf,设置为disable。
sanity check,设置为disable。

MDNS,设置为disable。
TFTP,设置为disable。

++++++++++++++++++++++++++++++++++++++
在使用了Freertos时,通常使用socket API进行应用编程。
这些API,在sockets.c文件中。

Socket API 是基于 NETCONN API 之上来实现的,系统最多提供
MEMP_NUM_NETCONN
个 netconn 连接结构,LwIP 定义了一个 lwip_sock 类型的 sockets数组,通过socket index就可以直接索引并且访问这个结构体了,

使用socket时,首先会调用
socket()
函数创建一个socket。在lwip中实际调用的就是lwip_socket()函数。lwip_socket()接下来通过alloc_socket()分配了socket。从sockets[]中找到一个空闲的socket,并返回index。这是一个全局变量,存有系统所有的socket,

static struct lwip_socket sockets[NUM_SOCKETS];

用户调用bind()时,实际调用的是lwip_bind(),lwip_bind()需要由用户传入待绑定的ip地址和端口号。用IP和PORT,填充到socket中去。
如果用户在调用bind()时,想直接使用本机地址作为绑定的ip地址,可以直接在这个参数传一个0。这样在bind()时不设置pcb里的ip地址,而在TCP层发包时才通过调用ip层函数来设置pcb的ip地址。

用户使用socket,调用listen()时,实际调用的是lwip里的lwip_listen()。
tcp_listen_with_backlog这个函数才是真正做了重要工作的地方。
tcp_listen_with_backlog这个函数主要是将pcb的状态设置为LISTEN,并将其挂在lwip里的tcp_listen_pcbs这个链表里。

accept()过程,首先Server端TCP层接到了Client来的TCP segment。
tcp_listen_input()中,产生了一个新pcb,这个pcb处在SYN_RCVD状态。这个pcb被加入了active pcbs链表。
tcp_process()中,新的newconn产生了,并且它放在负责listen的pcb的conn的acceptmbox里。那么谁来取走这个newconn呢?就是lwip_accept()函数。
netconn_accept取出了client端连接server后server为这一对peer生成的newconn。
lwip_accept()函数返回了一个new socket,这个socket从listen socket而来,是server专为listen到的client准备的一个socket,可以认为是为这一对通路单独服务的server端socket。

recv()过程,accept()生成了一个新的socket,作为server端socket,用于某一个特定client和server通信。
用户就要使用这个新socket来接收client端数据了。接收的方法是recv()函数,即lwip_recv()。

总结一下,listen socket listen到的client的连接请求后,会在server端开辟一个新的pcb、新的conn和新的socket。
当有一个tcp_input()来到后,根据tcp segment的ip address和port,找到pcb,从pcb找到conn,放到conn的recvmbox上;
另一方面,当用户通过socket调用recv()函数时,recv()函数通过socket找到conn,并到conn的recvmbox上取tcp segment。

int connect(int s, const struct sockaddr *name, socklen_t namelen)

该函数功能与bind函数相对应:将套接字与远端目的地址信息进行绑定。
该函数本质上是对Sequential API函数netconn_connect的封装,
作为客户端程序,通常需要使用该函数来绑定服务器的地址信息。
对于TCP连接,调用这个函数会导致客户端与服务器之间发生连接握手过程,并最终建立一条稳定的连接;
对于UDP连接,该函数调用不会有任何数据包被发送,只是在连接结构中记录下服务器的地址信息。
当调用成功时,函数返回0;否则返回-1。

int send(int s, const void *dataptr, size_t size, int flags);
int sendto(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);

函数sendto主要在UDP连接中使用,作用是向另一端发送UDP报文。
该函数本质上是对Sequential API函数netconn_send的封装,参数data和size分别指出了待发送数据的起始地址和长度;flags指明数据发送时的特殊处理,例如带外数据、紧急数据等,通常设置为0;参数to和tolen分别指明了目的地址信息及信息的长度,地址信息包含了目的IP地址和目的端口号。调用成功后,函数返回成功发送的字节数,出错则返回-1。

另一个函数send主要用于在一条已建立的连接上发送数据,因此不需要在参数中包含目的地址信息。
该函数即可用于TCP程序,也可用于UDP程序,
其本质是对Sequential API函数netconn_write和netconn_send的封装。
调用成功后,函数返回成功发送的字节数,出错则返回-1。

int recv(int s, void *mem, size_t len, int flags);
int recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

函数recvfrom用来从一个套接字中接收数据,该函数通常用在UDP程序中。
该函数本质上是对Sequential API函数netconn_recv的封装,其参数与函数sendto的参数完全相似,数据发送方的地址信息会被填写到from中,fromlen指明了缓存from的长度;mem和len分别记录了接收数据的缓存起始地址和缓存长度,flags指明用户控制接收的方式,通常设置为0。
调用成功后,函数返回接收到的数据长度,否则返回-1。若返回值为0,则表示连接被对方断开,或者连接出现异常,此时应用程序应该对这种异常进行处理,常见的方式是直接关闭socket。

函数recv是基于recvfrom函数来实现的,其参数的意义与recvfrom也相同。

int close(int s); 

函数close作用是关闭套接字,该函数执行后,对应的套接字描述符不再有效,与描述符对应的内核结构lwip_socket也将被全部复位。
该函数本质上是对Sequential API函数netconn_delete的封装,
对于TCP连接来说,该函数将导致断开握手过程的发生。
若调用成功,该函数返回0;否则返回-1。

++++++++++++++++++++++++++++++++++++++++++
在freertos下进行网络编程,最关键的一步,就是任务进程的角色划分。

版权声明:本文为CSDN博主「Huskar_Liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42418557/article/details/122688650

通过在cubemx中配置ethernet和lwip,可以方便的使用网络功能。

首先是要配置ethernet。
++++++++++++++++++++++++++++
ethernet MAC----配置MAC地址。
PHY addr----配置PHY的个数。这里设置为1。
Rx Mode----配置为interrupt Mode。
Tx Mode----配置为Hardware Checksum。

control register ---- 地址设置为0x00。
status register ---- 地址设置为0x01。
special control / status register offset ----地址偏移设置为0x10。

PHY reset mask----设置为0x8000,
Select Loopback mode mask ----设置为0x4000,
set full deplex mode at 100Mbps mask----设置为0x2100,
set half deplex mode at 100Mbps mask----设置为0x2000,
set full deplex mode at 10Mbps mask----设置为0x0100,
set half deplex mode at 10Mbps mask----设置为0x0000,
enable AN mask----设置为0x1000,
restart AN mask----设置为0x0200,
select powerdown mode mask----设置为0x0800,
Isolate PHY from MII mask----设置为0x0400,
AN complete mask----设置为0x0020,
valid link established mask----设置为0x0004,
Jabber condition mask----设置为0x0002,

PHY speed mask ----设置为0x0004,
PHY duplex mask----设置为0x0010,

+++++++++++++++++++++++++++++
根据标准,SMI访问的PHY的register为0~31。data为16bits。
其中0~15为固定用途。
其中,
0x00----CR,
0x01----SR,
0x02,0x03----PHY Identifier.
0x04----AN advertisement,
0x05----ANLP base page ability,
0x06----AN expansion,

0x07----AN next page,
0x08----ANLP received next page,
0x09----Master Slave Control Register,
0x0A----Master Slave Status Register,

0x0F----extended status

从0x10到0x1F,是留给vendor的地址空间。

++++++++++++++++++++++++++++++++++++
来看看CR。
bit15----Reset,1为PHY复位,0为常规操作。
bit14----Loopback,1为enable loopback,0为disable loopback。

bit6----speed selection (MSB),
bit13----speed selection(LSB),
SS的值,
11----Reserved
10----1000Mbps
01----100Mbps
00----10Mbps

bit12----AN Enable, 1为enable AN,

bit11----power down,1为powerdown,0为常规操作。
bit10----isolate PHY from MII,1为isolate,0为normal。
bit9----restart AN,1为restart,0为normal,
bit8----duplex mode,1为full, 0 为 half

bit7----collision test,1为enable COL,0为disable COL。
bit6~bit0----Reserved。
++++++++++++++++++++++++++++++++++++++
来看看SR。
bit15----100BASE-T4 ability
bit14----100BASE-X Full Duplex ability
bit13----100BASE-X Half Duplex ability
bit12----10Mbps Full Duplex ability
bit11----10Mbps Half Duplex ability
bit10----100BASE-T2 Full Duplex ability
bit9----100BASE-T2 Half Duplex ability
bit8----extended status ability
bit6----MF preamble surpression
bit5----AN complete
bit4----remote fault
bit3----AN ability
bit2----link status
bit1----jabber condition detect
bit0----extended register ability

++++++++++++++++++++++++++++++++++++++++
再来配置LWIP。
首先是general setting.
DHCP----设置为enable。
ICMP----设置为enable。
IGMP----设置为disable。
DNS----设置为disable。
UDP----设置为enable。
MEMP_NUM_UDP_PCB----设置为4。表示静态分配4个UDP的PCB。
TCP ----设置为enable。
MEMP_NUM_TCP_PCB----设置为5。表示静态分配5个TCP的PCB。

然后是key option。
SYS----设置为OS used。
TIMERS----设置为sys_timeout。即systick。
MEMORY_PROTECT----设置为enable。
HEAP SIZE----设置为16384bytes。
MEMP_NUM_PBUF----设置为16。表示静态分配16个PBUF。
MEMP_NUM_RAW_PCB----设置为4.表示静态分配4个RAW_PCB。
MEMP_NUM_TCP_PCB_LISTEN----设置为8.表示静态分配8个listentcp。
MEMP_NUM_TCP_SEG_QUEUED----设置为16.表示静态分配16个segment。
MEMP_NUM_LOCALHOSTLIST----设置为1.表示静态分配1个localhostlist的条目。因为STM32只有一个MAC,所以只会有一个网口。

PBUF_POOL_SIZE----设置为16.表示静态分配16个PBUF。
PBUF_POOL_BUFSIZE----设置为592bytes,表示每个PBUF的大小。
ARP----设置为enable。

TCP_TTL----设置为255nodes.
TCP_RECEIVE_WINDOW----设置为2144bytes。
TCP_QUEUE_OOSEQ----设置为enable。
TCP_SACK_OUT----设置为disable。
TCP_MAX_SEG_SIZE----设置为536bytes。
TCP_SEND_BUFF----设置为1072bytes。
TCP_SEND_QUEUE_LEN----设置为9,表示允许最多9个PBUF作为TCP的发送队列。

NETIF_STATUS_CALLBACK----设置为disable。
NETIF_EXTEND_STATUS_CALLBACK----设置为disable。
NETIF_LINK_CALLBACK----设置为enable。当link状态发生变化时,会调用这个callback。

NETIF_LOOPBACK----设置为disable。

TCPIP_THREAD_NAME----设置TASK的名称。“tcpip_thread”
TCPIP_THREAD_STACKSIZE----设置为1024bytes。
TCPIP_THREAD_PRIO----设置为3。
TCPIP_MBOX_SIZE----设置为6。

DEFAULT_THREAD_NAME----设置TASK的名称。“lwip_thread”
DEFAULT_THREAD_STACKSIZE----设置为1024bytes。
DEFAULT_THREAD_PRIO----设置为3。
DEFAULT_RAW_RECVMBOX_SIZE----设置为0.
DEFAULT_TCP_RECVMBOX_SIZE----设置为6.
DEFAULT_ACCEPTMBOX_SIZE----设置为6.

NETCONN_API----设置为enable。

SOCKET_API----设置为enable。
SOCKET_COMPAT_BSD----设置为1.
SOCKET_OFFSET----设置为0。
SOCKET_SELECT_FUNC----设置为enable。
SOCKET_POLL_FUNC----设置为enable。

再来看看PPP,设置为disable。
IPV6,设置为disable。
HTTPD,设置为disable。
SNMP,设置为disable。
SNTP,设置为disable。
SMTP,设置为disable。

再来看看debug。
DEBUG_LEVEL,设置为all。

checksum_by_hardware,设置为enable。

statistic,设置为disable。

perf,设置为disable。
sanity check,设置为disable。

MDNS,设置为disable。
TFTP,设置为disable。

++++++++++++++++++++++++++++++++++++++
在使用了Freertos时,通常使用socket API进行应用编程。
这些API,在sockets.c文件中。

Socket API 是基于 NETCONN API 之上来实现的,系统最多提供
MEMP_NUM_NETCONN
个 netconn 连接结构,LwIP 定义了一个 lwip_sock 类型的 sockets数组,通过socket index就可以直接索引并且访问这个结构体了,

使用socket时,首先会调用
socket()
函数创建一个socket。在lwip中实际调用的就是lwip_socket()函数。lwip_socket()接下来通过alloc_socket()分配了socket。从sockets[]中找到一个空闲的socket,并返回index。这是一个全局变量,存有系统所有的socket,

static struct lwip_socket sockets[NUM_SOCKETS];

用户调用bind()时,实际调用的是lwip_bind(),lwip_bind()需要由用户传入待绑定的ip地址和端口号。用IP和PORT,填充到socket中去。
如果用户在调用bind()时,想直接使用本机地址作为绑定的ip地址,可以直接在这个参数传一个0。这样在bind()时不设置pcb里的ip地址,而在TCP层发包时才通过调用ip层函数来设置pcb的ip地址。

用户使用socket,调用listen()时,实际调用的是lwip里的lwip_listen()。
tcp_listen_with_backlog这个函数才是真正做了重要工作的地方。
tcp_listen_with_backlog这个函数主要是将pcb的状态设置为LISTEN,并将其挂在lwip里的tcp_listen_pcbs这个链表里。

accept()过程,首先Server端TCP层接到了Client来的TCP segment。
tcp_listen_input()中,产生了一个新pcb,这个pcb处在SYN_RCVD状态。这个pcb被加入了active pcbs链表。
tcp_process()中,新的newconn产生了,并且它放在负责listen的pcb的conn的acceptmbox里。那么谁来取走这个newconn呢?就是lwip_accept()函数。
netconn_accept取出了client端连接server后server为这一对peer生成的newconn。
lwip_accept()函数返回了一个new socket,这个socket从listen socket而来,是server专为listen到的client准备的一个socket,可以认为是为这一对通路单独服务的server端socket。

recv()过程,accept()生成了一个新的socket,作为server端socket,用于某一个特定client和server通信。
用户就要使用这个新socket来接收client端数据了。接收的方法是recv()函数,即lwip_recv()。

总结一下,listen socket listen到的client的连接请求后,会在server端开辟一个新的pcb、新的conn和新的socket。
当有一个tcp_input()来到后,根据tcp segment的ip address和port,找到pcb,从pcb找到conn,放到conn的recvmbox上;
另一方面,当用户通过socket调用recv()函数时,recv()函数通过socket找到conn,并到conn的recvmbox上取tcp segment。

int connect(int s, const struct sockaddr *name, socklen_t namelen)

该函数功能与bind函数相对应:将套接字与远端目的地址信息进行绑定。
该函数本质上是对Sequential API函数netconn_connect的封装,
作为客户端程序,通常需要使用该函数来绑定服务器的地址信息。
对于TCP连接,调用这个函数会导致客户端与服务器之间发生连接握手过程,并最终建立一条稳定的连接;
对于UDP连接,该函数调用不会有任何数据包被发送,只是在连接结构中记录下服务器的地址信息。
当调用成功时,函数返回0;否则返回-1。

int send(int s, const void *dataptr, size_t size, int flags);
int sendto(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);

函数sendto主要在UDP连接中使用,作用是向另一端发送UDP报文。
该函数本质上是对Sequential API函数netconn_send的封装,参数data和size分别指出了待发送数据的起始地址和长度;flags指明数据发送时的特殊处理,例如带外数据、紧急数据等,通常设置为0;参数to和tolen分别指明了目的地址信息及信息的长度,地址信息包含了目的IP地址和目的端口号。调用成功后,函数返回成功发送的字节数,出错则返回-1。

另一个函数send主要用于在一条已建立的连接上发送数据,因此不需要在参数中包含目的地址信息。
该函数即可用于TCP程序,也可用于UDP程序,
其本质是对Sequential API函数netconn_write和netconn_send的封装。
调用成功后,函数返回成功发送的字节数,出错则返回-1。

int recv(int s, void *mem, size_t len, int flags);
int recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

函数recvfrom用来从一个套接字中接收数据,该函数通常用在UDP程序中。
该函数本质上是对Sequential API函数netconn_recv的封装,其参数与函数sendto的参数完全相似,数据发送方的地址信息会被填写到from中,fromlen指明了缓存from的长度;mem和len分别记录了接收数据的缓存起始地址和缓存长度,flags指明用户控制接收的方式,通常设置为0。
调用成功后,函数返回接收到的数据长度,否则返回-1。若返回值为0,则表示连接被对方断开,或者连接出现异常,此时应用程序应该对这种异常进行处理,常见的方式是直接关闭socket。

函数recv是基于recvfrom函数来实现的,其参数的意义与recvfrom也相同。

int close(int s); 

函数close作用是关闭套接字,该函数执行后,对应的套接字描述符不再有效,与描述符对应的内核结构lwip_socket也将被全部复位。
该函数本质上是对Sequential API函数netconn_delete的封装,
对于TCP连接来说,该函数将导致断开握手过程的发生。
若调用成功,该函数返回0;否则返回-1。

++++++++++++++++++++++++++++++++++++++++++
在freertos下进行网络编程,最关键的一步,就是任务进程的角色划分。

版权声明:本文为CSDN博主「Huskar_Liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42418557/article/details/122688650

生成海报
点赞 0

Huskar_Liu

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

pwm电机调速的原理介绍与代码实现

1、pwm实现调速的原理与介绍 PWM(Pulse Width Modulation)脉冲宽度调制。 1)占空比 pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。 通过调节PWM的占空比就能调节IO口上电压的持续

【STM32Cube笔记】2-STM32Cube安装教程

【STM32Cube笔记】系列文章目录 1-基于STM32的VSCode入门级教程前言 2-STM32Cube安装教程 3-STM32CubeIDE汉化 4-STM32Cube配置时钟设置 5-跑马灯引脚配置 6-Cortex-M7内核基本配