zoukankan      html  css  js  c++  java
  • RTL8711AM

    官方文档主要修改

    为了实现log服务

    1,log_service.h 取消注释

    #ifndef LOG_SERVICE_H
    #define LOG_SERVICE_H
    
    #include "dlist.h"
    /*
     * Include user defined options first. Anything not defined in these files
     * will be set to standard values. Override anything you dont like!
     */
    #if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B)
    #include "platform_opts.h"
    #include "platform_stdlib.h"
    #endif
    
    #ifdef __ICCARM__
    //#define STRINGIFY(s) #s
    //#define SECTION(_name) _Pragma( STRINGIFY(location=_name))
    #define log_module_init(fn) 
    	SECTION(".data.log_init") __root static void* log_##fn = (void*)fn
    #elif defined(__CC_ARM)
    #define log_module_init(fn) 
    static void* log_##fn __attribute__((section(".data.log_init")))  = (void*)fn;
    #define DiagPrintf printf
    #elif defined(__GNUC__)
    #define log_module_init(fn) 
    static void* log_##fn __attribute__((section(".data.log_init")))  = (void*)fn;
    #else
    #error "not implement"
    #endif
    
    //#define log_module_init(fn) 
    		
    #define ATC_INDEX_NUM 32
    
    #ifndef SUPPORT_LOG_SERVICE
    #define SUPPORT_LOG_SERVICE	1
    #endif
    
    
    //LOG_SERVICE_BUFLEN: default, only 63 bytes could be used for keeping input
    //                                     cmd, the last byte is for string end ('').
    #ifndef LOG_SERVICE_BUFLEN
    #define LOG_SERVICE_BUFLEN     64     
    #endif
    
    #ifndef CONFIG_LOG_HISTORY
    #define CONFIG_LOG_HISTORY	0
    #if CONFIG_LOG_HISTORY
    #define LOG_HISTORY_LEN    5
    #endif
    #endif //#ifndef CONFIG_LOG_HISTORY
    
    #ifndef MAX_ARGC
    #define MAX_ARGC 12
    #endif
    
    #ifndef CONFIG_LOG_SERVICE_LOCK
    #define CONFIG_LOG_SERVICE_LOCK 0 // //to protect log_buf[], only one command processed per time
    #endif
    
    
    
    #define AT_BIT(n)           (1<<n)
    #define AT_FLAG_DUMP        AT_BIT(0)
    #define AT_FLAG_EDIT        AT_BIT(1)
    #define AT_FLAG_ADC         AT_BIT(2)
    #define AT_FLAG_GPIO        AT_BIT(3)
    #define AT_FLAG_OTA         AT_BIT(4)
    #define AT_FLAG_NFC         AT_BIT(5)
    #define AT_FLAG_OS          AT_BIT(6)
    #define AT_FLAG_LWIP        AT_BIT(7)
    #define AT_FLAG_COMMON      AT_BIT(8)
    #define AT_FLAG_WIFI        AT_BIT(9)
    #define AT_FLAG_RDP         AT_BIT(10)
    
    enum{
    	AT_DBG_OFF = 0,
    	AT_DBG_ALWAYS,
    	AT_DBG_ERROR,
    	AT_DBG_WARNING,
    	AT_DBG_INFO
    };
    
    extern unsigned char  gDbgLevel;
    extern unsigned int   gDbgFlag;
    
    #define AT_PRINTK(...)			
    		do{							
    			printf(__VA_ARGS__); 	
    			printf("
    ");			
    		}while(0)
    #define _AT_PRINTK(...)	printf(__VA_ARGS__)
    #define AT_DBG_MSG(flag, level, ...)					
    		do{														
    			if(((flag) & gDbgFlag) && (level <= gDbgLevel)){	
    				AT_PRINTK(__VA_ARGS__);							
    			}													
    		}while(0)
    #define _AT_DBG_MSG(flag, level, ...)					
    		do{														
    			if(((flag) & gDbgFlag) && (level <= gDbgLevel)){	
    				_AT_PRINTK(__VA_ARGS__);						
    			}													
    		}while(0)
    
    #ifndef SUPPORT_INTERACTIVE_MODE
    #define SUPPORT_INTERACTIVE_MODE	0
    #endif //#ifndef SUPPORT_INTERACTIVE_MODE
    
    typedef void (*log_init_t)(void);
    typedef void (*log_act_t)(void*);
    typedef struct _at_command_item_{
    	char *log_cmd;
    	log_act_t at_act;
    	struct list_head node;
    }log_item_t;
    
    void log_service_add_table(log_item_t *tbl, int len);
    int parse_param(char *buf, char **argv);
    #if CONFIG_LOG_SERVICE_LOCK
    void log_service_lock_init(void);
    void log_service_lock(void);
    u32 log_service_lock_timeout(u32 ms);
    void log_service_unlock(void);
    #endif
    
    #define C_NUM_AT_CMD			4 //"ATxx", 4 characters
    #define C_NUM_AT_CMD_DLT		1 //"=", 1 charater
    #define STR_END_OF_ATCMD_RET	"
    
    # " //each AT command response will end with this string
    #define STR_END_OF_ATDATA_RET	"
    
    > " //data transparent transmission indicator
    #endif
    

     注销代码(这个为空函数,导致AT不识别)

    #define log_module_init(fn) 
    

      在rtl_consol.c中

    /*
     *  Routines to access hardware
     *
     *  Copyright (c) 2013 Realtek Semiconductor Corp.
     *
     *  This module is a confidential and proprietary property of RealTek and
     *  possession or use of this module requires written permission of RealTek.
     */
    
    #include "rtl8195a.h"
    //#include <stdarg.h>
    #include "rtl_consol.h"
    #include "FreeRTOS.h"
    #include "task.h"
    #include <event_groups.h>
    #include "semphr.h"
    #if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1)
    #include "freertos_pmu.h"
    #endif
    #include "tcm_heap.h"
    
    // Those symbols will be defined in linker script for gcc compiler
    // If not doing this would cause extra memory cost
    #if defined (__GNUC__)
    
        extern volatile UART_LOG_CTL    UartLogCtl;
        extern volatile UART_LOG_CTL    *pUartLogCtl;
        extern u8                       *ArgvArray[MAX_ARGV];
        extern UART_LOG_BUF             UartLogBuf;
    
    #ifdef CONFIG_UART_LOG_HISTORY
        extern u8  UartLogHistoryBuf[UART_LOG_HISTORY_LEN][UART_LOG_CMD_BUFLEN];
    #endif
    
    #else
    
    MON_RAM_BSS_SECTION 
        volatile UART_LOG_CTL    UartLogCtl;
    MON_RAM_BSS_SECTION 
        volatile UART_LOG_CTL    *pUartLogCtl;
    MON_RAM_BSS_SECTION 
        u8                       *ArgvArray[MAX_ARGV];
    MON_RAM_BSS_SECTION 
        UART_LOG_BUF             UartLogBuf;
    
    #ifdef CONFIG_UART_LOG_HISTORY
    MON_RAM_BSS_SECTION
        u8  UartLogHistoryBuf[UART_LOG_HISTORY_LEN][UART_LOG_CMD_BUFLEN];
    #endif
    
    #endif
    
    #ifdef CONFIG_KERNEL
    static void (*up_sema_from_isr)(_sema *) = NULL;
    #endif
    
    
    _LONG_CALL_
    extern u8
    UartLogCmdChk(
        IN  u8  RevData,
        IN  UART_LOG_CTL    *prvUartLogCtl,
        IN  u8  EchoFlag
    );
    
    _LONG_CALL_
    extern VOID
    ArrayInitialize(
        IN  u8  *pArrayToInit,
        IN  u8  ArrayLen,
        IN  u8  InitValue
    );
    
    _LONG_CALL_
    extern VOID
    UartLogHistoryCmd(
        IN  u8  RevData,
        IN  UART_LOG_CTL    *prvUartLogCtl,
        IN  u8  EchoFlag
    );
    
    _LONG_CALL_
    extern VOID
    UartLogCmdExecute(
        IN  PUART_LOG_CTL   pUartLogCtlExe
    );
    
    
    
    //=================================================
    
    
    /* Minimum and maximum values a `signed long int' can hold.
       (Same as `int').  */
    #ifndef __LONG_MAX__
    #if defined (__alpha__) || (defined (__sparc__) && defined(__arch64__)) || defined (__sparcv9) || defined (__s390x__)
    #define __LONG_MAX__ 9223372036854775807L
    #else
    #define __LONG_MAX__ 2147483647L
    #endif /* __alpha__ || sparc64 */
    #endif
    #undef LONG_MIN
    #define LONG_MIN   (-LONG_MAX-1)
    #undef LONG_MAX
    #define LONG_MAX   __LONG_MAX__
    
    /* Maximum value an `unsigned long int' can hold.  (Minimum is 0).  */
    #undef ULONG_MAX
    #define ULONG_MAX (LONG_MAX * 2UL + 1)
    
    #ifndef __LONG_LONG_MAX__
    #define __LONG_LONG_MAX__ 9223372036854775807LL
    #endif
    
    
    
    
    //======================================================
    //<Function>:  UartLogIrqHandleRam
    //<Usage   >:  To deal with Uart-Log RX IRQ
    //<Argus    >:  VOID
    //<Return   >:  VOID
    //<Notes    >:  NA
    //======================================================
    //MON_RAM_TEXT_SECTION
    VOID
    UartLogIrqHandleRam  //接收中断函数
    (
        VOID * Data
    )
    {
        u8      UartReceiveData = 0;
        //For Test
        BOOL    PullMode = _FALSE;
    
        u32 IrqEn = DiagGetIsrEnReg();    //获取中断
    
        DiagSetIsrEnReg(0);  
    
        UartReceiveData = DiagGetChar(PullMode);  //获取数据
        if (UartReceiveData == 0) {
            goto exit;
        }
    
        //KB_ESC chk is for cmd history, it's a special case here.
        if (UartReceiveData == KB_ASCII_ESC) {
            //4 Esc detection is only valid in the first stage of boot sequence (few seconds)
            if (pUartLogCtl->ExecuteEsc != _TRUE)
            {
                pUartLogCtl->ExecuteEsc = _TRUE;
                (*pUartLogCtl).EscSTS = 0;
            }
            else
            {
                //4 the input commands are valid only when the task is ready to execute commands
                if ((pUartLogCtl->BootRdy == 1)
    #ifdef CONFIG_KERNEL
                    ||(pUartLogCtl->TaskRdy == 1)
    #endif
                )
                {
                    if ((*pUartLogCtl).EscSTS==0)
                    {
                        (*pUartLogCtl).EscSTS = 1;
                    }
                }
                else
                {
                    (*pUartLogCtl).EscSTS = 0;
                }
            }
        }
        else if ((*pUartLogCtl).EscSTS==1){
            if (UartReceiveData != KB_ASCII_LBRKT){
                (*pUartLogCtl).EscSTS = 0;
            }
            else{
                (*pUartLogCtl).EscSTS = 2;
            }
        }
    
        else{
            if ((*pUartLogCtl).EscSTS==2){
                (*pUartLogCtl).EscSTS = 0;
    #ifdef CONFIG_UART_LOG_HISTORY
                if ((UartReceiveData=='A')|| UartReceiveData=='B'){
                    UartLogHistoryCmd(UartReceiveData,(UART_LOG_CTL *)pUartLogCtl,1);
                }
    #endif
            }
            else{
                if (UartLogCmdChk(UartReceiveData,(UART_LOG_CTL *)pUartLogCtl,1)==2)
                {
                    //4 check UartLog buffer to prevent from incorrect access
                    if (pUartLogCtl->pTmpLogBuf != NULL)
                    {
                        pUartLogCtl->ExecuteCmd = _TRUE;
    #if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED
        				if (pUartLogCtl->TaskRdy && up_sema_from_isr != NULL)
        					//RtlUpSemaFromISR((_Sema *)&pUartLogCtl->Sema);				
    						up_sema_from_isr((_sema *)&pUartLogCtl->Sema);
    #endif
                    }
                    else
                    {
                        ArrayInitialize((u8 *)pUartLogCtl->pTmpLogBuf->UARTLogBuf, UART_LOG_CMD_BUFLEN, '');
                    }
                }
            }
        }
    exit:
        DiagSetIsrEnReg(IrqEn);
    
    }
    
    
    
    MON_RAM_TEXT_SECTION
    VOID
    RtlConsolInitRam(  //初始化
        IN  u32     Boot,
        IN  u32     TBLSz,
        IN  VOID    *pTBL
    )
    {
        UartLogBuf.BufCount = 0;
        ArrayInitialize(&UartLogBuf.UARTLogBuf[0],UART_LOG_CMD_BUFLEN,'');
        pUartLogCtl = &UartLogCtl;
    
        pUartLogCtl->NewIdx = 0;
        pUartLogCtl->SeeIdx = 0;
        pUartLogCtl->RevdNo = 0;
        pUartLogCtl->EscSTS = 0;
        pUartLogCtl->BootRdy = 0;
        pUartLogCtl->pTmpLogBuf = &UartLogBuf;
    #ifdef CONFIG_UART_LOG_HISTORY
        pUartLogCtl->CRSTS = 0;
        pUartLogCtl->pHistoryBuf = &UartLogHistoryBuf[0];
    #endif
        pUartLogCtl->pfINPUT = (VOID*)&DiagPrintf;
        pUartLogCtl->pCmdTbl = (PCOMMAND_TABLE) pTBL;
        pUartLogCtl->CmdTblSz = TBLSz;
    #ifdef CONFIG_KERNEL
        pUartLogCtl->TaskRdy = 0;
    #endif
        //executing boot sequence
        if (Boot == ROM_STAGE)
        {
            pUartLogCtl->ExecuteCmd = _FALSE;
            pUartLogCtl->ExecuteEsc = _FALSE;
        }
        else
        {
            pUartLogCtl->ExecuteCmd = _FALSE;
            pUartLogCtl->ExecuteEsc= _TRUE;//don't check Esc anymore
    #if defined(CONFIG_KERNEL)
            /* Create a Semaphone */
            //RtlInitSema((_Sema*)&(pUartLogCtl->Sema), 0);
            rtw_init_sema((_sema*)&(pUartLogCtl->Sema), 0);
            pUartLogCtl->TaskRdy = 0;
    #ifdef PLATFORM_FREERTOS
    #define	LOGUART_STACK_SIZE	128 //USE_MIN_STACK_SIZE modify from 512 to 128
    #if CONFIG_USE_TCM_HEAP    //TCM
    	{
    		int ret = 0;
    		void *stack_addr = tcm_heap_malloc(LOGUART_STACK_SIZE*sizeof(int));
    		//void *stack_addr = rtw_malloc(stack_size*sizeof(int));
    		if(stack_addr == NULL){
    			DiagPrintf("Out of TCM heap in "LOGUART_TASK" ");
    		}
    		ret = xTaskGenericCreate(
    				RtlConsolTaskRam,               /* 任务函数 */
    				(const char *)"LOGUART_TASK",   /* 任务名 */
    				LOGUART_STACK_SIZE,             /* 任务栈大小,单位 word,也就是 4 字节 */
    				NULL,                           /* 任务参数 */
    				tskIDLE_PRIORITY + 5 + PRIORITIE_OFFSET,  /* 任务优先级 */
    				NULL,
    				stack_addr,
    				NULL);
    		if (pdTRUE != ret)
    		{
    			DiagPrintf("Create Log UART Task Err!!
    ");
    		}
    	}
    #else
    	if (pdTRUE != xTaskCreate( RtlConsolTaskRam,              /* 任务函数 */
                                      (const signed char * const)"LOGUART_TASK",  /* 任务名 */
                                      LOGUART_STACK_SIZE,            /* 任务栈大小,单位 word,也就是 4 字节 */
                                      NULL,                          /* 任务参数 */
                                      tskIDLE_PRIORITY + 5 + PRIORITIE_OFFSET, /* 任务优先级 */
                                      NULL))                        /* 任务句柄 */
    	{
    		DiagPrintf("Create Log UART Task Err!!
    ");
    	}
    #endif
    
    #endif
    
    #endif
        }
    
        CONSOLE_8195A();
    }
    
    extern u8** GetArgv(const u8 *string);
    #if SUPPORT_LOG_SERVICE
    extern char log_buf[LOG_SERVICE_BUFLEN];
    extern xSemaphoreHandle	log_rx_interrupt_sema;
    #endif
    //======================================================
    void console_cmd_exec(PUART_LOG_CTL   pUartLogCtlExe)
    {
        u8  CmdCnt = 0;
        u8  argc = 0;
        u8  **argv;
        //u32  CmdNum;
        PUART_LOG_BUF   pUartLogBuf = pUartLogCtlExe->pTmpLogBuf;
    #if SUPPORT_LOG_SERVICE
        strncpy(log_buf, (const u8*)&(*pUartLogBuf).UARTLogBuf[0], LOG_SERVICE_BUFLEN-1);
    #endif
        argc = GetArgc((const u8*)&((*pUartLogBuf).UARTLogBuf[0]));
        argv = GetArgv((const u8*)&((*pUartLogBuf).UARTLogBuf[0]));
    
    	if(argc > 0){
    #if SUPPORT_LOG_SERVICE
    //		if(log_handler(argv[0]) == NULL)
    //			legency_interactive_handler(argc, argv);
                  //RtlUpSema((_Sema *)&log_rx_interrupt_sema);
    		rtw_up_sema((_sema *)&log_rx_interrupt_sema);
    #endif
                  ArrayInitialize(argv[0], sizeof(argv[0]) ,0);
    	}else{
    #if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1)
    		pmu_acquire_wakelock(BIT(PMU_LOGUART_DEVICE));
    #endif
    		CONSOLE_8195A(); // for null command
    	}
    
        (*pUartLogBuf).BufCount = 0;
        ArrayInitialize(&(*pUartLogBuf).UARTLogBuf[0], UART_LOG_CMD_BUFLEN, '');
    }
    //======================================================
    // overload original RtlConsolTaskRam
    MON_RAM_TEXT_SECTION
    VOID
    RtlConsolTaskRam(
        VOID *Data
    )
    {
    #if SUPPORT_LOG_SERVICE
    	log_service_init();
    #endif
        //4 Set this for UartLog check cmd history
    #ifdef CONFIG_KERNEL
    	pUartLogCtl->TaskRdy = 1;
    	up_sema_from_isr = rtw_up_sema_from_isr;
    #endif
    #ifndef CONFIG_KERNEL
        pUartLogCtl->BootRdy = 1;
    #endif
        do{
    #if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED
    		//RtlDownSema((_Sema *)&pUartLogCtl->Sema);
    		rtw_down_sema((_sema *)&pUartLogCtl->Sema);
    #endif
            if (pUartLogCtl->ExecuteCmd) {
    			// Add command handler here
    			console_cmd_exec((PUART_LOG_CTL)pUartLogCtl);
                //UartLogCmdExecute((PUART_LOG_CTL)pUartLogCtl);
                pUartLogCtl->ExecuteCmd = _FALSE;
            }
        }while(1);
    }
    
    //======================================================
    #if BUFFERED_PRINTF
    xTaskHandle print_task = NULL;           /* 任务函数 */
    EventGroupHandle_t print_event = NULL;   /* 事件标志组句柄 */
    char print_buffer[MAX_PRINTF_BUF_LEN];    
    int flush_idx = 0;       
    int used_length = 0;                     //长度
    
    //检查可用空间
    int available_space(void)
    {
        return MAX_PRINTF_BUF_LEN-used_length;
    }
    
    
    //打印函数
    int buffered_printf(const char* fmt, ...)
    {
        /*合法性检查*/
        if((print_task==NULL) || (print_event==NULL) )
            return 0;
        
        char tmp_buffer[UART_LOG_CMD_BUFLEN+1];
        static int print_idx = 0;
        int cnt;
    
        if(xEventGroupGetBits(print_event)!=1)  
                xEventGroupSetBits(print_event, /* 事件标志组句柄 */
                                   1);          /* 事件标志位设置 */
    
        memset(tmp_buffer,0,UART_LOG_CMD_BUFLEN+1);  
        VSprintf(tmp_buffer, fmt, ((const int *)&fmt)+1);//将param 按格式format写入字符串string中
        cnt = _strlen(tmp_buffer);     //检查长度
        
        if(cnt < available_space()){   //可用长度
            if(print_idx >= flush_idx){
                if(MAX_PRINTF_BUF_LEN-print_idx >= cnt){
                    memcpy(&print_buffer[print_idx], tmp_buffer, cnt);
                }else{
                    memcpy(&print_buffer[print_idx], tmp_buffer, MAX_PRINTF_BUF_LEN-print_idx);
                    memcpy(&print_buffer[0], &tmp_buffer[MAX_PRINTF_BUF_LEN-print_idx], cnt-(MAX_PRINTF_BUF_LEN-print_idx));
                }
            }else{  // space is flush_idx - print_idx, and available space is enough
                memcpy(&print_buffer[print_idx], tmp_buffer, cnt);
            }
            // protection needed
            taskENTER_CRITICAL(); /*为了保证不被中断,将操作放入临界区。进入临界区 */
            used_length+=cnt;
            taskEXIT_CRITICAL();  /* 完成操作,离开临界区*/
            print_idx+=cnt;     
            if(print_idx>=MAX_PRINTF_BUF_LEN)
                print_idx -= MAX_PRINTF_BUF_LEN;
        }else{
            // skip
            cnt = 0;
        }
    
        return cnt;
    }
    
    /* 任务函数 */
    void printing_task(void* arg)
    {
        while(1){
            //wait event    //等待事件标志被设置
            if(xEventGroupWaitBits(print_event,  /* 事件标志组句柄 */
                                   1,            /* 等待被设置的事件标志位 */
                                   pdFALSE,      /* 选择是否清零被置位的事件标志位 */
                                   pdFALSE,      /* 选择是否等待所有标志位都被设置 */
                                   100) == 1){   /* 设置等待时间 */
                while(used_length > 0){
                    putchar(print_buffer[flush_idx]);   //输出
                    flush_idx++;
                    if(flush_idx >= MAX_PRINTF_BUF_LEN)
                        flush_idx-=MAX_PRINTF_BUF_LEN;
                    taskENTER_CRITICAL();   /*为了保证不被中断,将操作放入临界区。进入临界区 */
                    used_length--;         
                    taskEXIT_CRITICAL();   /* 完成操作,离开临界区*/
                }
                // clear event
                xEventGroupClearBits( print_event, 1);   /* 事件标志位清零 */
            }
        }
    }
    
    //打印日志初始化
    void rtl_printf_init()
    {
        if(print_event==NULL){
            print_event = xEventGroupCreate();   /* 创建事件标志组 */
            if(print_event == NULL)              /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
                printf("
    
    print event init fail!
    ");
        }
        if(print_task == NULL){
            if(xTaskCreate(printing_task,                   /* 任务函数 */
                           (const char *)"print_task",      /* 任务名 */
                           512,                             /* 任务栈大小,单位 word,也就是 4 字节 */
                           NULL,                            /* 任务参数 */
                           tskIDLE_PRIORITY + 1,            /* 任务优先级*/
                           &print_task) != pdPASS)          /* 任务句柄 */
                printf("
    
    print task init fail!
    ");
        }
    }
    #endif
    //======================================================
    
    //弱引用
    __weak void console_init(void)
    {
    
    	    IRQ_HANDLE          UartIrqHandle;
    	    
    	    //4 Register Log Uart Callback function
    	    UartIrqHandle.Data = NULL;//(u32)&UartAdapter;  //中断
    	    UartIrqHandle.IrqNum = UART_LOG_IRQ;            //中断号
    	    UartIrqHandle.IrqFun = (IRQ_FUN) UartLogIrqHandleRam;//中断函数
    	    UartIrqHandle.Priority = 0;      //中断优先级
    
    	    
    	    //4 Register Isr handle
    	    InterruptUnRegister(&UartIrqHandle);     //中断
    	    InterruptRegister(&UartIrqHandle);       //注册中断
            
    
    
    #if !TASK_SCHEDULER_DISABLED    
        RtlConsolInitRam((u32)RAM_STAGE,(u32)0,(VOID*)NULL);   //禁止配置和制定自动任务
    #else
        RtlConsolInitRam((u32)ROM_STAGE,(u32)0,(VOID*)NULL);
    #endif
    
    #if BUFFERED_PRINTF
        rtl_printf_init();   //无效
    #endif
    }
    

      主要是取消了MON_RAM_TEXT_SECTION注释

  • 相关阅读:
    使用C++与SFML编写一个简单的撞球游戏Part3——创建游戏启动界面
    生命游戏
    一道面试题
    为目标数字添加逗号分隔,由 baidu.number.comma 想到的
    制作JavaScript选择器(1)解析令牌
    Team Foundation 使用第三方比较工具
    Levenshtein Distance算法
    计算颜色的亮度值
    整理QUnit API
    Scrum笔记整理
  • 原文地址:https://www.cnblogs.com/zzuzpb/p/7678184.html
Copyright © 2011-2022 走看看