📄 main.c
字号:
//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 + -