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

📄 main.c

📁 单片机c语言程序设计100例--基于PIC+PROTEUS
💻 C
📖 第 1 页 / 共 2 页
字号:
          //ANO通道模/数转换值中的第ref个字符写入val所指向的缓冲区位置
          *val = AN0String[(BYTE)ref];
          if(AN0String[(BYTE)ref] == '\0' || AN0String[(BYTE)(ref+1)] == '\0')
           return HTTP_END_OF_VAR; else  return ++ref;
       //返回协议栈版本
       case VAR_STACK_VERSION:  //控制代码:%16
          if(ref == HTTP_START_OF_VAR)
          {
              strcpypgm2ram(VarString, VERSION);
          }
          *val = VarString[(BYTE)ref];
          if(VarString[(BYTE)ref] == '\0' || VarString[(BYTE)(ref+1)] == '\0')
             return HTTP_END_OF_VAR; else  return ++ref;
      //返回协议栈日期 
      case VAR_STACK_DATE:     //控制代码:%17
          if(ref == HTTP_START_OF_VAR)
          {   //__DATE__与__TIME__为PICC18预定义的宏
              //两个双引号用来在日期时间之前添加空格
              strcpypgm2ram(VarString, __DATE__ " " __TIME__);
          }
          *val = VarString[(BYTE)ref];
          if(VarString[(BYTE)ref] == '\0' || VarString[(BYTE)(ref+1)] == '\0')
             return HTTP_END_OF_VAR; else  return ++ref;
    }
    //前6个case将单字符(或字节)写入val所指向的返回值缓冲后,
    //跳出switch,在这里返回HTTP_END_OF_VAR
    return HTTP_END_OF_VAR;
}

//-----------------------------------------------------------------
// 控制板硬件初始化
//-----------------------------------------------------------------
static void InitializeBoard(void)
{
    OSCTUNE = 0x40;      //使能 4 x PLL
    ADCON0 = 0B10000001; //选择AN0通道,启动A/D转换模块,A/D时钟频率:Fosc/32
    ADCON1 = 0B10001110; //结果右对齐,Fosc/32, 仅AN0为模拟通道,VREF+/VREF-为VDD/VSS
    //LED指示灯端口方向及初值
    LED_PTR_TRIS = 0;  LED_PTR_IO = 0;
    LED1_TRIS = 0;     LED1_IO = 0; 
    LED2_TRIS = 0;     LED2_IO = 0;
    //继电器端口方向及初值
    RELAY_TRIS = 0; RELAY_IO = 1;//(初始时继电器断开)
    //使能PORTB端口内部上拉
    INTCON2bits.RBPU = 0;
    //配置USART
    TXSTA = 0B00100000;
    RCSTA = 0B10010000;
    SPBRG = SPBRG_VAL;
    //T0定时器中断禁止
    T0CON = 0;
    INTCONbits.GIEH = 1;
    INTCONbits.GIEL = 1;
}

//-----------------------------------------------------------------
// 初始化应用程序配置
//-----------------------------------------------------------------
static void InitAppConfig(void)
{
    BYTE c, *p;
    #if defined(STACK_USE_DHCP) || defined(STACK_USE_IP_GLEANING)
    AppConfig.Flags.bIsDHCPEnabled = TRUE;
    #else
    AppConfig.Flags.bIsDHCPEnabled = FALSE;
    #endif
    //指针p指向APP_CONFIG类型的结构变量AppConfig
    p = (BYTE*)&AppConfig;
    //设置内部计数地址为0x00,并置EEPROM为顺序读模式(EEPROM_CONTROL为0xA0)
    XEEBeginRead(EEPROM_CONTROL, 0x00);
    c = XEERead();//读取第0字节(标志识字节0x55)
    XEEEndRead(); //停止读(释放IIC总线)
    //如果有记录被保存,则从EEPROM中MPFS文件系统读取的首字节为0x55
    //当读取到0x55时表示EEPROM中保存的是有效配置数据
    if(c == 0x55)
    {   //从EEPROM第1字节开始读
        XEEBeginRead(EEPROM_CONTROL, 0x01);
        //顺序读取所有配置数据存入p指针所指向的AppConfig结构空间
        //包括4节IP地址,6字节MAC地址,4字节子网掩码,4字节网关,4字节DNS等内容
        for ( c = 0; c < sizeof(AppConfig); c++ )
            *p++ = XEERead();
        XEEEndRead();//停止读(释放IIC总线)
    }
    else SaveAppConfig(); //如果EEPROM不存在有效配置数据时则保存配置数据
}

