目录
前言
一、详细步骤
1.配置UART打印调试信息
? ? ? ? 1.1.配置UART5
? ? ? ? 1.2.查看串口的引脚配置
? ? ? ? 1.3.? UART5 NVIC Settings
? ? ? ? 1.4.配置完成进行PING测试
2.写程序代码前的准备工作
? ? ? ? ?2.1.?在usart.c这个文件中重定向一下printf()这个函数的输出。
? ? ? ? ?2.2.在usart.c加上头文件”stdio.h”
? ? ? ? ?2.3.在main.h在头文件处加上”stdio.h”
? ? ? ? ? ?2.4.测试一下串口输出效果
3.在FreeRTOS任务1(Task1)中编写客户端代码,运行连接程序
? ? ? ? 3.1.代码编写
? ? ? ? 3.2.测试连接
3.在FreeRTOS任务2(Task2)中编写服务器端代码,运行连接程序
? ? ? ? 3.1.代码编写
? ? ? ? 3.2.测试连接
?
4.结束
总结
前言
????????因为工作关系需要用到STM32F207ZGTx的芯片,所以公司购买了基于STM32F207ZGT6U芯片的ST开发板NUCLEO-F207ZG。而且要求使用FeeRTOS+Lwip的环境,来进行软件开发,主要目的是通过此环境,运行一个软件下载程序,此程序用到了lwip库封装的socket套接字函数库,通过Tcp请求服务器下载文件包。因为我自己第一次正式做一个关于STM32的程序项目,之前只是在学校学习和了解一些基础,认识比较浅显,所以写下本文来加深自己的学习和了解,并且总计经验分享出来,有问题可以共同探讨。
????????本文内容:使用STM32CubeMx配置FreeRTOS和Lwip的例子在上一章已经讲过了就不重复列出来了,这里这里主要讲配置UART串口打印调试信息和使用Lwip库中socket套接字做tcp连接测试的例子和流程。
????????配置软件:STM32CubeMx 6.2.1
????????开发板:NUCLEO-F207ZG
????????配置环境:FreeRTOS+Lwip
????????编译软件:keil ?5.27
一、详细步骤
1.配置UART打印调试信息
? ? ? ? 1.1.配置UART5
????????????????配置UART5作为打印信息的串口,默认波特率115200/bps,其他也是默认配置,如果有需要其他波特率可以根据自己需求修改。
? ? ? ? 1.2.查看串口的引脚配置
????????????????可以看到选择UART5后,CubeMx软件已经将串口的引脚配置完成了,我们只需要根据引脚进行接线即可。????
? ? ? ? 1.3.? UART5 NVIC Settings
????????????????NVIC Settings这里我没有开启中断,当然可以根据自己的需要开启。
? ? ? ? 1.4.配置完成进行PING测试
????????????????串口配置已经完成,生成后续时钟配置按照上一章的进行即可,接下来写代码。在写代码之前注意ping一下自己配置的ip,保证网卡驱动配置正确。
2.写程序代码前的准备工作
? ? ? ? ?2.1.?在usart.c这个文件中重定向一下printf()这个函数的输出。
?????????????????代码:
/* USER CODE BEGIN 1 */
//这个变量是其他.c文件定义的
//extern UART_HandleTypeDef huart1;
uint8_t ch;
uint8_t ch_r;
//重写这个函数,重定向printf函数到串口,意思就是说printf直接输出到串口,其默认输出到控制台的
/*fputc*/
int fputc(int c, FILE * f)
{
ch=c;
HAL_UART_Transmit(&huart5,&ch,1,1000);//发送串口
return c;
}
//重定向scanf函数到串口 意思就是说接受串口发过来的数据,其默认是接受控制台的数据
/*fgetc*/
int fgetc(FILE * F)
{
HAL_UART_Receive(&huart5,&ch_r,1,0xffff);//接收
return ch_r;
}
/* USER CODE END 1 */
? ? ? ? ?2.2.在usart.c加上头文件”stdio.h”
? ? ? ? ?2.3.在main.h在头文件处加上”stdio.h”
????????????????????????找到main.h在头文件处加上”stdio.h”,这样其他包含main.h的源文件都能够使用printf()打印调试信息,不然你使用printf()就需要在使用的源文件中声明stdio.h。
? ? ? ? ? ?2.4.测试一下串口输出效果
? ? ? ? ? ? ? ? ? ? ? ? 重定义准备完成,测试一下串口输出效果。在freertos.c的任务1函数,StartTask1(void const * argument)的for循环中运行调试信息,下载到开发板中,运行程序,打开串口调试软件,这里我的端口为COM7,所以选择com7,波特率115200bps,打开串口可以看到输出的信息,说明整个流程是成功的。
????????????????????????代码:
void StartTask1(void const * argument)
{
/* init code for LWIP */
MX_LWIP_Init();
/* USER CODE BEGIN StartTask1 */
/* Infinite loop */
for(;;)
{
printf("这是task1 打印调试信息!\r\n");
osDelay(1000); //1s循环一次
}
/* USER CODE END StartTask1 */
}
3.在FreeRTOS任务1(Task1)中编写客户端代码,运行连接程序
? ? ? ? 3.1.代码编写
????????????????前期准备工作完成,接下来就是进行tcp编程了,先进行客户端代码的编写。
????????????????代码:
void StartTask1(void const * argument)
{
/* init code for LWIP */
MX_LWIP_Init();
/* USER CODE BEGIN StartTask1 */
/* Infinite loop */
//tcp client 连接测试
int iResult = 0;
struct sockaddr_in conn_addr; //连接地址
int conn_fd; //服务器的 socked
// int sock_conn; // 请求的 socked
// socklen_t addr_len; // 地址长度
// int err;
printf("come StartTask1\r\n");
conn_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
conn_addr.sin_family = AF_INET;
conn_addr.sin_port = htons(28884);
conn_addr.sin_addr.s_addr = inet_addr("192.168.0.64");
if( connect(conn_fd, (struct sockaddr *)&conn_addr, sizeof(struct sockaddr_in)) == -1)
{
printf("connect error\n");
}
printf("connect successful!conn_fd=%d\r\n",conn_fd);
unsigned char bTmpSendBuf[]="hello server!This is NUCLEO-F207ZGT6U connect!";
for(;;)
{
write(conn_fd,bTmpSendBuf,sizeof(bTmpSendBuf));
if(iResult>0)
{
printf("write successful Size=%d\r\n",iResult);
}
osDelay(1000);
}
/* USER CODE END StartTask1 */
}
? ? ? ? 3.2.测试连接
????????????????创建一个tcp服务器端,接收NUCLEO-F207ZG开发板客户端的连接,我这里服务器端配置的ip是”192.168.0.64”,端口28884,可以看到客户端成功连接上服务器端
3.在FreeRTOS任务2(Task2)中编写服务器端代码,运行连接程序
? ? ? ? 3.1.代码编写
????????????????代码:
/* USER CODE END Header_StartTask2 */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
char data_buffer[512];
/* Infinite loop */
struct sockaddr_in server_addr; //服务器地址
struct sockaddr_in conn_addr; //连接地址
int sock_fd; //服务器的 socked
int sock_conn; // 请求的 socked
socklen_t addr_len; // 地址长度
int err;
printf("come StartTask2\r\n");
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //建立一个新的socket连接
if(sock_fd<0)
{
printf("create sock_fd failed! sock_fd=%d\r\n",sock_fd);
}
printf("create sock_fd successful! sock_fd=%d\r\n",sock_fd);
memset(&server_addr, 0, sizeof(server_addr)); //将服务器地址清空
server_addr.sin_family = AF_INET; //地址家族
server_addr.sin_addr.s_addr =htonl(INADDR_ANY); //注意转化为网络字节序
server_addr.sin_port = htons(18885); //使用指定端口号
err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //建立绑定
if (err < 0) //如果绑定失败则关闭套接字
{
printf("bind socket failed!\r\n");
closesocket(sock_fd); //关闭套接字
}
while(1)
{
err = listen(sock_fd, 1); //监听连接请求 1:ACCEPT队列的大小
if (err < 0) //如果监听失败则关闭套接字
{
printf("listen socketFd failed!socketFd=%d\r\n",sock_fd);
closesocket(sock_fd); //关闭套接字
}
addr_len = sizeof(struct sockaddr_in); //将链接地址赋值给addr_len
sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len); //等待连接,状态赋值给sock_conn
if(sock_conn < 0) //状态小于0代表连接故障,此时关闭套接字
{
printf("accept socketFd failed!socketFd=%d\r\n",sock_fd);
closesocket(sock_fd);
}
while (1)
{
memset(data_buffer, 0, sizeof(data_buffer)); //清空接收Buff
int length = recv(sock_conn, (unsigned int *)data_buffer, 100, 0); //将收到的数据放到接收Buff 100为可以接收的数据长度
if(length > 0)
{
printf("recv buf data! size=%d\r\n",length);
send(sock_conn, data_buffer,length, 1); //回复客户端
printf("[StartTask2] Send bufSize=%d\r\n",length);
}
else
{
printf("Recv error Data!\r\n");
closesocket(sock_conn);
break;
}
osDelay(1);
}
osDelay(1);
}
/* USER CODE END StartTask2 */
}
? ? ? ? 3.2.测试连接
????????????????(一)、创建客户端,这里目的服务器ip是开发板的ip192.168.0.179,端口:18885,ping一下测试是否能够连接,如果能够ping通,说明可以连接
????????????????(二)、选择打开tcp,发送数据,可以看到串口打印了调试信息,NUCLEO-F207ZG服务器端接收到数据后完整返回给了客户端。这里表示整个流程成功。
4.结束
总结
- 通过以上测试我们就能够完整的掌握整个NUCLEO-F207ZG开发板配置FreeRTOS+LWIP然后进行tcp连接的一个完整流程了。
- 注意串口软件的波特率需要与配置串口的波特率一致。
- 在连接是需要多注意一下是否能够ping通,在运行程序连接。
|