From 988251e5aa68e9b029c16b9813c918111fda3494 Mon Sep 17 00:00:00 2001 From: xu <1012118331@qq.com> Date: Wed, 23 Sep 2026 10:57:43 +0800 Subject: [PATCH] =?UTF-8?q?UBoot=E9=87=8C=E5=88=9D=E5=A7=8B=E5=8C=96it6602?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 3 +- mpp/sample/it66021/src/Main.c | 75 +++- mpp/sample/it66021/src/Utility.c | 2 +- mpp/sample/it66021/src/hi_i2c_ops.c | 3 +- mpp/sample/it66021/src/it6602.c | 56 +-- mpp/sample/it66021/src/it66021 | Bin 71592 -> 71940 bytes mpp/sample/lc6600_uart/ctrl_pnl_yt.c | 421 +++++++++++++++++++++ mpp/sample/uart2net_mesh/ctrl_pnl_suzhou.c | 404 ++++++++++++++++++++ 8 files changed, 932 insertions(+), 32 deletions(-) create mode 100644 mpp/sample/lc6600_uart/ctrl_pnl_yt.c create mode 100644 mpp/sample/uart2net_mesh/ctrl_pnl_suzhou.c diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e976182..29cd9e9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,8 @@ { "permissions": { "allow": [ - "Read(//c/Windows/Fonts//**)" + "Read(//c/Windows/Fonts//**)", + "Read(//z/work/**)" ], "deny": [ "Edit", diff --git a/mpp/sample/it66021/src/Main.c b/mpp/sample/it66021/src/Main.c index afdf27b..74b8800 100644 --- a/mpp/sample/it66021/src/Main.c +++ b/mpp/sample/it66021/src/Main.c @@ -19,6 +19,13 @@ #include "it6602.h" #include "it6602_reg.h" +#include +#include +#include +#include +#include + + char Firmware_date[] = __DATE__; char Firmware_time[] = __TIME__; @@ -52,6 +59,66 @@ void InitMessage() } +static void dump_regs(unsigned long addr, int n) +{ + unsigned long page = addr & ~0xfffUL; + int fd = open("/dev/mem", O_RDWR | O_SYNC); + void *m; + int i; + + if (fd < 0) { perror("open /dev/mem"); return; } + m = mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page); + if (m == MAP_FAILED) { perror("mmap"); close(fd); return; } + + for (i = 0; i < n; i++) + printf(" %08lx: %08x\n", addr + i * 4, + *(volatile unsigned int *)((char *)m + (addr - page) + i * 4)); + + munmap(m, 0x10000); + close(fd); +} + +static void it66021_reset_gpio(void) +{ + unsigned long base = 0x11095000; /* PL061 gpio_chip5 */ + int fd = open("/dev/mem", O_RDWR | O_SYNC); + void *m; + + if (fd < 0) { perror("open"); return; } + m = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base); + if (m == MAP_FAILED) { perror("mmap"); close(fd); return; } + + printf("GPIO5 before: DATA=0x%02x DIR=0x%02x AFSEL=0x%02x DEN=0x%02x\n", + *(volatile unsigned char *)((char *)m + 0x008), + *(volatile unsigned char *)((char *)m + 0x400), + *(volatile unsigned char *)((char *)m + 0x420), + *(volatile unsigned char *)((char *)m + 0x51c)); + + /* 释放复位(bit1):输出 + 数字使能 + 拉高 */ + *(volatile unsigned char *)((char *)m + 0x400) |= 0x02; /* DIR */ + *(volatile unsigned char *)((char *)m + 0x51c) |= 0x02; /* DEN */ + *(volatile unsigned char *)((char *)m + 0x008) = 0x02; /* DATA(PL061 掩码写,bit1) */ + + usleep(10000); + printf("GPIO5 after : DATA=0x%02x DIR=0x%02x\n", + *(volatile unsigned char *)((char *)m + 0x008), + *(volatile unsigned char *)((char *)m + 0x400)); +} + + +void DumpI2cRegs(const char *tag) +{ + printf("===== I2C dump: %s =====\n", tag); + printf("-- I2C1 controller 0x11061000 --\n"); + dump_regs(0x11061000, 64); + printf("-- I2C0 controller 0x11060000 --\n"); + dump_regs(0x11060000, 64); + printf("-- CRG 0x11014280 (I2C0) --\n"); + dump_regs(0x11014280, 2); + printf("-- CRG 0x11014288 (I2C1) --\n"); + dump_regs(0x11014288, 2); + printf("=============================\n"); +} @@ -64,14 +131,19 @@ void InitMessage() void main( void ) { - USHORT loopinterval; + USHORT loopinterval = 0; InitMessage(); open_i2c_fd(); + it66021_reset_gpio(); + DumpI2cRegs("before IT6602_fsm_init"); /* ← 加这里,最重要 */ + delay1ms(1000); //for power sequence IT6602_fsm_init(); + DumpI2cRegs("after IT6602_fsm_init"); /* ← 再加这里 */ + while(1) { loopinterval += getloopTicCount(); @@ -85,3 +157,4 @@ void main( void ) } + diff --git a/mpp/sample/it66021/src/Utility.c b/mpp/sample/it66021/src/Utility.c index c20d139..8e354a1 100644 --- a/mpp/sample/it66021/src/Utility.c +++ b/mpp/sample/it66021/src/Utility.c @@ -23,7 +23,7 @@ USHORT getloopTicCount() { usleep(5000); - return 1; + return 5; } diff --git a/mpp/sample/it66021/src/hi_i2c_ops.c b/mpp/sample/it66021/src/hi_i2c_ops.c index d191b53..0bda03a 100644 --- a/mpp/sample/it66021/src/hi_i2c_ops.c +++ b/mpp/sample/it66021/src/hi_i2c_ops.c @@ -59,7 +59,8 @@ static int fd; int open_i2c_fd() { int ret; - fd = open("/dev/i2c-1", O_RDWR); + //fd = open("/dev/i2c-1", O_RDWR); + fd = open("/dev/i2c-0", O_RDWR); if (fd<0) { printf("Open i2c dev error!\n"); diff --git a/mpp/sample/it66021/src/it6602.c b/mpp/sample/it66021/src/it6602.c index f08a041..bd47e1a 100644 --- a/mpp/sample/it66021/src/it6602.c +++ b/mpp/sample/it66021/src/it6602.c @@ -450,17 +450,17 @@ struct IT6602_REG_INI IT6602_HDMI_INIT_TABLE[] = { {REG_RX_010, 0xFF, 0x17}, //[4]Auto Video Reset [2]Int Reset [1]Audio Reset [0]Video Reset - {REG_RX_011, 0xFF, 0x1F}, //Port 0¡G[4]EQ Reset [3]CLKD5 Reset [2]CDR Reset [1]HDCP Reset [0]All logic Reset - {REG_RX_018, 0xFF, 0x1F}, //Port 1¡G[4]EQ Reset [3]CLKD5 Reset [2]CDR Reset [1]HDCP Reset [0]All logic Reset + {REG_RX_011, 0xFF, 0x1F}, //Port 0[4]EQ Reset [3]CLKD5 Reset [2]CDR Reset [1]HDCP Reset [0]All logic Reset + {REG_RX_018, 0xFF, 0x1F}, //Port 1[4]EQ Reset [3]CLKD5 Reset [2]CDR Reset [1]HDCP Reset [0]All logic Reset - {REG_RX_012, 0xFF, 0xF8}, //Port 0¡G[7:3] MHL Logic reset + {REG_RX_012, 0xFF, 0xF8}, //Port 0[7:3] MHL Logic reset {REG_RX_010, 0xFF, 0x10}, //[4]Auto Video Reset [2]Int Reset [1]Audio Reset [0]Video Reset - {REG_RX_011, 0xFF, 0xA0}, //Port 0¡G[7] Enable Auto Reset when Clock is not stable [5]Enable Auto Reset - {REG_RX_018, 0xFF, 0xA0}, //Port 1¡G[7] Enable Auto Reset when Clock is not stable [5]Enable Auto Reset + {REG_RX_011, 0xFF, 0xA0}, //Port 0[7] Enable Auto Reset when Clock is not stable [5]Enable Auto Reset + {REG_RX_018, 0xFF, 0xA0}, //Port 1[7] Enable Auto Reset when Clock is not stable [5]Enable Auto Reset - {REG_RX_012, 0xFF, 0x00}, //Port 0¡G[7:3] MHL Logic reset + {REG_RX_012, 0xFF, 0x00}, //Port 0[7:3] MHL Logic reset {REG_RX_00F, 0x03, 0x01}, //change bank 1 //2013-0430 Andrew suggestion {REG_RX_1B0, 0x03, 0x01}, // MHL Port Set HPD = 0 at Power On initial state @@ -473,8 +473,8 @@ struct IT6602_REG_INI IT6602_HDMI_INIT_TABLE[] = { {REG_RX_00F, 0x03, 0x00}, //change bank 0 //2013-0430 Andrew suggestion - {REG_RX_017, 0xC0, 0x80}, //Port 0¡G[7:6] = 10 invert Port 0 input HCLK , CLKD5I //2013-0430 Andrew suggestion - {REG_RX_01E, 0xC0, 0x00}, //Port 1¡G[7:6] = 00 invert Port 1 input TMDS , CLKD5I //2013-0430 Andrew suggestion + {REG_RX_017, 0xC0, 0x80}, //Port 0[7:6] = 10 invert Port 0 input HCLK , CLKD5I //2013-0430 Andrew suggestion + {REG_RX_01E, 0xC0, 0x00}, //Port 1[7:6] = 00 invert Port 1 input TMDS , CLKD5I //2013-0430 Andrew suggestion #ifdef Enable_IT6602_CEC {REG_RX_00E, 0xFF, 0xFF}, //for enable CEC Clock @@ -483,10 +483,10 @@ struct IT6602_REG_INI IT6602_HDMI_INIT_TABLE[] = { // {0xFE, 0x80, 0x80}, //BUS10B for FPGA - {REG_RX_016, 0x08, 0x08}, //Port 0¡G[3]1: Enable CLKD5 auto power down - {REG_RX_01D, 0x08, 0x08}, //Port 1¡G[3]1: Enable CLKD5 auto power down + {REG_RX_016, 0x08, 0x08}, //Port 0[3]1: Enable CLKD5 auto power down + {REG_RX_01D, 0x08, 0x08}, //Port 1[3]1: Enable CLKD5 auto power down -// {0x20, 0x01, 0x30}, //Port 0¡GAFE control +// {0x20, 0x01, 0x30}, //Port 0AFE control {REG_RX_02B, 0xFF, 0x07}, //FixTek3D // {REG_RX_031, 0xFF, 0x2C}, //[7:4]Enable repeater function [3:0] SCL hold time count & Update Ri sel @@ -679,7 +679,7 @@ struct IT6602_REG_INI IT6602_HDMI_INIT_TABLE[] = { #if defined(_IT66023_) //FIX_ID_016 xxxxx Support Dual Pixel Mode for IT66023 Only - {REG_RX_08C, 0x09, 0x09}, // Reg8C[0] = 1 // SPOutMode¡G// for enable IO Mapping for Signal Pixel mode + {REG_RX_08C, 0x09, 0x09}, // Reg8C[0] = 1 // SPOutMode// for enable IO Mapping for Signal Pixel mode //FIX_ID_016 xxxxx #endif @@ -711,14 +711,14 @@ static struct IT6602_REG_INI IT6602_MHL_INIT_TABLE[] = { {MHL_RX_53, 0xFF, 0x80}, //[7]=1 disable FW mode {MHL_RX_32, 0xFF, 0x0C}, //CBUS arbitration Low time - {MHL_RX_81, 0xFF, MHLVersion}, //DCAP00¡GMHL_VER_MAJOR and MINOR - {MHL_RX_82, 0xFF, (PLIM<<5)+(POW<<4)+DEV_TYPE_SINK}, //DCAP02¡GDEVICE TYPE - {MHL_RX_83, 0xFF, ADOPTER_ID_H}, //DCAP03¡GADOPTER_ID + {MHL_RX_81, 0xFF, MHLVersion}, //DCAP00MHL_VER_MAJOR and MINOR + {MHL_RX_82, 0xFF, (PLIM<<5)+(POW<<4)+DEV_TYPE_SINK}, //DCAP02DEVICE TYPE + {MHL_RX_83, 0xFF, ADOPTER_ID_H}, //DCAP03ADOPTER_ID {MHL_RX_84, 0xFF, ADOPTER_ID_L}, - {MHL_RX_8B, 0xFF, DEVICE_ID_H}, //DCAP0B¡GDEVICE_ID + {MHL_RX_8B, 0xFF, DEVICE_ID_H}, //DCAP0BDEVICE_ID {MHL_RX_8C, 0xFF, DEVICE_ID_L}, - {MHL_RX_28, 0x0F, 0x00}, //Port 0¡G[0] = 0 RS value of CH1 if req_setEQ=1 //2013-0430 Andrew suggestion + {MHL_RX_28, 0x0F, 0x00}, //Port 0[0] = 0 RS value of CH1 if req_setEQ=1 //2013-0430 Andrew suggestion {MHL_RX_0F, 0x20, 0x20}, //[5]Guard band swap when Pack Mode //FIX_ID_004 xxxxx //Add 100ms calibration for Cbus @@ -2092,11 +2092,11 @@ void IT66023OutputPixelModeSet(unsigned char bSignalPixelMode) { HDMIRX_VIDEO_PRINTF(("IT66023OutputPixelModeSet( ) 111111111111 Signal Pixel Mode\r\n")); // 1 . Signal Pixel Mode - // Reg0D[3] = 0 // EnPHFClk¡G0 for disable PHFCLK for signal pixel mode - // Reg8C[0] = 1 // SPOutMode¡G// for enable IO Mapping for Signal Pixel mode - // Reg8C[3] = 1 // VDIO3en¡G// for enable QA IO - // Reg8B[0] = 0 // EnDualPixel¡G0 for disable Dual Pixel mode - // Reg8B[7] = 1 // PWDDownDualData¡G// for gating dual pix data output + // Reg0D[3] = 0 // EnPHFClk0 for disable PHFCLK for signal pixel mode + // Reg8C[0] = 1 // SPOutMode// for enable IO Mapping for Signal Pixel mode + // Reg8C[3] = 1 // VDIO3en// for enable QA IO + // Reg8B[0] = 0 // EnDualPixel0 for disable Dual Pixel mode + // Reg8B[7] = 1 // PWDDownDualData// for gating dual pix data output hdmirxset(REG_RX_00D, 0x08, 0x00); hdmirxset(REG_RX_08C, 0x09, 0x09); hdmirxset(REG_RX_08B, 0x81, 0x80); @@ -2106,11 +2106,11 @@ void IT66023OutputPixelModeSet(unsigned char bSignalPixelMode) { HDMIRX_VIDEO_PRINTF(("IT66023OutputPixelModeSet( ) 00000000000000 Dual Pixel Mode\r\n")); // 2. Dual Pixel Mode - // Reg0D[3] = 1 // EnPHFClk¡G// for enable PHFCLK for Dual pixel mode - // Reg8C[0] = 0 // SPOutMode¡G0 for Disable IO Mapping for Dual Pixel mode - // Reg8C[3] = 1 // VDIO3en¡G// for enable QA IO - // Reg8B[0] = 1 // EnDualPixel¡G// for enable Dual Pixel mode - // Reg8B[7] = 0 // PWDDownDualData¡G0 for PWD UP DUAL DATA + // Reg0D[3] = 1 // EnPHFClk// for enable PHFCLK for Dual pixel mode + // Reg8C[0] = 0 // SPOutMode0 for Disable IO Mapping for Dual Pixel mode + // Reg8C[3] = 1 // VDIO3en// for enable QA IO + // Reg8B[0] = 1 // EnDualPixel// for enable Dual Pixel mode + // Reg8B[7] = 0 // PWDDownDualData0 for PWD UP DUAL DATA hdmirxset(REG_RX_00D, 0x08, 0x08); hdmirxset(REG_RX_08C, 0x09, 0x08); hdmirxset(REG_RX_08B, 0x81, 0x01); @@ -6713,7 +6713,7 @@ static void IT6602VideoOutputEnable(unsigned char bEnableOutput) //FIX_ID_016 xxxxx Support Dual Pixel Mode for IT66023 Only #if defined(_IT66023_) - hdmirxset(REG_RX_08C, 0x08, 0x00); // Reg8C[3] = 0 // VDIO3en¡G// for disable QA IO + hdmirxset(REG_RX_08C, 0x08, 0x00); // Reg8C[3] = 0 // VDIO3en// for disable QA IO #endif //FIX_ID_016 xxxxx diff --git a/mpp/sample/it66021/src/it66021 b/mpp/sample/it66021/src/it66021 index ecc49502c443e47a5f56a87337b549a9524c733f..e070a984e3461b5249c271a51f65cb03f7169aef 100644 GIT binary patch delta 16446 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 t$dowOtihe5inr)2jcIp|C;&+wylr>eMavKDH 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 +#include +#include // gettimeofday +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "uartinf.h" +#include "confile.h" +#include "udp_util.h" + +char _rx_data[1024]; + +struct lc6600_param{ + int password; + int freq; + int power; + int bw; + int ap_sta; + int ip; + int setup; +}; + +char freq1[10]; +char freq2[10]; +//char dbm1[5]; +//char dbm2[5]; + +#define RSSI_TIMEOUT_MS 3000 //超时时间 +#define RSSI_NO_SIGNAL 0x55 //无信号值 + + + +int setup_lc6600(struct lc6600_param *pparam){ + int n_read; + char *read_ok; + char str[30]; + + uart_write(ROUTE_UART, "AT+CFUN=0\r"); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT+CFUN=0"); + } + sleep(1); + //password + sprintf(str,"AT^DAPI=%d\r",pparam->password); + uart_write(ROUTE_UART, str); + printf("%s\n",str); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DAPI"); + } + sleep(1); + // 1 center 2 station + sprintf(str,"AT^DDTC=%d\r",pparam->ap_sta); + uart_write(ROUTE_UART, str); + printf("%s\n",str); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DDTC"); + } + sleep(1); + +// sprintf(str,"AT^DDTC=%d\r",pparam->ap_sta); + uart_write(ROUTE_UART, "AT^DSTC=2\r"); + printf("AT^DSTC\n"); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DDSTC"); + } + sleep(1); + + + // 2 5M 3 10M 4 15M 5 20M "25" dbm + sprintf(str,"AT^DRPS=,%d,\"%d\"\r",pparam->bw,pparam->power); + uart_write(ROUTE_UART, str); + printf("%s\n",str); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DRPS\n"); + } + sleep(1); + // 01 800M 04 1.4G 08 2.4G + sprintf(str,"AT^DAOCNDI=0%d\r",pparam->freq); + uart_write(ROUTE_UART, str); + printf("%s\n",str); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DAOCNDI\n"); + } + + sleep(1); + + + sprintf(str,"AT^NETIFCFG=2,\"192.168.2.%d\"\r",pparam->ip); + uart_write(ROUTE_UART, str); + printf("%s\n",str); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT^DAOCNDI\n"); + } + + sleep(1); + + uart_write(ROUTE_UART, "AT+CFUN=1\r"); + n_read = uart_read(ROUTE_UART, _rx_data); + read_ok = strstr(_rx_data,"OK"); + if(read_ok == NULL){ + printf("can not exce AT+CFUN=1"); + } + + + +} + +static long elapsed_ms(struct timeval *start, struct timeval *end) +{ + return (end->tv_sec - start->tv_sec) * 1000L + + (end->tv_usec - start->tv_usec) / 1000L; +} + +#if 0 + +int get_rx1_freq(char *rx1){ + + char *s1; + char *s2; + char *s3; + char *s4; + int len; + int i; + + s1=strchr(rx1,','); + s2=strchr(s1+1,','); + s3=strchr(s2+1,','); + s4=strchr(s3+1,','); + + len= s2-s1-1; + + for(i=0;i0){ + buffer_freq=strstr(_rx_data,"^DRPS:"); + memcpy(freq_str,buffer_freq+6,6); + + printf("drps : %s\n",freq_str); + freq = atoi(freq_str); + + printf("freq %d \n",freq); + buffer_param[3]=freq; + }else{ + printf("no freq\n"); + } +#endif + + //freq=atoi(argv[3]); +// buffer_param[0] = 0x55; + //buffer_param[3] = 14300; + if(lc6600.ap_sta == 1){ + uart_write(ROUTE_UART, "AT^DAPR=1\r"); + // n_read = uart_read(ROUTE_UART, _rx_data); + // printf("%s\n",_rx_data); + + + while(1){ + + gettimeofday(&tv_now, NULL); + + if (tv_rx1.tv_sec == 0 || elapsed_ms(&tv_rx1, &tv_now) > RSSI_TIMEOUT_MS) { + buffer_param[1] = RSSI_NO_SIGNAL; + printf("rx1 no signal timeout\n"); + } + + if (tv_rx2.tv_sec == 0 || elapsed_ms(&tv_rx2, &tv_now) > RSSI_TIMEOUT_MS) { + buffer_param[2] = RSSI_NO_SIGNAL; + printf("rx2 no signal timeout\n"); + } + + + n_read = uart_read(ROUTE_UART, _rx_data); + + printf("%s\n",_rx_data); + +#if 1 + if(n_read > 10){ + + //ip_str=strstr(_rx_data,argv[2]); + //ip_str=strstr(_rx_data,"192.168.1.15"); + //ip_len = strlen(ip_str); + //printf("ip_str %d \n ",ip_len); + ip_str=strstr(_rx_data,argv[2]); + ip_len = strlen(argv[2]); + if(ip_str){ + //buffer_param[0] = 0x55; + //printf("ip_str %s \n ",ip_str); + memcpy(index,ip_str+(ip_len+2),1); + //printf("index %s \n ",index); + + if(strcmp(index, "1") == 0){ + memcpy(rssi1,ip_str+(ip_len+5),3); + dbm1 = atoi(rssi1); + printf("++dbm1:%d\n",dbm1); + buffer_param[1] = dbm1; + gettimeofday(&tv_rx1, NULL); + } + + if(strcmp(index, "2") == 0){ + memcpy(rssi2,ip_str+(ip_len+5),3); + dbm2 = atoi(rssi2); + printf("++dbm2:%d\n",dbm2); + buffer_param[2] = dbm2; + gettimeofday(&tv_rx2, NULL); + } + + }else{ + printf("no signal\n"); + } + + } + else{ + printf("no \n"); + uart_write(ROUTE_UART, "AT^DAPR=1\r"); + // buffer_param[0] = 0xAA; + } + +#endif + } + } + +} diff --git a/mpp/sample/uart2net_mesh/ctrl_pnl_suzhou.c b/mpp/sample/uart2net_mesh/ctrl_pnl_suzhou.c new file mode 100644 index 0000000..d690d1f --- /dev/null +++ b/mpp/sample/uart2net_mesh/ctrl_pnl_suzhou.c @@ -0,0 +1,404 @@ +#include +#include +#include // gettimeofday +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 新增:clock_gettime / clock_nanosleep */ +#include +#include "serial.h" +#include "uartinf.h" +#include + +#define RECV_PORT 12000 // UDP接收端口 +#define SEND_PORT 12001 // 发送端口(UART转NET) + +/* ===================== 网络帧协议定义 ===================== */ +#define NET_FRAME_LEN 32 /* 收/发报文固定长度 */ +#define NET_TAIL 0xBB /* 帧尾 */ + +#define PAYLOAD_LEN 30 /* 帧长度 byte0..29 */ +#define BYTE_MODE 30 /* 接收帧 mode 字段偏移(0-4) */ +#define BYTE_TAIL 31 /* 接收帧帧尾偏移 */ + +#define CHECKSUM_END 30 /* 校验和覆盖 byte0..byte30(含) */ +#define NET_FRAME_MAX_LEN 256 /* recvfrom 缓冲大小 */ + +#define BYTE_NOSIG0 25 /* byte25 = 视频流0无信号标志(1=无信号,0=有信号) */ +#define BYTE_NOSIG1 26 /* byte26 = 视频流1无信号标志(1=无信号,0=有信号) */ +#define BYTE_RSSI1 28 /* 信号包=RSSI1;设备包=哨兵 */ +#define BYTE_RSSI2 29 /* 信号包=RSSI2;设备包=哨兵 */ +#define SIG_MARK 0x5A /* 设备包 byte28/29 哨兵值 */ +#define SIGNAL_INTERVAL_MS 100 /* 独立信号包发送间隔(ms) */ +#define FRAME_HEAD 0x11 /* 上行帧帧头(byte0),面板校验要求 */ +#define FRAME_TAIL 0x22 /* 上行帧帧尾(byte30),面板校验要求 */ +#define FRAME_HEAD_TX 0xAA /* 下行帧帧头(byte0),设备→面板 */ + + +/* ===================== 共享内存 ===================== */ +#define SHM_MSG_NAME "/video_switch" +#define SHM_MODE_IDX 0 /* switch_mode -> shm[0](即 switch_video[0]) */ + + +static int *shm_msg = NULL; +static int *buffer_param = NULL; /* 新增:/param.txt 共享内存,供信号线程读 RSSI */ + +char _rx_data[200]; + + +static int socket_descriptor; +static struct sockaddr_in address; + +bool first = false; + +int socket_descriptor_recv; +struct sockaddr_in address_recv; +pthread_attr_t attr; +pthread_t pkts_thread; + +/* 打开共享内存 /param.txt(由 lc6600_uart 进程写入 RSSI) */ +static int *param_memory_open(void) { + int fd = shm_open("/param.txt", O_RDWR, 0777); + if (fd < 0) { + fd = shm_open("/param.txt", O_RDWR | O_CREAT, 0777); + if (fd < 0) { perror("shm_open /param.txt"); return NULL; } + if (ftruncate(fd, 4096) == -1) { perror("ftruncate /param.txt"); return NULL; } + } + int *retval = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (retval == MAP_FAILED) { perror("mmap /param.txt"); return NULL; } + return retval; +} + + +static int *switch_memory_open(void) { + char buf[128]; + int fd; + sprintf(buf, SHM_MSG_NAME); + fd = shm_open(buf, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if(fd < 0) { perror("shm_open"); exit(1); } + if (ftruncate(fd, 4096) == -1) { perror("ftruncate"); exit(1); } + int *retval = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + return retval; +} + + +int init_net_recv(int port){ + + int i; + struct timeval tv; + + bzero(&address_recv,sizeof(struct sockaddr_in)); + address_recv.sin_family=AF_INET; + address_recv.sin_addr.s_addr=htonl(INADDR_ANY); + address_recv.sin_port=htons(port); + + socket_descriptor_recv=socket(AF_INET,SOCK_DGRAM,0);//IPV4 + + tv.tv_sec = 2; + tv.tv_usec = 0; + if (setsockopt(socket_descriptor_recv, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + printf("socket option SO_RCVTIMEO not support\n"); + return -1; + } + + + + if (bind(socket_descriptor_recv, (struct sockaddr *)&address_recv, sizeof(struct sockaddr_in)) < 0) + { + perror("bind"); + exit(1); + } + + return 0; +} + + + + +int udp_init(char *ip, int port){ + + + bzero(&address,sizeof(address)); + address.sin_family=AF_INET; + address.sin_addr.s_addr=inet_addr(ip); + address.sin_port=htons(port); + + + socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);//IPV4 + return 0; +} + +#if 0 +void *net_recv(void *arg) +{ + int i; + struct sockaddr_in clientAddr; + int len = sizeof(clientAddr); + int ret; + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + if (setsockopt(socket_descriptor_recv, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + printf("socket option SO_RCVTIMEO not support\n"); + //return -1; + } + + char buffer_cmd[200]; + while(1){ + i = recvfrom(socket_descriptor_recv, buffer_cmd, 200, 0, (struct sockaddr*)&clientAddr, &len); + if(i>0){ + uart_write_len(ROUTE_UART,buffer_cmd,i); + } + else{ + printf(" time_out to receive data\n"); + + } + + } + +} + +#endif +#if 1 +void *net_recv(void *arg) +{ + int i, k; + struct sockaddr_in clientAddr; + socklen_t len; + struct timeval tv; + + char buffer_cmd[NET_FRAME_MAX_LEN]; + char frame_out[NET_FRAME_MAX_LEN]; + int sum; + unsigned char checksum; + + tv.tv_sec = 5; + tv.tv_usec = 0; + if (setsockopt(socket_descriptor_recv, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + printf("socket option SO_RCVTIMEO not support\n"); + + while (1) { + len = sizeof(clientAddr); /* 每次循环重置,recvfrom 会改写 len */ + i = recvfrom(socket_descriptor_recv, buffer_cmd, NET_FRAME_MAX_LEN, 0, + (struct sockaddr*)&clientAddr, &len); + if (i <= 0) { + printf(" time_out to receive data\n"); + continue; + } + + if (i != NET_FRAME_LEN) { + printf("bad frame len=%d\n", i); + continue; + } + + /* 帧尾校验 */ + if ((unsigned char)buffer_cmd[BYTE_TAIL] != NET_TAIL) { + printf("bad tail=%02x\n", (unsigned char)buffer_cmd[BYTE_TAIL]); + continue; + } + + /* 解析 mode -> 写共享内存 */ + if (shm_msg) { + static int last_mode = -1; + int mode = (unsigned char)buffer_cmd[BYTE_MODE]; + + if (mode <= 4) { + if (mode != last_mode) { + shm_msg[SHM_MODE_IDX] = mode; //写入共享内存 viode_switch + last_mode = mode; + printf("write shm: mode=%d\n", mode); + } + } + else { + printf("filed mode=%d\n", mode); + } + } + + + /* 构造下行帧:byte0=0xAA 帧头,byte1..29 原样,byte30=0xBB 帧尾 */ + frame_out[0] = FRAME_HEAD_TX; /* [0] 帧头 0xAA */ + memcpy(frame_out + 1, buffer_cmd + 1, PAYLOAD_LEN - 1); /* [1..29] 原样 */ + frame_out[PAYLOAD_LEN] = NET_TAIL; /* [30] 帧尾 0xBB */ + + /* 校验和 = byte0..byte30 求和取低8位,写入 byte31 */ + sum = 0; + for (k = 0; k <= CHECKSUM_END; k++) + sum += (unsigned char)frame_out[k]; + checksum = sum & 0xFF; + frame_out[PAYLOAD_LEN + 1] = checksum; /* [31] 校验和 */ + + +#if 0 + /* 打印发出的包 */ + printf(" send to uart %d bytes:", NET_FRAME_LEN); + for (k = 0; k < NET_FRAME_LEN; k++) + printf(" %02x", (unsigned char)frame_out[k]); + printf("\n"); +#endif + + uart_write_len(ROUTE_UART, frame_out, NET_FRAME_LEN); //发送给串口 + } + + return NULL; +} + +#endif + +/* 独立信号包线程:与串口收包完全解耦,固定 SIGNAL_INTERVAL_MS 周期发送。 + * 用绝对时间 clock_nanosleep 对齐发送时刻,避免 sendto/printf 开销累积成漂移。 */ +void *signal_send(void *arg) +{ + char sig_frame[NET_FRAME_LEN]; + struct timespec next; + int k, sum; + + clock_gettime(CLOCK_MONOTONIC, &next); + + while (1) { + memset(sig_frame, 0, NET_FRAME_LEN); + sig_frame[0] = (char)FRAME_HEAD; /* byte0 = 0x11 */ + + /* byte25/26 = 视频流0/1无信号,byte27 = 布局开关(light_pwm_lvds 的 0x68 按键) */ + sig_frame[BYTE_NOSIG0] = (shm_msg && shm_msg[2] == 0x55) ? 1 : 0; + sig_frame[BYTE_NOSIG1] = (shm_msg && shm_msg[3] == 0x55) ? 1 : 0; + sig_frame[27] = (shm_msg && shm_msg[1]) ? 1 : 0; + printf("layout byte27 = %d\n", sig_frame[27]); + + if (buffer_param) { + sig_frame[BYTE_RSSI1] = (char)buffer_param[1]; + sig_frame[BYTE_RSSI2] = (char)buffer_param[2]; + } + sig_frame[PAYLOAD_LEN] = (char)FRAME_TAIL; /* byte30 = 0x22 */ + + sum = 0; + for (k = 0; k <= CHECKSUM_END; k++) + sum += (unsigned char)sig_frame[k]; + sig_frame[CHECKSUM_END + 1] = (char)(sum & 0xFF); + + sendto(socket_descriptor, sig_frame, NET_FRAME_LEN, 0, + (struct sockaddr *)&address, sizeof(address)); + + /* 下一个发送时刻 = 本次时刻 + 周期,与实际执行耗时无关 */ + next.tv_nsec += (long)SIGNAL_INTERVAL_MS * 1000000L; + while (next.tv_nsec >= 1000000000L) { + next.tv_sec++; + next.tv_nsec -= 1000000000L; + } + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); + } + + return NULL; +} + + +int pthread_init() +{ + int ret; + pthread_t sig_thread; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + ret = pthread_create(&pkts_thread, &attr, net_recv, NULL); + + if(ret){ + printf("create pthread1 error\n"); + pthread_attr_destroy(&attr); + return 1; + } + + ret = pthread_create(&sig_thread, &attr, signal_send, NULL); + if(ret){ + printf("create pthread2 error\n"); + pthread_attr_destroy(&attr); + return 1; + } + + pthread_attr_destroy(&attr); + + return 0; +} + + +int main (int argc, char *argv[]) +{ + int n_read; + int i; + int count=0; + + char buffer_send[100]; + int buffer_len; + + n_read = uart_init(ROUTE_UART, "/dev/ttyAMA4", 115200, 'N'); + if(n_read <=0 ) { + printf("open /dev/ttyAMA4 failed\n"); + return -1; + } + + + + // udp_init(argv[1], atoi(argv[2])); + // init_net_recv(atoi(argv[3])); + udp_init("127.0.0.1", SEND_PORT); // 发送目标:本机回环 12001 + init_net_recv(RECV_PORT); // 接收:0.0.0.0:12000(INADDR_ANY) + + + shm_msg = switch_memory_open(); + buffer_param = param_memory_open(); + + if (pthread_init()) { + printf("pthread_init failed\n"); + return -1; + } + + + + while(1){ + n_read = uart_read(ROUTE_UART, _rx_data); + if(n_read > 0 ){ + int k, sum = 0; + + /* 收包校验:完整 32 字节帧,帧头 0x11、帧尾 0x22、byte31 校验和正确,否则丢弃 */ + if (n_read < NET_FRAME_LEN) { + printf("drop bad frame: len=%d\n", n_read); + continue; + } + if ((unsigned char)_rx_data[0] != FRAME_HEAD || + (unsigned char)_rx_data[CHECKSUM_END] != FRAME_TAIL) { + printf("drop bad frame: head=%02x tail=%02x\n", + (unsigned char)_rx_data[0], (unsigned char)_rx_data[CHECKSUM_END]); + continue; + } + for (k = 0; k <= CHECKSUM_END; k++) + sum += (unsigned char)_rx_data[k]; + if ((unsigned char)_rx_data[31] != (sum & 0xFF)) { + printf("drop bad frame: checksum=%02x expect=%02x\n", + (unsigned char)_rx_data[31], (unsigned char)(sum & 0xFF)); + continue; + } + + /* byte28/29 固定哨兵值,RSSI 改由独立信号包承载 */ + _rx_data[BYTE_RSSI1] = (char)SIG_MARK; + _rx_data[BYTE_RSSI2] = (char)SIG_MARK; + + + /* 和校验:byte0..byte30 求和取低 8 位,写入 byte31 */ + sum = 0; + for (k = 0; k <= CHECKSUM_END; k++) + sum += (unsigned char)_rx_data[k]; + _rx_data[31] = (char)(sum & 0xFF); + + sendto(socket_descriptor, _rx_data, NET_FRAME_LEN, 0, (struct sockaddr *)&address, sizeof(address)); + } + + } + +}