//-----------------------------------------------------------------
// 保存应用程序配置数据(将AppConig结构变量所有字节写入EEPROM,首字节为0x55)
//-----------------------------------------------------------------
static void SaveAppConfig(void)
{
    BYTE c, *p = (BYTE*)&AppConfig;
    //设置内部计数地址为0x00
    XEEBeginWrite(EEPROM_CONTROL, 0x00);
    XEEWrite(0x55);//首字节写0x55(有效性标志字节)
    //将AppConfig结构变量所有配置字节写入EEPROM
    for ( c = 0; c < sizeof(AppConfig); c++ )
    {
        XEEWrite(*p++);
    }
    XEEEndWrite();//停止写,释放IIC总线
}

//-----------------------------------------------------------------
// 命令菜单字符串
//-----------------------------------------------------------------
ROM char menu[] =
    "\r\n\r\n\rMicrochip TCP/IP Config Application ("VERSION", " __DATE__ ")\r\n\r\n"
    "\t1: Change Board serial number.\r\n"
    "\t2: Change Board Host Name.\r\n"
    "\t3: Change default IP address.\r\n"
    "\t4: Change default gateway address.\r\n"
    "\t5: Change default subnet mask.\r\n"
    "\t6: Change default DNS server address.\r\n"
    "\t7: Enable DHCP & IP Gleaning.\r\n"
    "\t8: Disable DHCP & IP Gleaning.\r\n"
    "\t0: Save & Quit.\r\n"
    "\r\n"
    "Enter a menu choice (1-0): ";
//-----------------------------------------------------------------
// 命令菜单枚举类型定义(1~9,0)
//-----------------------------------------------------------------
typedef enum _MENU_CMD
{
    MENU_CMD_SERIAL_NUMBER  = '1',
    MENU_CMD_HOST_NAME,
    MENU_CMD_IP_ADDRESS,
    MENU_CMD_GATEWAY_ADDRESS,
    MENU_CMD_SUBNET_MASK,
    MENU_CMD_DNS_ADDRESS,
    MENU_CMD_ENABLE_AUTO_CONFIG,
    MENU_CMD_DISABLE_AUTO_CONFIG,
    MENU_CMD_QUIT  = '0',
    MENU_CMD_INVALID = MENU_CMD_DISABLE_AUTO_CONFIG + 1
} MENU_CMD;
//-----------------------------------------------------------------
// 菜单命令提示字符串
//-----------------------------------------------------------------
ROM char * const menuCommandPrompt[] =
{
    "\r\nNow running application...\r\n",
    "\r\nSerial Number (",
    "\r\nHost Name (",
    "\r\nDefault IP Address (",
    "\r\nDefault Gateway Address (",
    "\r\nDefault Subnet Mask (",
    "\r\nDefault DNS Server Address (",
    "\r\nDHCP & IP Gleaning enabled.\r\n",
    "\r\nDHCP & IP Gleaning disabled.\r\n"
};
//用户输入无效时的提示信息
//(该提示信息超长,字符串拆成两行编写,第一行反引号后不要加逗号)
ROM char InvalidInputMsg[] = 
"\r\nInvalid input received - Input ignored.\r\n"
"Press any key to continue...\r\n";
//-----------------------------------------------------------------
// 将所输入的用逗号分隔的IP地址串(形如:xxx.xxx.xxx.xxx)转换为4字节IP地址
//-----------------------------------------------------------------
BOOL StringToIPAddress(char *str, IP_ADDR *buffer)
{
    BYTE v,byteIndex = 0;
    char *temp = str;   //temp指针指向IP地址串首字符
    while( v = *str )   //逐个取得IP串中的字符,遇到'\0'时结束
    {   if ( v == '.' ) //如果是IP地址串中的分隔符"."
        {   //将分隔符"."用串结束标志替换,逐次处理后
            //原始串将变为"xxx\0xxx\0xxx\0xxx\0"
            *str++ = '\0'; 
            //将原始IP地址串中的第byteIndex个字符串转换为字符形式
            //存入buffer中的对应位置
            buffer->v[byteIndex++] = atoi(temp);
            //temp指针指向IP地址串内部分割的下一个串首字符地址
            temp = str;
        }
        //遇到非法数据时返回FALSE
        else if ( v < '0' || v > '9' ) return FALSE;
        //str指针累加,直到遇到下一个"."
        str++;
    }
    //处理IP地址串的第4部分,存入buffer结构对应位置
    buffer->v[byteIndex] = atoi(temp);
    //如果完整处理了IP地址串的4个部分(0,1,2,3)则返回TRUE(1)
    return (byteIndex == 3);
}

