⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 单片机c语言程序设计100例--基于PIC+PROTEUS
💻 C
📖 第 1 页 / 共 2 页
字号:
//-----------------------------------------------------------------
//  名称: 基于PIC18+RTL8019与Microchip协议栈的HTTP服务器应用
//-----------------------------------------------------------------
//  说明: 本例运行时,在IE浏览器中输入基于PIC18+RTL8019与Microchip协议栈
//        开发的HTTP服务器应用系统地址,形如 http://xxx.xxx.xxx.xxx时,
//        可通过打开的WEB页与单片机HTTP应用系统服务程序交互,实现对系统的
//        远程控制. 本例C程序编译器为: Microchip C18 v3.02及以上版本.
//
//-----------------------------------------------------------------
#define VERSION         "v3.75"     //TCP/IP协议栈版本
#define BAUD_RATE       (19200)     //波特率bps
//以下是必须包括的头文件
#include <string.h>
#include "..\Include\Compiler.h"
#include "..\Include\StackTsk.h"
#include "..\Include\Tick.h"
#include "..\Include\MAC.h"
#include "..\Include\Helpers.h"
#include "..\Include\Delay.h"
#include "..\Include\UART.h"
#include "..\Include\MPFS.h"
#include "..\Include\LCDBlocking.h"
#include "..\Include\GenericTCPClient.h"
#include "..\Include\HTTP.h"
#include "..\Include\XEEPROM.h"
#include "..\Include\DHCP.h"
#include "..\Include\DNS.h"
#include "..\Include\Announce.h"
#include "..\Include\NBNS.h"
//-----------------------------------------------------------------
#define USART_USE_BRGH_LOW
#if defined(USART_USE_BRGH_LOW)
    #define SPBRG_VAL (((INSTR_FREQ/BAUD_RATE)/16) - 1)
#else
    #define SPBRG_VAL (((INSTR_FREQ/BAUD_RATE)/4) - 1)
#endif
#if (SPBRG_VAL > 255) && !defined(__C30__)
    #error "Calculated SPBRG value is out of range for currnet CLOCK_FREQ."
#endif
//-----------------------------------------------------------------
//对于以下应用程序配置内容,主程序必须定义并用适当的值初始化
//配置包括:分别为IP地址,介质访问层地址MAC,子网掩码MASK,网关GATE等
//结构中所有符号常量均定义于StackTsk.h文件
APP_CONFIG AppConfig = 
{
 //4字节的默认IP地址
 { MY_DEFAULT_IP_ADDR_BYTE1,  MY_DEFAULT_IP_ADDR_BYTE2, 
   MY_DEFAULT_IP_ADDR_BYTE3,  MY_DEFAULT_IP_ADDR_BYTE4 
 },
 //6字节的MAC地址
 { MY_DEFAULT_MAC_BYTE1,      MY_DEFAULT_MAC_BYTE2,  MY_DEFAULT_MAC_BYTE3, 
   MY_DEFAULT_MAC_BYTE4,      MY_DEFAULT_MAC_BYTE5,  MY_DEFAULT_MAC_BYTE6
 },
 //4字节的默认子网掩网
 { MY_DEFAULT_MASK_BYTE1,     MY_DEFAULT_MASK_BYTE2, 
   MY_DEFAULT_MASK_BYTE3,     MY_DEFAULT_MASK_BYTE4
 },
 //4字节的网关地址
 { MY_DEFAULT_GATE_BYTE1,     MY_DEFAULT_GATE_BYTE2, 
   MY_DEFAULT_GATE_BYTE3,     MY_DEFAULT_GATE_BYTE4
 },
 //4字节的DNS地址
 { MY_DEFAULT_DNS_BYTE1,      MY_DEFAULT_DNS_BYTE2, 
   MY_DEFAULT_DNS_BYTE3,      MY_DEFAULT_DNS_BYTE4
 },
 //配置标志位
 {0B11111111}//使能DHCP
};
//-----------------------------------------------------------------
BYTE myDHCPBindCount = 0;
#if defined(STACK_USE_DHCP)
    extern BYTE DHCPBindCount;
#else
    #define DHCPBindCount    (0xFF)
#endif
//-----------------------------------------------------------------
//CGI命令码,本例中它们是Javascript通过GetServerFile请求的形如'0?0=LED1'
//的"URL"或称"文件名"中的"?"号前面的数字,每个数字代表一种操作命令
//本例定义的操作命令有以下三类
#define CGI_CMD_DIGOUT       (0) //命令码0,其后带参数分别控制LED开关/继电器开关
#define CGI_CMD_LCDOUT       (1) //命令码1,其后参数为待显示在LCD上的字符串
//CGI的命令码CGI_CMD_DIGOUT(0)下设了4个操作码(0~3)
//分别用于控制两只LED及继电器(电机启/停)
#define CMD_LED1             (0)
#define CMD_LED2             (1)
#define CMD_RELAY_ON         (2)
#define CMD_RELAY_OFF        (3)
//本例CGI文件中使用的动态控制码字节,动态控制码字节必须为两位,不足两位时用0填充
#define VAR_LED0             (0x00)    //LED0控制
#define VAR_LED1             (0x01)    //LED1控制
#define VAR_RELAY            (0x10)    //继电器控制
#define VAR_ANAIN_AN0        (0x02)    //模拟通道0访问
#define VAR_STACK_VERSION    (0x16)    //协议栈版本
#define VAR_STACK_DATE       (0x17)    //协议栈日期
//-----------------------------------------------------------------
// 设置熔丝配置位
//-----------------------------------------------------------------
#pragma config OSC = HS, WDT = OFF, LVP = OFF
//函数声明
static void InitAppConfig(void);
static void InitializeBoard(void);
static void ProcessIO(void);
static void DisplayIPValue(IP_ADDR *IPVal);
static void SetConfig(void);
static void FormatNetBIOSName(BYTE Name[16]);
BOOL StringToIPAddress(char *str, IP_ADDR *buffer);
#if defined(MPFS_USE_EEPROM)
static void SaveAppConfig(void);
#else
     #define SaveAppConfig()
