早期嵌入式开发没有嵌入式操作系统的概念,直接操作裸机,在裸机上写程序,比如51单片机基本就没有操作系统的概念。通常把程序分为两部分,前台系统和后台系统。中断任务就属于前台系统,无线循环的就属于后台系统。RTOS系统:全称Real Time OS,就是实时操作系统,强调的是实时性。又分为硬实时和软实时。我们可以把要实现的功能划分为多个任务,每个任务负责实现其中一部分,每个任务都是一个简单的程序通常是一个死循环。 FreeRTOS是一个可裁剪、可剥夺型的多任务内核,而且没有任务书限制,它提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。
FreeRTOS可以直接去官网下载 http://www.freertos.org
学习
FreeRTOS有一定难度,开发时只需要学会怎么调用
FreeRTOS的API函数就可以,由C语言函数实现,对C语言还有一定的要求,需要了解指针、结构体、数据结构体中的链表等,因为
FreeRTOS内核的调用都是通过双链表进行实现的。
FreeRTOS任务:
特性:1、使用简单。2、没有使用限制。3、支持抢占。4、支持优先级。5、每个任务都拥有堆栈导致了RAM使用量增大。6、如果使用抢占的话,必须仔细的考虑重入问题。
状态:运行态、就绪态、阻塞态、挂起态。
优先级:由低到高,空闲任务--->中断任务。《FREERTOS 实时内核实用指南》这本书(资料下载地址
https://share.weiyun.com/5h6TCnn
)能够让读者理解如何实现一个任务、如何创建一个或多个任务的实例、如何使用任务函数、如何改变一个亿创建任务的优先级、如何删除任务、
如何实现周期性处理、空闲任务时运行,可以用来干什么。
WIFI UDP Clinet编程:
UDP通讯模型:server端和client端。今天主要学习客户端的编程,主要用到四个函数:socket()、sendto()、recvfrom()、close()。
功能的开发:首先需要在PC端模拟一个UDP Server,指定IP、PORT,等待Client数据,UDP Client向Server发送“I am Client”,Server收到数据后,向Client发送“I am Server”。在user文件夹里新建“udpclient.c”,在include文件夹新建“udpclient.h”。
代码实现:在udpclient.c下,添加udpclient初始化、任务。在udpclient.h下声明。
#include "esp_common.h"#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "lwip/sockets.h"#include "lwip/dns.h"#include "lwip/netdb.h"#include "udpclient.h"#define SERVERADDR "192.168.20.172"#define SERVERPORT 8000/****************************************************************************** * FunctionName : ATaskUdpclient * Description : ATaskUdpclient任务 * Parameters : none * Returns : 描述函数 第一步:判断是否获取到IP地址 第二步:创建socket 第三步:设置接收超时时间 第四步:赋值server信息 第五步:发送数据到server端 第六步:从server端接收数据*******************************************************************************/void ATaskUdpclient( void *pvParameters ){ int iVariableExample = 0; int fd = -1; int NetTimeOut = 5000; //5秒 int ret ; struct sockaddr_in ServerAddr; char udpmsg[48]; STATION_STATUS StaStatus; do { StaStatus = wifi_station_get_connect_status(); vTaskDelay(100); }while(StaStatus != STATION_GOT_IP); fd = socket(PF_INET ,SOCK_DGRAM,0); //socket(int domain ,int type, int protocol ); //domain:地址族,type:套接字类型,protocol:参数通常设置为0 if(fd == -1) { printf("gey socket fail !\n"); vTaskDelete( NULL ); //任务删除 } setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &NetTimeOut,sizeof(int)); memset(&ServerAddr,0,sizeof(ServerAddr)); ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = inet_addr(SERVERADDR); ServerAddr.sin_port = htons(SERVERPORT); ServerAddr.sin_len = sizeof(ServerAddr); /* 任务通常实现在一个死循环中。*/ for( ;; ) { sendto(fd,"i am udpclient", sizeof("i am udpclient"), \ 0,(struct sockaddr *)&ServerAddr , (socklen_t)ServerAddr.sin_len); do { ret = recvfrom(fd,udpmsg, 48, \ 0,(struct sockaddr *)&ServerAddr , (socklen_t*)&ServerAddr.sin_len); if (ret > 0) { printf ("udpServer : %s \n",udpmsg); } else { printf ("udpServer data is no\n"); } }while(ret == -1); } vTaskDelete( NULL );}/****************************************************************************** * FunctionName : Udpclient_init * Description : 初始化 * Parameters : none * Returns : none*******************************************************************************/void Udpclient_init(void){ xTaskCreate(ATaskUdpclient, "udpclient", 256, NULL, 4, NULL); }
#ifndef __UDPCLIENT_H__#define __UDPCLIENT_H__#ifdef __cplusplusextern "C" {#endifvoid ATaskUdpclient( void *pvParameters );void Udpclient_init(void);#ifdef __cplusplus}#endif#endif
win+R-->cmd-->ipconfig+回车,可以查看当前以太网配置信息。
WIFI UDP Server编程:#include "esp_common.h"#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "lwip/sockets.h"#include "lwip/dns.h"#include "lwip/netdb.h"#include "gpio.h"#include "udpserver.h"#define SERVERADDR "192.168.20.186"#define SERVERPORT 8000/****************************************************************************** * FunctionName : ATaskUdpServer * Description : ATaskUdpServer任务 * Parameters : none * Returns : 描述函数 第一步:判断是否获取到IP地址 第二步:创建socket 第三步:Serveraddr信息设置 第四步:设置接收超时时间 第五步:绑定socket 第六步:从client端接收数据 第七步:发送数据到client*******************************************************************************/void ATaskUdpServer( void *pvParameters ){ int iVariableExample = 0; int fd = -1; int fdbroad = -1; int NetTimeOut = 1000; //1秒 int ret ; struct sockaddr_in ServerAddr; struct sockaddr from; socklen_t fromlen = sizeof(struct sockaddr); char udpmsg[48]; STATION_STATUS StaStatus; struct ip_info info; //用于获取IP地址的信息 char*ipaddr = NULL; int RecvCode = 0; do { StaStatus = wifi_station_get_connect_status(); vTaskDelay(100); }while(StaStatus != STATION_GOT_IP); wifi_get_ip_info(STATION_IF,&info); ipaddr = inet_ntoa(*(struct in_addr *)&(info.ip)); printf("IP(%s)\n",ipaddr); fd = socket(PF_INET ,SOCK_DGRAM,0); //socket(int domain ,int type, int protocol ); //domain:地址族,type:套接字类型,protocol:参数通常设置为0 if(fd == -1) { printf("get socket fail !\n"); vTaskDelete( NULL ); //任务删除 return ; } setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &NetTimeOut,sizeof(int)); memset(&ServerAddr,0,sizeof(ServerAddr)); ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = INADDR_ANY; ServerAddr.sin_port = htons(SERVERPORT); ServerAddr.sin_len = sizeof(ServerAddr); if (bind(fd,(struct sockaddr *)&ServerAddr,ServerAddr.sin_len) != 0 ) { printf("bind socket fail !\n"); vTaskDelete(NULL); return ; } /* 任务通常实现在一个死循环中。*/ for( ;; ) { do { memset(udpmsg,0,48); ret = recvfrom(fd,udpmsg, 48, 0,&from, &fromlen); if (ret > 0) { printf ("udpclient : %s \n",udpmsg); } else { printf ("udpclient data is no\n"); } }while(ret == -1); sendto(fd,"i am udpserver", sizeof("i am udpserver"),0,&from, fromlen); } vTaskDelete( NULL );}/****************************************************************************** * FunctionName : UdpServer_init * Description : 初始化 * Parameters : none * Returns : none*******************************************************************************/void UdpServer_init(void){ xTaskCreate(ATaskUdpServer, "UdpServer", 256, NULL, 4, NULL);}
#ifndef __UDPSERVER_H__#define __UDPSERVER_H__#ifdef __cplusplusextern "C" {#endifvoid ATaskUdpServer( void *pvParameters );void UdpServer_init(void);#ifdef __cplusplus}#endif#endif
完成一个从client端接收数据给WiFi模块,对比数据信息,符合后输出一个端口控制一盏灯。
在server开发代码中,添加宏定义:
#define OPEN_STATUS "1101" //匹配的字符int G_led_status = 0;
添加控制代码:
/****************************************************************************** * FunctionName : led_init * Description : led任务 * Parameters : none * Returns : none*******************************************************************************/void led_init(void){ // 配置 MTDI 为 GPIO 模式 PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);}/****************************************************************************** * FunctionName : ATaskLED * Description : led任务 * Parameters : none * Returns : none*******************************************************************************/void ATaskLED( void *pvParameters ){ //printf("ATaskLED start\n"); /* 任务通常实现在一个死循环中。 */ for( ;; ) { /* 完成任务功能的代码将放在这里。 */ // printf("ATaskLED start1\n"); if (G_led_status== 1) { GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1); vTaskDelay(500); // 配置 MTDI 输出⾼低电平 GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0); vTaskDelay(100); G_led_status= 0; } vTaskDelay(10); } vTaskDelete( NULL );}
在void ATaskUdpServer( void *pvParameters ) 函数中的for循环里面改成以下代码:
for( ;; ) { do { memset(udpmsg,0,48); ret = recvfrom(fd,udpmsg, 48, 0,&from, &fromlen); if (ret > 0) { printf ("udpclient : %s \n",udpmsg); if(strncmp(udpmsg,OPEN_STATUS,4) ==0) { G_led_status= 1; } } else { printf ("udpclient data is no\n"); } }while(ret == -1);
初始化函数改成:
void UdpServer_init(void){ led_init(); xTaskCreate(ATaskLED, "LED", 256, NULL, 2, NULL); xTaskCreate(ATaskUdpServer, "UdpServer", 256, NULL, 4, NULL);}
在user_main()中,添加#include "udpserver.h",在user_init()函数中,添加UdpServer_init(); 到此,这段代码就已完成。需注意,连接的网路要在同一个网络里面,同一个服务器。
代码保存完成,回去虚拟机里面进行编译,再进行烧录,最后进行测试实现过程。