//-----------------------------------------------------------------
// 获取所选择的菜单项索引(1~8,0)
//-----------------------------------------------------------------
MENU_CMD GetMenuChoice(void)
{
    BYTE c;
    while(!DataRdyUART())
    {   //配置开关合上时间超过4s时,使EEPROM中的配置内容失效.
        #if defined(MPFS_USE_EEPROM)
        if(BUTTON0_IO == 0)
        {   //获取起始"嘀嗒"时间
            TICK StartTime = TickGet();
            while(BUTTON0_IO == 0)
            {   //配置开关合上时间超过4s则使EEPROM配置数据失效
                //EEPROM首字节为0x55时有效,这里写入0x00,使之失效
                if(TickGet() - StartTime > 4*TICK_SECOND)
                {   XEEBeginWrite(EEPROM_CONTROL, 0x00);
                    XEEWrite(0x00);
                    XEEEndWrite();
                    putrsUART("\r\nBUTTON0 held >= 4s. EEPROM contents erased.\r\n");
                    break;
                }
            }
        }
        #endif
    }
    //读取用户所选择的菜单项索引(1~8,0),如果为合法索引则返回索引号
    c = ReadUART();
    if ( c >= '0' && c < MENU_CMD_INVALID ) return c;
    //否则返回MENU_CMD_INVALID
    else return MENU_CMD_INVALID;
}

#define MAX_USER_RESPONSE_LEN   (20)
//-----------------------------------------------------------------
// 执行菜单命令项
//-----------------------------------------------------------------
void ExecuteMenuChoice(MENU_CMD choice)
{   //回显到(虚拟)终端的字符串
    char response[MAX_USER_RESPONSE_LEN];
    IP_ADDR tempIPValue;
    IP_ADDR *destIPValue;
    putrsUART(CRLF);
    //根据不同菜单选项,显示不同的操作提示字符串
    putrsUART(menuCommandPrompt[choice-'0']);
    switch(choice)
    {   //处理序列号修改命令
        case MENU_CMD_SERIAL_NUMBER:
           //读取原始序列号并转换为字符串,存入response,然后调用putsUART显示
           itoa(AppConfig.SerialNumber.Val, response);
           putsUART(response);
           putrsUART("): ");
           //从(虚拟)终端读取所输入的序列号
           if(ReadStringUART(response, sizeof(response)))
           {   //将序列号存入配置结构变量
               AppConfig.SerialNumber.Val = atoi(response);
               AppConfig.MyMACAddr.v[4] = AppConfig.SerialNumber.v[1];
               AppConfig.MyMACAddr.v[5] = AppConfig.SerialNumber.v[0];
           }
           break;
        //处理主机名修改命令
        case MENU_CMD_HOST_NAME:
            //首先显示原始主机名(本例默认为PICWEBSERVER)
            putsUART(AppConfig.NetBIOSName); putrsUART("): ");
            //从(虚拟)终端读取用户输入的新主机名,串长不超过NetBIOSName的长度
            ReadStringUART(response, 
              sizeof(response) > sizeof(AppConfig.NetBIOSName) 
              ? sizeof(AppConfig.NetBIOSName) : sizeof(response));
            //如果所输入新主机名非空则保存到AppConfig.NetBIOSName.
            //然后调用FormatNetBIOSName对所输入的主机名规范化(转大写+空格填充)
            if(response[0] != '\0')
            {   memcpy(AppConfig.NetBIOSName, 
                    (void*)response, sizeof(AppConfig.NetBIOSName));
                FormatNetBIOSName(AppConfig.NetBIOSName);
            }
            break;
        //---------------------------------------------------------
        // 下面是对IP,GATEWAY,MASK,DNS的手动配置,先取出原始地址保存到
        // destIPValue,再跳到ReadIPConfig从(虚拟)终端读配置地址数据
        //---------------------------------------------------------
        //手动配置IP地址
        case MENU_CMD_IP_ADDRESS:
            destIPValue = &AppConfig.MyIPAddr;  goto ReadIPConfig;
        //手动配置网关地址
        case MENU_CMD_GATEWAY_ADDRESS:
            destIPValue = &AppConfig.MyGateway; goto ReadIPConfig;
        //手动配置子网掩码
        case MENU_CMD_SUBNET_MASK:
            destIPValue = &AppConfig.MyMask;    goto ReadIPConfig;
        //手动配置DNS地址
        case MENU_CMD_DNS_ADDRESS:
            destIPValue = &AppConfig.PrimaryDNSServer;    
        //前面保存原始地址到destIPValue后跳到这里
        ReadIPConfig:
            DisplayIPValue(destIPValue);//显示原始地址串
            putrsUART("): ");
            //从(虚拟)终端读取手动输入的新的地址
            ReadStringUART(response, sizeof(response));
            //将所输入的地址数据串转换到tempIPValue,如果输入非法则显示错误信息
            if ( !StringToIPAddress(response, &tempIPValue) )
            {   putrsUART(InvalidInputMsg);
                while(!DataRdyUART());
                ReadUART();
            }
            //所输入的地址数据合法则将新地址保存到destIPValue
            else
            {   destIPValue->Val = tempIPValue.Val;
            }
            break;
        //使能自动配置(DHCP)(Flags.bIsDHCPEnabled定义在StackTsk.h)
        case MENU_CMD_ENABLE_AUTO_CONFIG:
            AppConfig.Flags.bIsDHCPEnabled = TRUE;    break;
        //禁止自动配置(DHCP)
        case MENU_CMD_DISABLE_AUTO_CONFIG:
            AppConfig.Flags.bIsDHCPEnabled = FALSE;   break;
        //保存并退出配置菜单    
        case MENU_CMD_QUIT:           SaveAppConfig();break;
    }
}

//-----------------------------------------------------------------
// 在(虚拟)终端显示配置菜单并执行用户输入的菜单项(直到保存/退出)
//-----------------------------------------------------------------
static void SetConfig(void)
{
    MENU_CMD choice;
    do
    {   //显示配置菜单
        putrsUART(menu);
        //等待用户输入要执行的菜单项
        choice = GetMenuChoice();
        //如果菜单项合法则执行菜单项命令
        if ( choice != MENU_CMD_INVALID )
            ExecuteMenuChoice(choice);
      //选择最后的保存/退出菜单项时退出配置操作
    } while(choice != MENU_CMD_QUIT);
}

//-----------------------------------------------------------------
// 格式化NETBIOS名称(转换为大写,名称不足15个字符时用空格填充)
//-----------------------------------------------------------------
static void FormatNetBIOSName(BYTE Name[16])
{
    BYTE i = 0;  Name[15] = '\0'; strupr(Name);
    while(i < 15)
    {   if(Name[i] == '\0')
        {   while(i < 15) Name[i++] = ' ';
            break;
        }
        i++;
    }
}

⌨️ 快捷键说明

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