#endif
//-----------------------------------------------------------------
// 中断函数定义
//-----------------------------------------------------------------
#pragma interruptlow HighISR
void HighISR(void)
{
    #ifdef __18CXX
    TickUpdate();
    #endif
    #if defined(STACK_USE_SLIP)
    MACISR();
    #endif
}
#if defined(__18CXX) && !defined(HI_TECH_C)
#pragma code highVector = 0x08
void HighVector (void)
{
    _asm goto HighISR _endasm
}
#pragma code
#endif

ROM char NewIP[] = "New IP Address: ";
ROM char CRLF[] = "\r\n";
//-----------------------------------------------------------------
// 主程序入口
//-----------------------------------------------------------------
void main(void)
{
    static TICK t = 0;
    InitializeBoard();           //主控制板端口及相关硬件初始化
    LCDInit(); DelayMs(100);     //初始化LCD并延时100ms
    //LCDText定义在LCDBlocking.c中(33个字符空间,实际使用2*16=32个,
    //最后一个为结束标志),初始时通过MCC18的预定义宏VERSION在该字符串中
    //存入协议栈版本及16个空格(16个空格清空LCD第二行)
    strcpypgm2ram(LCDText, "TCPStack " VERSION "  "
                           "                ");//本行字符串内有16个空格    
    LCDUpdate();                 //刷新LCD显示(两行) 
    TickInit();                  //"嘀嗒"时钟初始
    MPFSInit();                  //MPFS文件系统初始化
    //加载默认的NetBIOS主机名(PICWEBSERVER)
    memcpypgm2ram(AppConfig.NetBIOSName,(ROM void*)MY_DEFAULT_HOST_NAME, 16);
    //规范化主机名(转大写,长度不足时用空格填充)
    FormatNetBIOSName(AppConfig.NetBIOSName);
    //初始化协议栈及并对配置数据的有效性进行判断和处理
    InitAppConfig();
    //如果启动时,"配置"开关合上则初始化主控板的配置程序
    if(BUTTON0_IO == 0)  SetConfig();
    StackInit();                //协议栈初始化
    HTTPInit();                 //HTTP服务器初始化
    //使能DHCP及IP获取
    #if defined(STACK_USE_DHCP) || defined(STACK_USE_IP_GLEANING)
    if(!AppConfig.Flags.bIsDHCPEnabled)
    {
        myDHCPBindCount = 1;    //确保IP地址更新显示
        #if defined(STACK_USE_DHCP)
        DHCPDisable();
        #endif
    }
    #endif

    //完成上述初始化操作以后,进入无限循环,处理协议栈任务
    //如果应用需要执行自己的任务可在while循环的末尾完成
    //while循环使用使用的是"协作式多任务"机制,每项任务完成后返回,以便其他
    //任务能够完成他们的作业,如果某项任务需要很长的时间去完成作业时,应将
    //其分解为多个更小的任务,以便其他任务能够分配到CPU时间.
    while(1)
    {   //系统LED每秒闪烁一次
        if ( TickGetDiff(TickGet(), t) >= TICK_SECOND/2 )
        {
            t = TickGet(); LED_PTR_IO ^= 1;
        }
        //执行常规的协议栈任务,包括检查输入的包,包类型
        //并调用相当的入口程序进行处理
        StackTask();
        HTTPServer();           //HTTP服务处理程序
        #if defined(STACK_USE_ANNOUNCE)
        DiscoveryTask();
        #endif
        #if defined(STACK_USE_NBNS)
        NBNSTask();
        #endif
        ProcessIO();            //处理IO操作(本例中为A/D转换操作)
        //显示自上次复位以来更新的IP配置
        if ( DHCPBindCount != myDHCPBindCount )
        {   myDHCPBindCount = DHCPBindCount;
            putrsUART(NewIP);
            DisplayIPValue(&AppConfig.MyIPAddr);
            putrsUART(CRLF);
            #if defined(STACK_USE_ANNOUNCE)
            AnnounceIP();
            #endif
        }
    }
}

//-----------------------------------------------------------------
// 显示IP地址
//-----------------------------------------------------------------
static void DisplayIPValue(IP_ADDR *IPVal)
{
    BYTE i,j,IPDigit[5], LCDPos = 16;
    //显示形如xxx.xxx.xxx.xxx的IP地址串
    for (i = 0; i < 4; i++)
    {
        itoa(IPVal->v[i], IPDigit);    putsUART(IPDigit);
        for(j = 0; j < strlen(IPDigit); j++)
        {
            LCDText[LCDPos++] = IPDigit[j];
        }
        if (i == 3) break; //IP最后一个地址字节后不加点"."
        LCDText[LCDPos++] = '.';
        while(BusyUART());    WriteUART('.');
    }
    //LCDText字符串长不足32字节时用空格填充
    while(LCDPos < 32) LCDText[LCDPos++] = ' ';
    LCDUpdate();
    while(BusyUART());
}

static char AN0String[8];
//-----------------------------------------------------------------
// IO事件处理
//-----------------------------------------------------------------
static void ProcessIO(void)
{   //启动A/D转换模块
    ADCON0bits.GO = 1;
    //等待A/D转换完成
    while(ADCON0bits.GO);
    //将10位的A/D转换值转换为字符串
    itoa(*((WORD*)(&ADRESL)), AN0String);
}

//-----------------------------------------------------------------
// HTTP命令处理函数 void HTTPExecCmd(BYTE** argv, BYTE argc)
// argv为参数列表, argc为参数个数,该函数为回调函数,当远程结点通过WEB
// 页面执行交互操作时,HTTPSERVER将根据该参数信息调用HTTPExecCmd函数.
// argc的格式:
// 以HTTP参数:ControlPage.htm?P1=ON&P2=OFF为例,有:
//            argv[0] => ControlPage.htm
//            argv[1] => P1
//            argv[2] => ON
//            argv[3] => P2
//            argv[4] => OFF
// http.c的HTTP URL解析函数HTTPParse,以空格、/、\、?、=、&等字符为
// 参数串的分隔符
//-----------------------------------------------------------------
void HTTPExecCmd(BYTE** argv, BYTE argc)
{
    BYTE command, var;
    //对于本命名,下面的语句将从形如GetServerFile('0?0=LED1','')的
    //调用中取得'?'前面的字符并转换为数字,因为Javascript函数GetServerFile
    //将'0?0=LED1'作为文件名,通过XMLHttp对象向HTTP服务器请求'0?0=LED1'文件.
    command = argv[0][0] - '0';
    //根据数字命令(动作代码),执行相应的操作
    switch(command)
    {

      //CGI命令处理:数字输出(本例分于用于控制LED1/LED2)
      case CGI_CMD_DIGOUT:    //ACTION=0








      //CGI命令处理:处理由浏览器提交到液晶屏显示输出的字符串
      case CGI_CMD_LCDOUT:    //ACTION=1








    }
}

//-----------------------------------------------------------------
// 处理并返回客户端HTTP请求:WORD HTTPGetVar(BYTE var, WORD ref, BYTE* val)
// 参数为:var为控制变量,ref为当前回调引用,val返回值缓冲
//-----------------------------------------------------------------
WORD HTTPGetVar(BYTE var, WORD ref, BYTE* val)
{
    //用于缓冲待返回到浏览器的字符串
    static BYTE VarString[35];
    //根据不同的CGI请求变量返回不同的值
    switch(var)
    {
       //以下前2个case的返回值只有一个字符(或字节)时,
       //返回值写入*val后函数直接返回HTTP_END_OF_VAR
       //下面的case返回指示LED1,LED2的开关状态(Y/N),控制代码分别为:%00,%01
       case VAR_LED0:    *val = LED1_IO    ? 'N':'Y'; break;
       case VAR_LED1:    *val = LED2_IO    ? 'N':'Y'; break;       
       //下在的4个case返回的都是多字符(或多字节),case语句首先将返回值写入varString
       //通过多次回调HTTPGetVar逐一返回所有字符,在返回最后一个字节(或字节)后,
       //本函数才返回HTTP_END_OF_VAR
       //下面的case根据返回电机运行状态(运行/停止 Running.../STOP!)
       case VAR_RELAY: 
          //首先确定当前是否是第一次调用(HTTP_START_OF_VAR),首次调用时取得完整的
          //返回字符并存入VarString,但返回到客户端的IE浏览器时是逐个完成的.
          if(ref == HTTP_START_OF_VAR)
          {   //电机正在运转时将返回红色的"Running...",否则返回黑色的"STOP!"










          }
          //将待返回的字符串VarString中的第ref个字符写入val所指向的缓冲位置
          *val = VarString[(BYTE)ref];
          //如果待返回字符串VarString中的当前字符或下一字符为空则返回HTTP_END_OF_VAR
          //用来通知HTTP 服务器传输已完成,否则ref递增,HTTP服务器将再次调用该函数
          //处理待返回字符串中的下一字符(直到返回所有的字符)
          if(VarString[(BYTE)ref] == '\0' || VarString[(BYTE)(ref+1)] == '\0')
             return HTTP_END_OF_VAR; else  return ++ref;
       //返回模/数转换通道AN0的当前转换值
       case VAR_ANAIN_AN0:      //控制代码:%02

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -