[{"content":" 经常会遇到USB转TTL模块不够用的情况，因此这次直接搞一个一转四的USB转TTL模块，这下总够用了吧；\n原理图 设计原理图\nPCB PCB图\n​\nPCB仿真图(正面)\nPCB仿真图(反面)\n在线BOM BOM\n实物图 实物图(正面)\n实物图(反面)\n四个COM口！！！！\n","permalink":"https://fan-pengfei.top/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/","summary":"\u003cblockquote\u003e\n\u003cp\u003e经常会遇到USB转TTL模块不够用的情况，因此这次直接搞一个一转四的USB转TTL模块，这下总够用了吧；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"原理图\"\u003e原理图\u003c/h2\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e设计原理图\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"pcb\"\u003ePCB\u003c/h2\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-2.png\"\u003e\n\u003cstrong\u003ePCB图\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e​\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePCB仿真图(正面)\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePCB仿真图(反面)\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"在线bom\"\u003e在线BOM\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://www.fan-pengfei.top/HTML/USB2TTL\"\u003eBOM\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"实物图\"\u003e实物图\u003c/h2\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e实物图(正面)\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e实物图(反面)\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e四个COM口！！！！\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E4%B8%80%E8%BD%AC%E5%9B%9Busb%E8%BD%ACttl%E6%A8%A1%E5%9D%97/img-7.jpg\"\u003e\u003c/p\u003e","title":"一转四USB转TTL模块"},{"content":" 这个是关于LOG分级打印的配置，并有时间戳，基于STM32H7和gcc；\n环境 STM32H743IIT6、STM32CubeMX、GCC、MakeFile\nprintf映射 usart.c中加入以下代码\n#include \u0026#34;stdio.h\u0026#34; /*# 7- Retarget printf to UART (std library and toolchain dependent) #########*/ #if defined(__GNUC__) int _write(int fd, char *ptr, int len) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY); return len; } #elif defined(__ICCARM__) #include \u0026#34;LowLevelIOInterface.h\u0026#34; size_t __write(int handle, const unsigned char *buffer, size_t size) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)buffer, size, HAL_MAX_DELAY); return size; } #elif defined(__CC_ARM) int fputc(int ch, FILE *f) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)\u0026amp;ch, 1, HAL_MAX_DELAY); return ch; } #endif #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */ /** * @brief Retargets the C library printf function to the USART. * @param None * @retval None */ PUTCHAR_PROTOTYPE { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)\u0026amp;ch, 1, HAL_MAX_DELAY); return ch; } MakeFile中删除以下配置：\n# LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections LDFLAGS = $(MCU) -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections LOG分级打印 新建my_log.h文件，写入以下内容：\n#ifndef __MYLOG_H__ #define __MYLOG_H__ #ifdef __cplusplus extern \u0026#34;C\u0026#34; { #endif #include #include \u0026#34;main.h\u0026#34; /* 日志级别 */ #define ELOG_LVL_ASSERT 0 #define ELOG_LVL_ERROR 1 #define ELOG_LVL_WARN 2 #define ELOG_LVL_INFO 3 #define ELOG_LVL_DEBUG 4 #define ELOG_LVL_VERBOSE 5 /* 设置日志级别 */ #define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /*是否打印详细信息*/ #define ELOG_DETAIL 0 #if ELOG_DETAIL == 1 /* 断言(Assert) */ #define LOG_ASSERT(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_ASSERT) \\ { \\ printf(\u0026#34;[%ld.%03ld][ASSERT/%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) /* 错误(Error) */ #define LOG_ERROR(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_ASSERT) \\ { \\ printf(\u0026#34;[%ld.%03ld][ERROR/%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) /* 警告(Warn) */ #define LOG_WARN(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_WARN) \\ { \\ printf(\u0026#34;[%ld.%03ld][WARN /%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) /* 信息(Info) */ #define LOG_INFO(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_INFO) \\ { \\ printf(\u0026#34;[%ld.%03ld][INFO /%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) /* 调试(Debug) */ #define LOG_DEBUG(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_DEBUG) \\ { \\ printf(\u0026#34;[%ld.%03ld][DEBUG/%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) /* 详细(Verbose) */ #define LOG_VERBOSE(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_VERBOSE) \\ { \\ printf(\u0026#34;[%ld.%03ld][VERBOSE/%s Line:%.4d] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, __FILE__, __LINE__, ##__VA_ARGS__); \\ } \\ } while (0) #else /* 断言(Assert) */ #define LOG_ASSERT(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_ASSERT) \\ { \\ printf(\u0026#34;[%ld.%03ld][ASSERT] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) /* 错误(Error) */ #define LOG_ERROR(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_ASSERT) \\ { \\ printf(\u0026#34;[%ld.%03ld][ERROR] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) /* 警告(Warn) */ #define LOG_WARN(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_WARN) \\ { \\ printf(\u0026#34;[%ld.%03ld][WARN ] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) /* 信息(Info) */ #define LOG_INFO(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_INFO) \\ { \\ printf(\u0026#34;[%ld.%03ld][INFO ] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) /* 调试(Debug) */ #define LOG_DEBUG(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_DEBUG) \\ { \\ printf(\u0026#34;[%ld.%03ld][DEBUG] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) /* 详细(Verbose) */ #define LOG_VERBOSE(args, ...) \\ do \\ { \\ if (ELOG_OUTPUT_LVL \u0026gt;= ELOG_LVL_VERBOSE) \\ { \\ printf(\u0026#34;[%ld.%03ld][VERBOSE] \u0026#34; args \u0026#34;\\r\\n\u0026#34;, tick_all / 1000, tick_all % 1000, ##__VA_ARGS__); \\ } \\ } while (0) #endif // !ELOG_DETAIL #ifdef __cplusplus } #endif #endif /* __MYLOG_H__ */ stm32h7xx_it.c中添加以下内容：\nuint32_t tick_all = 0; void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ tick_all++; /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ } main.h中添加以下内容：\n/* USER CODE BEGIN ET */ extern uint32_t tick_all; /* USER CODE END ET */ 测试 main.c中添加以下代码：\nwhile (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin); HAL_Delay(200); LOG_DEBUG(\u0026#34;LOG_DEBUG\u0026#34;); LOG_INFO(\u0026#34;LOG_INFO\u0026#34;); LOG_WARN(\u0026#34;LOG_INFO\u0026#34;); LOG_ERROR(\u0026#34;LOG_ERROR\u0026#34;); } 结果：\n增加VOFA支持 增加宏定义如下：\n/* VOFA */ #define LOG_VOFA(channel, args) \\ do \\ { \\ switch (sizeof(args) / sizeof(args[0])) \\ { \\ case 1: \\ printf(\u0026#34;[VOFA] %s:%f\\n\\r\u0026#34;, tick_all / 1000, tick_all % 1000, channel, args[0]); \\ break; \\ case 2: \\ printf(\u0026#34;[VOFA] %s:%f,%f\\n\\r\u0026#34;, tick_all / 1000, tick_all % 1000, channel, args[0], args[1]); \\ break; \\ case 3: \\ printf(\u0026#34;[VOFA] %s:%f,%f,%f\\n\\r\u0026#34;, tick_all / 1000, tick_all % 1000, channel, args[0], args[1], args[2]); \\ break; \\ case 4: \\ printf(\u0026#34;[VOFA] %s:%f,%f,%f,%f\\n\\r\u0026#34;, tick_all / 1000, tick_all % 1000, channel, args[0], args[1], args[2], args[3]); \\ break; \\ default: \\ break; \\ } \\ } while (0) 测试代码：\nfloat t = 0.0; float ch4[4] = {0.0f}; t = t + 0.01; ch4[0] = (float)sin(t); ch4[1] = (float)sin(2 * t); ch4[2] = (float)sin(3 * t); ch4[3] = (float)sin(4 * t); HAL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin); HAL_Delay(10); LOG_VOFA(\u0026#34;ch4\u0026#34;, ch4); ","permalink":"https://fan-pengfei.top/posts/log%E5%88%86%E7%BA%A7%E6%89%93%E5%8D%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这个是关于LOG分级打印的配置，并有时间戳，基于STM32H7和gcc；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"环境\"\u003e环境\u003c/h2\u003e\n\u003cp\u003eSTM32H743IIT6、STM32CubeMX、GCC、MakeFile\u003c/p\u003e\n\u003ch2 id=\"printf映射\"\u003eprintf映射\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eusart.c中加入以下代码\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;stdio.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*# 7- Retarget printf to UART (std library and toolchain dependent) #########*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#if defined(__GNUC__)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003e_write\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e fd, \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eptr, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e len)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_UART_Transmit\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ehuart1, (\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)ptr, len, HAL_MAX_DELAY);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e len;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#elif defined(__ICCARM__)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;LowLevelIOInterface.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003esize_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003e__write\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e handle, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ebuffer, \u003cspan style=\"color:#66d9ef\"\u003esize_t\u003c/span\u003e size)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_UART_Transmit\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ehuart1, (\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)buffer, size, HAL_MAX_DELAY);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e size;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#elif defined(__CC_ARM)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efputc\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e ch, FILE \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ef)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_UART_Transmit\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ehuart1, (\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ech, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, HAL_MAX_DELAY);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e ch;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ifdef __GNUC__\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#else\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif \u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e/* __GNUC__ */\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  Retargets the C library printf function to the USART.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @param  None\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @retval None\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePUTCHAR_PROTOTYPE\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_UART_Transmit\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ehuart1, (\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ech, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, HAL_MAX_DELAY);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e ch;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003eMakeFile中删除以下配置：\u003c/p\u003e","title":"LOG分级打印"},{"content":" windows11下如何烧录uboot镜像呢？当然是用windows下的dd命令了；\n首先下载dd.exe，并将其命名为dd.exe，将其加入系统变量；\nhttp://www.chrysocome.net/downloads/ddrelease64.exe\n测试如下图所示即安装成功：\n然后已有u-boot-sunxi-with-spl.bin，因而插入SD卡，输入以下命令：\ndd.exe if=.\\u-boot-sunxi-with-spl.bin of=d:\\temp.img bs=1024 seek=8 然后使用DiskImager将temp.img镜像烧录进SD卡即可。\n感觉之后生成的各种镜像只要组合成一个大的image就可以。\n","permalink":"https://fan-pengfei.top/posts/windows11%E4%B8%8Buboot%E7%83%A7%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003ewindows11下如何烧录uboot镜像呢？当然是用windows下的dd命令了；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e首先下载dd.exe，并将其命名为dd.exe，将其加入系统变量；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"http://www.chrysocome.net/downloads/ddrelease64.exe\"\u003ehttp://www.chrysocome.net/downloads/ddrelease64.exe\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e测试如下图所示即安装成功：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/windows11%E4%B8%8Buboot%E7%83%A7%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后已有u-boot-sunxi-with-spl.bin，因而插入SD卡，输入以下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edd.exe \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e=.\\u-boot-sunxi-with-spl.bin of=d:\\temp.img bs=\u003cspan style=\"color:#ae81ff\"\u003e1024\u003c/span\u003e seek=\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/windows11%E4%B8%8Buboot%E7%83%A7%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后使用DiskImager将temp.img镜像烧录进SD卡即可。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e感觉之后生成的各种镜像只要组合成一个大的image就可以。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"windows11下uboot烧录"},{"content":" 使用buildroot构建系统的话，如果在output/build中对某个软件包修改的话，一旦使用make clean，就会导致自己做的修改被抹除；为避免这个情况，buildroot是提供了一种机制，也即OVERRIDE_SRCDIR； 参考：https://www.cnblogs.com/pwl999/p/15534966.html\nBuildroot的一般操作是下载tar包、提取、配置、编译和安装该tar包内的软件。源代码提取保存在临时目录output/build/-目录中，当执行make clean时，该目录会被完全删除，并在下一次make时重新创建。即使将Git或Subversion等版本管理系统作为软件包源代码的输入，Buildroot也会从中创建一个tar包，然后像对待一般tar包一样工作。\n这种方式非常适合将Buildroot当做集成工具，编译和集成嵌入式Linux系统的所有组件。但是，如果是在开发系统的某些组件的过程中使用Buildroot，这种方式非常不方便：开发者希望对一个软件包的源代码做少许修改，并能够使用Buildroot快速重建系统。 直接修改output/build/-不是合适的解决方案，因为该目录会在make clean时删除。\n因此，Buildroot针对该场景提供了一种特殊的机制，即_OVERRIDE_SRCDIR机制。Buildroot读取一个override文档，该文档允许用户告诉Buildroot某些软件包的源代码位置。\n覆盖文档(override)的默认位置是$(CONFIG_DIR)/local.mk。由BR2_PACKAGE_OVERRIDE_FILE配置选项定义。$(CONFIG_DIR)是Buildroot .config文档的位置，因此local.mk默认情况下与.config文档放在一起，这意味着：这意味着:\nBuildroot目录树内构建时位于Buildroot顶层目录中（当O=不使用时）\nBuildroot目录树外构建时位于目录树外目录（当O=使用时）\n如果需要不同于这些默认值的位置，可以通过BR2_PACKAGE_OVERRIDE_FILE配置选项指定。\n在这个override文档中，Buildroot期望找到以下形式中的行:\n_OVERRIDE_SRCDIR = /path/to/pkg1/sources _OVERRIDE_SRCDIR = /path/to/pkg2/sources 例如:\nLINUX_OVERRIDE_SRCDIR = /home/bob/linux/ BUSYBOX_OVERRIDE_SRCDIR = /home/bob/busybox/ 当Buildroot发现给定的软件包存在_OVERRIDE_SRCDIR定义时，它将不再尝试下载、提取和修补软件包，它将直接使用指定目录中可用的源代码，并且make clean时不会涉及该目录。这允许将Buildroot指向您自己的目录，该目录可以由Git、Subversion或其他版本控制系统管理。为此，Buildroot将使用rsync将软件包的源代码从_OVERRIDE_SRCDIR指定的位置复制到output/build/-custom/目录。\n该机制最好与make -rebuild和make -reconfigure结合使用。make-rebuild all将rsync源代码从_OVERRIDE_SRCDIR到output/build/-custom（只有修改过的文档会被复制），并重新启动这个软件包的构建过程。\n在上述Linux软件包的示例中，开发人员可以修改 /home/bob/linux目录下的源代码，然后运行：\nmake linux-rebuild all 并在几秒钟内在output/images中获得更新后的Linux内核映像。类似地，可以在/home/bob/busybox和后面对BusyBox源代码进行更改:\nmake busybox-rebuild all output/images中的根文档系统映像包含更新后的BusyBox。\n大型项目一般有成百上千的文档，很多文档对于构建时是不需要的，但是会减慢rsync复制源代码的过程。可选的，可以定义_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS跳过源代码中的某些文档。例如，当处理webkitgtk软件包时，以下内容将从本地WebKit源代码中排除：\nWEBKITGTK_OVERRIDE_SRCDIR = /home/bob/WebKit WEBKITGTK_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = \\ --exclude JSTests --exclude ManualTests --exclude PerformanceTests \\ --exclude WebDriverTests --exclude WebKitBuild --exclude WebKitLibraries \\ --exclude WebKit.xcworkspace --exclude Websites --exclude Examples 默认情况下，Buildroot会跳过VCS信息（例如.git或.svn）的同步。一些软件包在编译过程中会使用VCS信息，例如精确确认提交信息。要取消Buildroot的内置过滤规则，需要重新添加以下目录：\nLINUX_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS = --include .git ","permalink":"https://fan-pengfei.top/posts/buildroot%E7%9A%84override_srcdir%E6%9C%BA%E5%88%B6/","summary":"\u003cblockquote\u003e\n\u003cp\u003e使用buildroot构建系统的话，如果在output/build中对某个软件包修改的话，一旦使用make clean，就会导致自己做的修改被抹除；为避免这个情况，buildroot是提供了一种机制，也即\u003ccode\u003eOVERRIDE_SRCDIR\u003c/code\u003e；\n参考：\u003ca href=\"https://www.cnblogs.com/pwl999/p/15534966.html\"\u003ehttps://www.cnblogs.com/pwl999/p/15534966.html\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eBuildroot的一般操作是下载tar包、提取、配置、编译和安装该tar包内的软件。源代码提取保存在临时目录\u003ccode\u003eoutput/build/-\u003c/code\u003e目录中，当执行\u003ccode\u003emake clean\u003c/code\u003e时，该目录会被完全删除，并在下一次make时重新创建。即使将Git或Subversion等版本管理系统作为软件包源代码的输入，Buildroot也会从中创建一个tar包，然后像对待一般tar包一样工作。\u003c/p\u003e\n\u003cp\u003e这种方式非常适合将Buildroot当做集成工具，编译和集成嵌入式Linux系统的所有组件。但是，如果是在开发系统的某些组件的过程中使用Buildroot，这种方式非常不方便：开发者希望对一个软件包的源代码做少许修改，并能够使用Buildroot快速重建系统。\n直接修改\u003ccode\u003eoutput/build/-\u003c/code\u003e不是合适的解决方案，因为该目录会在\u003ccode\u003emake clean\u003c/code\u003e时删除。\u003c/p\u003e\n\u003cp\u003e因此，Buildroot针对该场景提供了一种特殊的机制，即\u003ccode\u003e_OVERRIDE_SRCDIR\u003c/code\u003e机制。Buildroot读取一个override文档，该文档允许用户告诉Buildroot某些软件包的源代码位置。\u003c/p\u003e\n\u003cp\u003e覆盖文档(override)的默认位置是\u003ccode\u003e$(CONFIG_DIR)/local.mk\u003c/code\u003e。由\u003ccode\u003eBR2_PACKAGE_OVERRIDE_FILE\u003c/code\u003e配置选项定义。\u003ccode\u003e$(CONFIG_DIR)\u003c/code\u003e是Buildroot \u003ccode\u003e.config\u003c/code\u003e文档的位置，因此\u003ccode\u003elocal.mk\u003c/code\u003e默认情况下与\u003ccode\u003e.config\u003c/code\u003e文档放在一起，这意味着：这意味着:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eBuildroot目录树内构建时位于Buildroot顶层目录中（当O=不使用时）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eBuildroot目录树外构建时位于目录树外目录（当O=使用时）\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如果需要不同于这些默认值的位置，可以通过\u003ccode\u003eBR2_PACKAGE_OVERRIDE_FILE\u003c/code\u003e配置选项指定。\u003c/p\u003e\n\u003cp\u003e在这个override文档中，Buildroot期望找到以下形式中的行:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-makefile\" data-lang=\"makefile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e_OVERRIDE_SRCDIR \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e /path/to/pkg1/sources\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e_OVERRIDE_SRCDIR \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e /path/to/pkg2/sources\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e例如:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-makefile\" data-lang=\"makefile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLINUX_OVERRIDE_SRCDIR \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e /home/bob/linux/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBUSYBOX_OVERRIDE_SRCDIR \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e /home/bob/busybox/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e当Buildroot发现给定的软件包存在_OVERRIDE_SRCDIR定义时，它将不再尝试下载、提取和修补软件包，它将直接使用指定目录中可用的源代码，并且make clean时不会涉及该目录。这允许将Buildroot指向您自己的目录，该目录可以由Git、Subversion或其他版本控制系统管理。为此，Buildroot将使用rsync将软件包的源代码从_OVERRIDE_SRCDIR指定的位置复制到\u003ccode\u003eoutput/build/-custom/\u003c/code\u003e目录。\u003c/p\u003e\n\u003cp\u003e该机制最好与\u003ccode\u003emake -rebuild\u003c/code\u003e和\u003ccode\u003emake -reconfigure\u003c/code\u003e结合使用。make\u003ccode\u003e-rebuild all\u003c/code\u003e将rsync源代码从\u003ccode\u003e_OVERRIDE_SRCDIR\u003c/code\u003e到\u003ccode\u003eoutput/build/-custom\u003c/code\u003e（只有修改过的文档会被复制），并重新启动这个软件包的构建过程。\u003c/p\u003e\n\u003cp\u003e在上述Linux软件包的示例中，开发人员可以修改 \u003ccode\u003e/home/bob/linux\u003c/code\u003e目录下的源代码，然后运行：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake linux-rebuild all\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e并在几秒钟内在\u003ccode\u003eoutput/images\u003c/code\u003e中获得更新后的Linux内核映像。类似地，可以在\u003ccode\u003e/home/bob/busybox\u003c/code\u003e和后面对BusyBox源代码进行更改:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake busybox-rebuild all\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eoutput/images\u003c/code\u003e中的根文档系统映像包含更新后的BusyBox。\u003c/p\u003e\n\u003cp\u003e大型项目一般有成百上千的文档，很多文档对于构建时是不需要的，但是会减慢rsync复制源代码的过程。可选的，可以定义\u003ccode\u003e_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS\u003c/code\u003e跳过源代码中的某些文档。例如，当处理webkitgtk软件包时，以下内容将从本地WebKit源代码中排除：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-lua\" data-lang=\"lua\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWEBKITGTK_OVERRIDE_SRCDIR \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehome\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ebob\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eWebKit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWEBKITGTK_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\\\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e--exclude JSTests --exclude ManualTests --exclude PerformanceTests \\\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e--exclude WebDriverTests --exclude WebKitBuild --exclude WebKitLibraries \\\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e--exclude WebKit.xcworkspace --exclude Websites --exclude Examples\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e默认情况下，Buildroot会跳过VCS信息（例如.git或.svn）的同步。一些软件包在编译过程中会使用VCS信息，例如精确确认提交信息。要取消Buildroot的内置过滤规则，需要重新添加以下目录：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-makefile\" data-lang=\"makefile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLINUX_OVERRIDE_SRCDIR_RSYNC_EXCLUSIONS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e --include .git\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"buildroot的OVERRIDE_SRCDIR机制"},{"content":" 最近买了一块便宜的Linux开发板，是基于全志H3芯片的，想从头到尾跑一下开发流程；\nUboot开发 环境搭建 安装好make、arm-linux-gnueabihf-等工具。\nUboot编译 源码下载：https://ftp.denx.de/pub/u-boot/\nwget https://ftp.denx.de/pub/u-boot/u-boot-2020.04.tar.bz2 选择u-boot-2020.04.tar.bz2即可；\n使用以下命令进行解压操作：\ntar -xvf u-boot-2020.04.tar.bz2 然后进行编译选项配置：\ncd u-boot-2020.04/ make -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- orangepi_lite_defconfig 编译：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- V=1 Uboot烧录 准备一个micro SD卡（大于8GB）；\n通过读卡器插入电脑；\n安装并打开gparted：\nsudo apt install gparted sudo gparted 按照以下方式修改分区配置：\n可能会出现/sdb大小不对的问题，可以先取下读卡器，然后使用sudo rm -rf /dev/sdb即可，然后再次进行分区即可；\n使用以下命令进行烧录即可：\nsudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8 上电测试 将SD卡插入开发板，给开发板上电，打开调试接口接口看到以下输出：\nLinux内核开发 源码下载 git clone --depth 1 --branch orange-pi-5.4 https://ghproxy.com/https://github.com/orangepi-xunlong/linux-orangepi.git cd linux-orangepi/ 编译 make sunxi_defconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8 zImage dtbs ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 生成的设备树和镜像路径：\n/arch/arm/boot UBoot启动 setenv bootcmd \u0026#39;load mmc 0:1 0x43000000 sun8i-h3-orangepi-lite.dtb; load mmc 0:1 0x42000000 zImage; bootz 0x42000000 - 0x43000000\u0026#39; saveenv boot rootfs 烧写 sudo dd if=/dev/sdb2 of=rootfs.ext2 bs=1M count=512 ","permalink":"https://fan-pengfei.top/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近买了一块便宜的Linux开发板，是基于全志H3芯片的，想从头到尾跑一下开发流程；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"uboot开发\"\u003eUboot开发\u003c/h1\u003e\n\u003ch2 id=\"环境搭建\"\u003e环境搭建\u003c/h2\u003e\n\u003cp\u003e安装好\u003ccode\u003emake\u003c/code\u003e、\u003ccode\u003earm-linux-gnueabihf-\u003c/code\u003e等工具。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"66731729411d3b779b15ae939b3edbc\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"9abe75f6eb356759b44633eaf2ab67f\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-2.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"uboot编译\"\u003eUboot编译\u003c/h2\u003e\n\u003cp\u003e源码下载：\u003ca href=\"https://ftp.denx.de/pub/u-boot/\"\u003ehttps://ftp.denx.de/pub/u-boot/\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget https://ftp.denx.de/pub/u-boot/u-boot-2020.04.tar.bz2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e选择\u003ccode\u003eu-boot-2020.04.tar.bz2\u003c/code\u003e即可；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e使用以下命令进行解压操作：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etar -xvf u-boot-2020.04.tar.bz2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后进行编译选项配置：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd u-boot-2020.04/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake -j4 ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabihf- orangepi_lite_defconfig\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e编译：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- V=1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"uboot烧录\"\u003eUboot烧录\u003c/h2\u003e\n\u003cp\u003e准备一个micro SD卡（大于8GB）；\u003c/p\u003e\n\u003cp\u003e通过读卡器插入电脑；\u003c/p\u003e\n\u003cp\u003e安装并打开gparted：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install gparted\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo gparted\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e按照以下方式修改分区配置：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"a79398dcd897eee65a10a1093509cf6\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"1937ec428da5753e33a8b837fbf785d\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-4.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e可能会出现\u003ccode\u003e/sdb\u003c/code\u003e大小不对的问题，可以先取下读卡器，然后使用\u003ccode\u003esudo rm -rf /dev/sdb\u003c/code\u003e即可，然后再次进行分区即可；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e使用以下命令进行烧录即可：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dd \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eu-boot-sunxi-with-spl.bin of\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/sdb bs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1024\u003c/span\u003e seek\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"c45e7037f5e9aac71fb62139e471ffe\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-5.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"上电测试\"\u003e上电测试\u003c/h2\u003e\n\u003cp\u003e将SD卡插入开发板，给开发板上电，打开调试接口接口看到以下输出：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"871bacab875c25527f5ea4da991101a\" loading=\"lazy\" src=\"/posts/%E5%85%A8%E5%BF%97h3%E5%BC%80%E5%8F%91/img-6.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"linux内核开发\"\u003eLinux内核开发\u003c/h1\u003e\n\u003ch2 id=\"源码下载\"\u003e源码下载\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone --depth \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e  --branch orange-pi-5.4  https://ghproxy.com/https://github.com/orangepi-xunlong/linux-orangepi.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd linux-orangepi/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"编译\"\u003e编译\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake sunxi_defconfig ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabihf-\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake -j8 zImage dtbs ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabihf-\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e生成的设备树和镜像路径：\u003c/p\u003e","title":"全志H3开发"},{"content":"","permalink":"https://fan-pengfei.top/posts/%E6%88%91%E7%9A%84%E5%A4%A7%E5%AD%A6%E5%9B%9B%E5%B9%B4/","summary":"","title":"我的大学四年"},{"content":" 很有一意思的一段趣闻；\n参考：https://www.zhihu.com/question/582916722/answer/2951998203\nDuring his own Google interview, Jeff Dean was asked the implications if P=NP were true. He said, “P = 0 or N = 1.” Then, before the interviewer had even finished laughing, Jeff examined Google’s public certificate and wrote the private key on the whiteboard.\n当他被Google面试时，Jeff Dean被问及如果P=NP意味着什么。他说，“P=0 或者 N=1”。然后，在所有的面试官还没笑完之前，Jeff瞄了一眼Google的公共证书然后在白板上写上了对应的私钥。\nCompilers don’t warn Jeff Dean. Jeff Dean warns compilers.\n编译器从来不给Jeff编译警告，而是Jeff警告编译器。\nThe rate at which Jeff Dean produces code jumped by a factor of 40 in late 2000 when he upgraded his keyboard to USB 2.0.\n在2000年后段，Jeff码代码的速度突然激增了40倍，原因是他把自己的键盘升级到了USB 2.0。\nJeff Dean builds his code before committing it, but only to check for compiler and linker bugs.\nJeff还是会在提交代码前把它们编译一遍，不过这么做的目的只是为了检查下编译器和链接器有没有bug。\nAll pointers point to Jeff Dean.\n所有指针都是指向Jeff的。\ngcc -O4 emails your code to Jeff Dean for a rewrite.\ngcc的-O4优化选项是将你的代码邮件给Jeff重写一下。\nThe speed of light in a vacuum used to be about 35 mph. Then Jeff Dean spent a weekend optimizing physics.\n光在真空中的速度曾近是35英里每小时，后来Jeff花了一个周末对物理学进行了小小的优化。\nJeff Dean was born on December 31, 1969 at 11:48 PM. It took him twelve minutes to implement his first time counter.\nJeff出生于1969年12月31日的下午11点48分，然后他花了整整12分钟的时间实现了他的第一个计时器。（背景：计算机中的计时器数值通常被设计为从1970年1月1日0点0分0秒到当前为止的秒数）\nWhen Jeff Dean sends an ethernet frame there are no collisions because the competing frames retreat back up into the buffer memory on their source nic.\n当Jeff向以太网发送一个数据包时从来不会有冲突，原因是本来要和它有冲突的包都默默撤回了缓冲区。\nUnsatisfied with constant time, Jeff Dean created the world’s first O(1/n)algorithm.\nJeff对常量的时间复杂度并不满足，于是他创造了世界上第一个O(1/n)的算法。（即随着问题规模的增大，其解决问题所花的时间会越短）\nJeff Dean was forced to invent asynchronous APIs one day when he optimized a function so that it returned before it was invoked.\nJeff曾被迫发明了异步API，原因是经他优化后的某个函数会在调用开始前返回。\nWhen Jeff Dean designs software, he first codes the binary and then writes the source as documentation.\n当Jeff写软件时，他是直接码机器码的。写源代码只是为了作为文档使用。\nJeff Dean wrote an O(n^2) algorithm once. It was for the Traveling Salesman Problem.\nJeff曾无奈写过一次O(n^2)的算法，其解决的问题是旅行商问题。（该问题是NPC的，即计算机中最复杂最难解决的一类问题，许多人相信这些问题是没有多项式时间复杂度的解的）\nJeff Dean can beat you at connect four. In three moves.\nJeff在四子连珠的游戏中能赢你，在三步内。\nWhen your code has undefined behavior, you get a seg fault and corrupted data. When Jeff Dean’s code has undefined behavior, a unicorn rides in on a rainbow and gives everybody free ice cream.\n当你的代码有不确定行为时，你通常得到一个段错误或者不正确的数据。当Jeff的代码有不确定行为时，孙悟空会驾着七彩云朵给每个人发免费的长寿仙桃。\nWhen Jeff Dean fires up the profiler, loops unroll themselves in fear.\n当Jeff触发程序的程序性能采样时，循环会因害怕而自动展开。\nJeff Dean is still waiting for mathematicians to discover the joke he hid in the digits of PI.\nJeff依然孤独地等待着数学家们解开他在PI的数字中隐藏的笑话。\nJeff Dean’s keyboard has two keys: 1 and 0.\nJeff的键盘多达两个键：1和0。\nWhen Jeff has trouble sleeping, he Mapreduces sheep.\n当Jeff失眠时，他Mapreduce羊群。（Mapreduce是Jeff的作品之一，这个分布式处理的框架算法是Google立足的根本之一）\nWhen Jeff Dean listens to mp3s, he just cats them to /dev/dsp and does the decoding in his head.\n当Jeff听MP3时，他查看其中的二进制内容然后在他脑子里进行音频解码。\nWhen Graham Bell invented the telephone, he saw a missed call from Jeff Dean.\n当贝爷在沙漠中逮到一个手机时，他发现手机里有个Jeff的未接来电。\n当贝尔发明电话后，他发现电话里有个Jeff的未接来电。\nJeff Dean’s watch displays seconds since January 1st, 1970. He is never late.\nJeff的手表显示的数字是自1970年1月1日0点0分0秒以来的秒数，他因此从来不迟到。\nJeff starts his programming sessions with ‘cat \u0026gt; /dev/mem’.\nJeff在开始编程之前，会做’cat \u0026gt; /dev/mem’。（即把输入的内容直接导到内存中）\nOne day Jeff Dean grabbed his Etch-a-Sketch instead of his laptop on his way out the door. On his way back home to get his real laptop, he programmed the Etch-a-Sketch to play Tetris.\n有一次Jeff出门时错将草图画板当笔记本拿了。结果在他回家取笔记本的路上，他对画板进行了重编程以在其上玩俄罗斯方块。\nGoogle search went down for a few hours in 2002, and Jeff Dean started handling queries by hand. Search Quality doubled.\n在2002年，Google搜索曾挂了几个小时，于是Jeff站出来手动处理用户的查询请求。搜索准确度翻了番。\nThe x86-64 spec includes several undocumented instructions marked ‘private use’. They are actually for Jeff Dean’s use.\nx86-64指令集中有一些没有被记入到文档的‘私用’指令。事实上，他们是给Jeff用的。\nmantri@mantri-laptop~$ rm -r / rm: cannot remove root directory ‘/’ mantri@mantri-laptop~$ su - jeffdean -c “rm -r /“ I am extremely sorry. Removing root directory..\n不解释。\nJeff Dean once shifted a bit so hard, it ended up on another computer.\n有次Jeff移位移得太恨了，结果那一位跑到另一台计算机上去了。\nJeff Dean has gone to /dev/null and come back.\nJeff从/dev/null那涅槃了。（庞统啊~）\nJeff又从/dev/null那涅槃了。（擦！）\nJeff Dean sorts his phone contacts by their vcard’s md5 checksums.\nJeff通讯录的排序规则是按照联系人的md5值。\nJeff Dean doesn’t kill processes, he slays them.\nJeff他不杀进程，他肢解虐杀它们。\nThe needle in haystack found Jeff Dean.\n海底的针会自己找上Jeff。\nAll of the Google App Engine is actually hosted from Jeff Dean’s Nexus S.\nGoogle App Engine的服务器实际上是Jeff的Nexus S。\nJeff Deans’s keyboard doesn’t have a Ctrl key because nothing controls Jeff Dean.\nJeff的键盘压根就没有Ctrl(控制)键，因为没有什么东西能控制Jeff。\nYou name three pointers, Einstein, Euler, and Turing, when you de-reference them, all you get is Jeff Dean.\n如果你命名三个指针分别为爱因斯坦、欧拉和图灵，当你查看它们的指向时，你看到的都会是Jeff。\n","permalink":"https://fan-pengfei.top/posts/%E7%A5%9E%E5%A5%87%E7%9A%84jeff-dean/","summary":"\u003cblockquote\u003e\n\u003cp\u003e很有一意思的一段趣闻；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e参考：\u003ca href=\"https://www.zhihu.com/question/582916722/answer/2951998203\"\u003ehttps://www.zhihu.com/question/582916722/answer/2951998203\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eDuring his own Google interview, Jeff Dean was asked the implications if P=NP were true. He said, “P = 0 or N = 1.” Then, before the interviewer had even finished laughing, Jeff examined Google’s public certificate and wrote the private key on the whiteboard.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e当他被Google面试时，Jeff Dean被问及如果P=NP意味着什么。他说，“P=0 或者 N=1”。然后，在所有的面试官还没笑完之前，Jeff瞄了一眼Google的公共证书然后在白板上写上了对应的私钥。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCompilers don’t warn Jeff Dean. Jeff Dean warns compilers.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e编译器从来不给Jeff编译警告，而是Jeff警告编译器。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe rate at which Jeff Dean produces code jumped by a factor of 40 in late 2000 when he upgraded his keyboard to USB 2.0.\u003c/p\u003e","title":"神奇的Jeff Dean"},{"content":" 最近在用LED组成的数码管，由于位号硬件上有所改动，因而需要进行码值位之间的交换；\n以下是一段C语言函数，实现将一个八位的二进制数的任意两位交换：\n#include /** * @brief 交换一个八位的二进制数的任意两位 * @param x 一个八位的二进制数 * @param i 要交换的两个位置 * @param j 要交换的两个位置 * @return 交换后的结果 */ unsigned char swap_bits(unsigned char x, int i, int j) { // 获取第i位和第j位的值 unsigned char bit_i = (x \u0026gt;\u0026gt; i) \u0026amp; 1; unsigned char bit_j = (x \u0026gt;\u0026gt; j) \u0026amp; 1; // 如果第i位和第j位的值不同，那么交换它们 if (bit_i ^ bit_j) { x ^= (1 ","permalink":"https://fan-pengfei.top/posts/c%E8%AF%AD%E8%A8%80%E5%AE%9E%E7%8E%B0%E4%BA%A4%E6%8D%A2%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E7%9A%84%E4%BB%BB%E6%84%8F%E4%B8%A4%E4%BD%8D/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近在用LED组成的数码管，由于位号硬件上有所改动，因而需要进行码值位之间的交换；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e以下是一段C语言函数，实现将一个八位的二进制数的任意两位交换：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e@\u003c/span\u003ebrief   \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e交换一个八位的二进制数的任意两位\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e@\u003c/span\u003eparam x \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e一个八位的二进制数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e@\u003c/span\u003eparam i \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e要交换的两个位置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e@\u003c/span\u003eparam j \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e要交换的两个位置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e@\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e  \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e交换后的结果\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eswap_bits\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e x, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e j)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 获取第i位和第j位的值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e bit_i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (x \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e i) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e bit_j \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (x \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e j) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 如果第i位和第j位的值不同，那么交换它们\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (bit_i \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e bit_j)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        x \u003cspan style=\"color:#f92672\"\u003e^=\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"C语言实现交换二进制数的任意两位"},{"content":" 这个是小一的第六个正式版本了，这次是用ESP32作为主控的；\n硬件 原理图 PCB 在线BOM 在线BOM\n实物图 ","permalink":"https://fan-pengfei.top/posts/%E5%9F%BA%E4%BA%8Eesp32%E7%9A%84%E6%89%8B%E8%A1%A8/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这个是小一的第六个正式版本了，这次是用ESP32作为主控的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"硬件\"\u003e硬件\u003c/h2\u003e\n\u003ch3 id=\"原理图\"\u003e原理图\u003c/h3\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Eesp32%E7%9A%84%E6%89%8B%E8%A1%A8/img-1.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"pcb\"\u003ePCB\u003c/h3\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Eesp32%E7%9A%84%E6%89%8B%E8%A1%A8/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Eesp32%E7%9A%84%E6%89%8B%E8%A1%A8/img-3.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"在线bom\"\u003e在线BOM\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://www.fan-pengfei.top/HTML/ESP32_LCD\"\u003e在线BOM\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"实物图\"\u003e实物图\u003c/h3\u003e","title":"基于esp32的手表"},{"content":" 这个是针对嘉立创打样的；\n单击菜单栏【文档】 \u0026raquo; 【绘制】，格式选择【Gerber】，各项设置如下截图所示。\n配置以下选项：\n然后点击绘制即可：\n点击生成钻孔文件：\n配置钻孔选项：\n然后生成钻孔文件和映射文件即可：\n将生成的文件打包就可以进行打样了；\n","permalink":"https://fan-pengfei.top/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这个是针对嘉立创打样的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e单击菜单栏【文档】 \u0026raquo; 【绘制】，格式选择【Gerber】，各项设置如下截图所示。\u003c/p\u003e\n\u003cp\u003e配置以下选项：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后点击绘制即可：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e点击生成钻孔文件：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e配置钻孔选项：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后生成钻孔文件和映射文件即可：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/kicad%E7%94%9F%E6%88%90gerber%E6%96%87%E4%BB%B6/img-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e将生成的文件打包就可以进行打样了；\u003c/strong\u003e\u003c/p\u003e","title":"Kicad生成Gerber文件"},{"content":" PID精进教程； 这篇文章的主要内容是如何改进初学者的PID控制器。文章中提到，初学者的PID控制器设计的目的是不规则地调用。这会导致两个问题：您无法从PID获得一致的行为，因为有时它被频繁调用，有时则没有。您需要进行额外的数学计算，以计算导数和积分，因为它们都依赖于时间的变化。作者提出了一种新的方法，称为“Proportional on Measurement”，可以解决这些问题。\n任何编写自己的 PID 算法的人都可以看看我是如何做的，并可以从中学到他们需要的东西。\n这将是一个艰难的过程，但我想我找到了一种不太痛苦的方式来解释我的代码。我将从所谓的“初学者的 PID”开始。然后我将逐步改进它，直到我们得到一个高效、健壮的 pid 算法。\nPID的开始 这是初学PID的人都知道的公式：\nOutput=K_pe(t)+K_t\\int{e(t)dt}+K_D{\\frac{d}{dt}}e(t) Where:e=Setpoint-Input\n公式(2)也就是指偏差e等于设定值和当前值的差别；\n根据这个公式，大多数都能写出下面的代码：\n/*working variables*/ unsigned long lastTime; double Input, Output, Setpoint; double errSum, lastErr; double kp, ki, kd; void Compute() { /*How long since we last calculated*/ unsigned long now = millis(); double timeChange = (double)(now - lastTime); /*Compute all the working error variables*/ double error = Setpoint - Input; errSum += (error * timeChange); double dErr = (error - lastErr) / timeChange; /*Compute PID Output*/ Output = kp * error + ki * errSum + kd * dErr; /*Remember some variables for next time*/ lastErr = error; lastTime = now; } void SetTunings(double Kp, double Ki, double Kd) { kp = Kp; ki = Ki; kd = Kd; } Compute()被定期或不定期地调用，并且运行良好。不过，这个系列并不是关于“工作得很好”。如果我们要将这段代码变成与工业 PID 控制器相当的东西，我们必须解决一些问题：\n采样时间（Sample Time）：PID 算法在定期被调用时最佳。如果算法运行间隔固定，我们还可以简化一些内部数学运算。 微分冲击（Derivative Kick）：微分冲击是一种在PID控制器中出现的现象，它会导致PID控制器的输出出现尖峰，从而影响系统的稳定性；不是什么大问题，很容易修复； 即时调节变化（On-The-Fly Tuning Changes）：也即在运行时进行调整；一个好的 PID 算法是一种可以在不影响内部工作的情况下改变调节参数的算法； 复位积分误差抑制（Reset Windup Mitigation）：复位积分误差抑制是一种用于控制系统中的PID控制器的技术，它可以防止PID控制器在某些情况下产生积分误差。； 开/关（自动/手动）（On/Off (Auto/Manual)）：在大多数应用中，有时会希望关闭 PID 控制器并手动调整输出，而不受控制器的干扰； 初始化（Initialization）：当控制器第一次打开时，我们需要一个“无扰动转换”。也就是说，我们不希望输出突然猛增到某个新值； 控制器方向（Controller Direction）：具体来说，控制器方向是指控制器的输出如何随着输入信号的变化而变化。如果控制器的输出随着输入信号的增加而增加，那么这个控制器就是正向控制器；如果控制器的输出随着输入信号的增加而减少，那么这个控制器就是反向控制器。它旨在确保用户使用正确的符号输入调整参数； 按比例测量（Proportional on Measurement）：按测量比例是指控制器输出的变化量与被控制量的变化量成比例。添加此功能可以更轻松地控制某些类型的过程；\n一旦我们解决了所有这些问题，我们就会有一个可靠的 PID 算法。我们还将拥有最新版本的 Arduino PID 库中使用的代码，这并非巧合。因此，无论您是想编写自己的算法，还是想了解 PID 库中发生的事情，我希望这对您有所帮助。让我们开始吧。\nPS：在的所有代码示例中，我使用的是double。因为在 Arduino 上，双精度与浮点数（单精度）相同。真正的双精度对 PID 来说太浪费了（精度过高）。如果你使用的语言是真正的双精度，我建议将所有双精度数更改为浮点数。\n采样时间 文章中提到，为了确保定期调用PID，可以指定每个周期调用计算函数。根据预先确定的采样时间，PID决定是否应立即计算或返回。一旦我们知道PID以恒定间隔进行评估，也可以简化微分和积分计算。\n问题 初学者的PID被设计为不定期地调用。这导致了两个问题：\n1、你不能从PID中获得一致的行为，因为它有时被频繁调用，有时不被调用。\n2、你需要做额外的数学运算来计算导数和积分，因为它们都依赖于时间的变化。\n解决方法 确保PID以固定的时间间隔被调用。我决定这样做的方法是指定每个周期调用计算函数。根据预先确定的采样时间，PID决定它是否应该计算或立即返回。\n一旦我们知道PID是以恒定的时间间隔被运行的，所以导数和积分的计算也可以被简化。\n代码 /*working variables*/ unsigned long lastTime; double Input, Output, Setpoint; double errSum, lastErr; double kp, ki, kd; int SampleTime = 1000; //1 sec void Compute() { unsigned long now = millis(); int timeChange = (now - lastTime); if(timeChange\u0026gt;=SampleTime) { /*Compute all the working error variables*/ double error = Setpoint - Input; errSum += error; double dErr = (error - lastErr); /*Compute PID Output*/ Output = kp * error + ki * errSum + kd * dErr; /*Remember some variables for next time*/ lastErr = error; lastTime = now; } } void SetTunings(double Kp, double Ki, double Kd) { double SampleTimeInSec = ((double)SampleTime)/1000; kp = Kp; ki = Ki * SampleTimeInSec; kd = Kd / SampleTimeInSec; } void SetSampleTime(int NewSampleTime) { if (NewSampleTime \u0026gt; 0) { double ratio = (double)NewSampleTime / (double)SampleTime; ki *= ratio; kd /= ratio; SampleTime = (unsigned long)NewSampleTime; } } 在第10行和第11行，算法现在通过判断来决定是否该计算了。另外，因为我们现在知道两个样本之间的时间是相同的，我们不需要不断地乘以时间变化。我们只需适当地调整Ki和Kd（第31行和第32行），结果在数学上是等同的，但更有效率。\n如果用户在操作过程中决定改变采样时间，Ki和Kd将需要重新调整以反映这种新的变化，这就是第39-42行的全部内容。\n另外请注意，我在第29行将采样时间转换为秒数。严格地说，这不是必须的，但允许用户以1/秒和s为单位输入Ki和Kd，而不是1/mS和mS。\n结果 上面的修改为我们做了3件事：\n1、无论调用Compute()的频率如何，PID算法将以固定的时间间隔被运行[第11行] 。\n2、因为有了时间减法[第10行]，当millis()返回0的时候就没有问题了。\n3、我们不需要再对时间变化进行乘除运算了。由于它是一个常数，我们可以将它从计算代码中移出[第15+16行]，并将其与PID常数放在一起[第31+32行]。在数学上，它的结果是一样的，但它节省了每次评估PID时的乘法和除法。\n关于中断 如果这个PID要进入一个微控制器，可以用一个非常好的理由来证明使用中断。SetSampleTime设置了中断频率，然后在时间到了时调用Compute。在这种情况下，就不需要第9-12、23和24行了。如果你打算在你的PID系统中这样做，那就去做吧! 不过要继续阅读这个系列。希望你仍能从后面的修改中得到一些好处。\n我没有使用中断的原因有三个：\n1、就这个系列而言，不是每个人都能使用中断。\n2、如果你想让它同时实现许多PID控制器，事情会变得很棘手。\n3、如果我说实话，我并没有想到这一点。Jimmie Rodgers在为我校对该系列文章时建议我这样做。我可能决定在未来版本的PID库中使用中断。\n微分冲击 问题 此修改将稍微调整导数项。目标是消除一种称为“微分冲击”的现象。\n上面的图片说明了这个问题。由于误差=设定-输入，设定点的任何变化都会导致误差的瞬时变化。这个变化的导数是无穷大（在实践中，由于dt不是0，它只是一个非常大的数字）。幸运的是，有一个简单的方法可以摆脱这种情况。\n解决办法 \\frac{dError}{dt}=\\frac{dSetpoint}{dt}-\\frac{dInput}{dt} 当设定值保持不变时：\n\\frac{dError}{dt}=-\\frac{dInput}{dt} 事实证明，误差的导数等于输入的负导数，但设定点变化时除外。这最终成为一个完美的解决方案。我们不是加(Kd误差的导数)，而是减(Kd输入的导数)。这就是所谓的使用 “测量导数”(Derivative on Measurement)。\n代码 /*working variables*/ unsigned long lastTime; double Input, Output, Setpoint; double errSum, lastInput; double kp, ki, kd; int SampleTime = 1000; //1 sec void Compute() { unsigned long now = millis(); int timeChange = (now - lastTime); if(timeChange\u0026gt;=SampleTime) { /*Compute all the working error variables*/ double error = Setpoint - Input; errSum += error; double dInput = (Input - lastInput); /*Compute PID Output*/ Output = kp * error + ki * errSum - kd * dInput; /*Remember some variables for next time*/ lastInput = Input; lastTime = now; } } void SetTunings(double Kp, double Ki, double Kd) { double SampleTimeInSec = ((double)SampleTime)/1000; kp = Kp; ki = Ki * SampleTimeInSec; kd = Kd / SampleTimeInSec; } void SetSampleTime(int NewSampleTime) { if (NewSampleTime \u0026gt; 0) { double ratio = (double)NewSampleTime / (double)SampleTime; ki *= ratio; kd /= ratio; SampleTime = (unsigned long)NewSampleTime; } } 这里的修改很容易。我们用-dInput代替+dError。我们现在不是记住最后的错误，而是记住最后的输入值。\n结果 下面是这些修改给我们带来的结果。请注意，输入的情况仍然是一样的。所以我们得到了同样的性能，但我们不会在每次设定点变化时发出巨大的输出尖峰。\n这可能是也可能不是一个大问题。这完全取决于你的应用对输出尖峰有多敏感。但在我看来，这也不需要更多的工作，为什么不把事情做对呢？\n","permalink":"https://fan-pengfei.top/posts/pid%E8%BF%9B%E9%98%B6%E6%95%99%E7%A8%8B/","summary":"\u003cblockquote\u003e\n\u003cp\u003ePID精进教程；\n这篇文章的主要内容是如何改进初学者的PID控制器。文章中提到，初学者的PID控制器设计的目的是不规则地调用。这会导致两个问题：您无法从PID获得一致的行为，因为有时它被频繁调用，有时则没有。您需要进行额外的数学计算，以计算导数和积分，因为它们都依赖于时间的变化。作者提出了一种新的方法，称为“Proportional on Measurement”，可以解决这些问题。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e任何编写自己的 PID 算法的人都可以看看我是如何做的，并可以从中学到他们需要的东西。\u003c/p\u003e\n\u003cp\u003e这将是一个艰难的过程，但我想我找到了一种不太痛苦的方式来解释我的代码。我将从所谓的“初学者的 PID”开始。然后我将逐步改进它，直到我们得到一个高效、健壮的 pid 算法。\u003c/p\u003e\n\u003ch2 id=\"pid的开始\"\u003ePID的开始\u003c/h2\u003e\n\u003cp\u003e这是初学PID的人都知道的公式：\u003c/p\u003e\n\u003cp\u003eOutput=K_pe(t)+K_t\\int{e(t)dt}+K_D{\\frac{d}{dt}}e(t)\nWhere:e=Setpoint-Input\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e公式(2)也就是指偏差e等于设定值和当前值的差别；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e根据这个公式，大多数都能写出下面的代码：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*working variables*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e lastTime;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e Input, Output, Setpoint;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e errSum, lastErr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e kp, ki, kd;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eCompute\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#75715e\"\u003e/*How long since we last calculated*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e now \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emillis\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e timeChange \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e)(now \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e lastTime);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#75715e\"\u003e/*Compute all the working error variables*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e error \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Setpoint \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e Input;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   errSum \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e (error \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e timeChange);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e dErr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (error \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e lastErr) \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e timeChange;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#75715e\"\u003e/*Compute PID Output*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   Output \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e kp \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e error \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e ki \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e errSum \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e kd \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e dErr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#75715e\"\u003e/*Remember some variables for next time*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   lastErr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e error;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   lastTime \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e now;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSetTunings\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e Kp, \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e Ki, \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e Kd)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   kp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Kp;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   ki \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Ki;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   kd \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Kd;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eCompute()\u003c/code\u003e被定期或不定期地调用，并且运行良好。不过，这个系列并不是关于“工作得很好”。如果我们要将这段代码变成与工业 PID 控制器相当的东西，我们必须解决一些问题：\u003c/p\u003e","title":"PID进阶教程"},{"content":" 这是一篇如何将LVGL移植到Arduino的教程(基于芯片ESP32 Pico D4)；\n软件版本 这次实验使用的lvgl版本是8.1.1，要先配置好tft_espi，确保显示正常；如果要使用触摸屏设备，在移植之前要确保能获取到触摸数据；\n工程配置 库安装 添加lvgl库 ，最好也添加lv_examples库，自带的例子虽然内容完全一样，但是并不能直接使用；\n库安装\n然后复制为lv_conf_template.h为lv_conf.h：\nlv_conf.h创建\n然后复制为lv_demo_conf_template.h为lv_demo_conf.h：\nlv_demo_conf.h创建\n配置文件 lv_conf.h 修改这几个地方；\n启动lv_conf.h：\n启动lv_conf\n设置色深，一般都是16：\n设置色深\n启动自定义时钟，不设置的话只会显示第一帧不动：\n启动自定义时钟\nLV_DPI_DEF 注意这里，虽然LVGL的作者说这个没这么重要，但他会严重影响到LVGL的动画效果，你应该进行DPI的手动计算，例如240x280分辨率1.69英寸的屏幕，那么 DPI为：\nLV_DPI_DEF =\\frac{\\sqrt{240*280} }{1.69} ≈ 153 LV_DPI_DEF配置\n也可以使能日志打印：\n使能日志打印\nlv_demo_conf.h 修改这几个地方；\n启动lv_demo_conf.h：\n启动Demo\n配置要运行的Demo：\nDemo选择\n自定义显示接口和外部输入接口 文件添加 在src文件夹下添加以下两个文件：\n自定义接口\n代码内容 my_lv_ports.cpp\n#include \u0026#34;my_lv_ports.h\u0026#34; #include \u0026#34;CST816T.h\u0026#34; // TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */ TFT_eSPI tft = TFT_eSPI(); /* TFT instance */ CST816T touch(19, 21, -1, 22); // sda, scl, rst, irq // /*Read the touchpad*/ void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) { bool FingerNum = 0; uint8_t gesture; uint16_t touchX, touchY; FingerNum = touch.getTouch(\u0026amp;touchX, \u0026amp;touchY, \u0026amp;gesture); if (FingerNum) { data-\u0026gt;state = LV_INDEV_STATE_REL; data-\u0026gt;point.x = touchX; data-\u0026gt;point.y = touchY; #if LV_USE_LOG != 0 Serial.printf(\u0026#34;Touch: x=%d y=%d mode=%d\\r\\n\u0026#34;, touchX, touchY, gesture); #endif FingerNum = 0; } else { data-\u0026gt;state = LV_INDEV_STATE_PR; } } /* Display flushing */ void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) { uint32_t w = (area-\u0026gt;x2 - area-\u0026gt;x1 + 1); uint32_t h = (area-\u0026gt;y2 - area-\u0026gt;y1 + 1); tft.setSwapBytes(true); // tft.pushImageDMA(area-\u0026gt;x1, area-\u0026gt;y1, w, h, (uint16_t *)\u0026amp;color_p-\u0026gt;full); tft.pushImage(area-\u0026gt;x1, area-\u0026gt;y1, w, h, (uint16_t *)\u0026amp;color_p-\u0026gt;full); // tft.startWrite(); // tft.setAddrWindow( area-\u0026gt;x1, area-\u0026gt;y1, w, h ); // tft.pushColors( ( uint16_t * )\u0026amp;color_p-\u0026gt;full, w * h, true ); // tft.endWrite(); lv_disp_flush_ready(disp); } #if LV_USE_LOG != 0 void my_print(const char *buf) { Serial.printf(\u0026#34;%s \\r\\n\u0026#34;, buf); } #endif void my_disp_init(void) { // 绘图缓冲初始化 // static lv_disp_draw_buf_t draw_buf; // static lv_color_t buf[screenWidth * 10]; // lv_disp_draw_buf_init(\u0026amp;draw_buf, buf, NULL, screenWidth * 10); static lv_disp_draw_buf_t draw_buf; static lv_color_t buf_2_1[screenWidth * 40]; /*A buffer for 10 rows*/ static lv_color_t buf_2_2[screenWidth * 40]; /*An other buffer for 10 rows*/ lv_disp_draw_buf_init(\u0026amp;draw_buf, buf_2_1, buf_2_2, screenWidth * 30); /*Initialize the display buffer*/ // TFT驱动初始化 tft.begin(); /* TFT init */ // tft.initDMA(); tft.setRotation(0); /* Landscape orientation, flipped */ // 设置LVGL显示设备 static lv_disp_drv_t disp_drv; lv_disp_drv_init(\u0026amp;disp_drv); /*Change the following line to your display resolution*/ disp_drv.hor_res = screenWidth; disp_drv.ver_res = screenHeight; disp_drv.flush_cb = my_disp_flush; disp_drv.draw_buf = \u0026amp;draw_buf; lv_disp_drv_register(\u0026amp;disp_drv); touch.begin(); // 设置LVGL输入设备（电阻屏） static lv_indev_drv_t indev_drv; lv_indev_drv_init(\u0026amp;indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = my_touchpad_read; lv_indev_drv_register(\u0026amp;indev_drv); // 设置LVGL串口输出设备（调试用） #if LV_USE_LOG != 0 lv_log_register_print_cb(my_print); #endif } my_lv_ports.h\n#ifndef _MY_LV_PORTS #define _MY_LV_PORTS #include #include /*Change to your screen resolution*/ const uint16_t screenWidth = 240; const uint16_t screenHeight = 280; void my_disp_init(void); // 挂载lvgl接口，设置buffer #endif 测试LVGL benchmark测试 main.cpp\n#include #include #include #include #include #include \u0026#34;user.h\u0026#34; #include #include \u0026#34;CST816T.h\u0026#34; void setup() { Serial.begin(115200); LCD_Light_Set(50);//LCD亮度设置 lv_init();//初始化LVGL my_disp_init();//初始化显示接口 // lv_demo_widgets(); lv_demo_benchmark(); // lv_demo_keypad_encoder(); // an encoder lv_demo_music(); // lv_demo_printer(); // lv_demo_stress(); Serial.println(\u0026#34;Setup done\u0026#34;); } void loop() { lv_timer_handler(); /* let the GUI do its work */ delay(1); } 运行结果；\n运行结果\n可以看到LVGL正常运行；\n","permalink":"https://fan-pengfei.top/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一篇如何将LVGL移植到Arduino的教程(基于芯片ESP32 Pico D4)；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"软件版本\"\u003e软件版本\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e这次实验使用的lvgl版本是8.1.1，要先配置好tft_espi，确保显示正常；如果要使用触摸屏设备，在移植之前要确保能获取到触摸数据；\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"工程配置\"\u003e工程配置\u003c/h2\u003e\n\u003ch3 id=\"库安装\"\u003e库安装\u003c/h3\u003e\n\u003cp\u003e添加lvgl库 ，最好也添加lv_examples库，自带的例子虽然内容完全一样，但是并不能直接使用；\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e库安装\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e然后复制为lv_conf_template.h为lv_conf.h：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003elv_conf.h创建\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e然后复制为lv_demo_conf_template.h为lv_demo_conf.h：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003elv_demo_conf.h创建\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"配置文件\"\u003e配置文件\u003c/h3\u003e\n\u003ch4 id=\"lv_confh\"\u003elv_conf.h\u003c/h4\u003e\n\u003cp\u003e修改这几个地方；\u003c/p\u003e\n\u003cp\u003e启动lv_conf.h：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-4.png\"\u003e\n\u003cstrong\u003e启动lv_conf\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e设置色深，一般都是16：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-5.png\"\u003e\n\u003cstrong\u003e设置色深\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e启动自定义时钟，不设置的话只会显示第一帧不动：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-6.png\"\u003e\n\u003cstrong\u003e启动自定义时钟\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eLV_DPI_DEF 注意这里，虽然LVGL的作者说这个没这么重要，但他会严重影响到LVGL的动画效果，你应该进行DPI的手动计算，例如240x280分辨率1.69英寸的屏幕，那么 DPI为：\u003c/p\u003e\n\u003cp\u003eLV_DPI_DEF =\\frac{\\sqrt{240*280} }{1.69} ≈ 153\n\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-7.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLV_DPI_DEF配置\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e也可以使能日志打印：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e使能日志打印\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"lv_demo_confh\"\u003elv_demo_conf.h\u003c/h4\u003e\n\u003cp\u003e修改这几个地方；\u003c/p\u003e\n\u003cp\u003e启动lv_demo_conf.h：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e启动Demo\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e配置要运行的Demo：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-10.png\"\u003e\n\u003cstrong\u003eDemo选择\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"自定义显示接口和外部输入接口\"\u003e自定义显示接口和外部输入接口\u003c/h2\u003e\n\u003ch3 id=\"文件添加\"\u003e文件添加\u003c/h3\u003e\n\u003cp\u003e在src文件夹下添加以下两个文件：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E5%9F%BA%E4%BA%8Earduino%E7%9A%84lvgl%E7%A7%BB%E6%A4%8D/img-11.png\"\u003e\n\u003cstrong\u003e自定义接口\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"代码内容\"\u003e代码内容\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003emy_lv_ports.cpp\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;my_lv_ports.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;CST816T.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTFT_eSPI tft \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTFT_eSPI\u003c/span\u003e();     \u003cspan style=\"color:#75715e\"\u003e/* TFT instance */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCST816T \u003cspan style=\"color:#a6e22e\"\u003etouch\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e19\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e21\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e22\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e// sda, scl, rst, irq\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// /*Read the touchpad*/\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emy_touchpad_read\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003elv_indev_drv_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eindev_driver, \u003cspan style=\"color:#66d9ef\"\u003elv_indev_data_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003edata)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e FingerNum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e gesture;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e touchX, touchY;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    FingerNum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e touch.\u003cspan style=\"color:#a6e22e\"\u003egetTouch\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003etouchX, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003etouchY, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003egesture);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (FingerNum)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        data\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003estate \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LV_INDEV_STATE_REL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        data\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003epoint.x \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e touchX;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        data\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003epoint.y \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e touchY;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#if LV_USE_LOG != 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        Serial.\u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Touch: x=%d y=%d mode=%d\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, touchX, touchY, gesture);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        FingerNum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        data\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003estate \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LV_INDEV_STATE_PR;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/* Display flushing */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emy_disp_flush\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003elv_disp_drv_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003edisp, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_area_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003earea,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                   \u003cspan style=\"color:#66d9ef\"\u003elv_color_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ecolor_p)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e w \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ex2 \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ex1 \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e h \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ey2 \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ey1 \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tft.\u003cspan style=\"color:#a6e22e\"\u003esetSwapBytes\u003c/span\u003e(true);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.pushImageDMA(area-\u0026gt;x1, area-\u0026gt;y1, w, h, (uint16_t *)\u0026amp;color_p-\u0026gt;full);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tft.\u003cspan style=\"color:#a6e22e\"\u003epushImage\u003c/span\u003e(area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ex1, area\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ey1, w, h, (\u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ecolor_p\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003efull);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.startWrite();\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.setAddrWindow( area-\u0026gt;x1, area-\u0026gt;y1, w, h );\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.pushColors( ( uint16_t * )\u0026amp;color_p-\u0026gt;full, w * h, true );\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.endWrite();\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_disp_flush_ready\u003c/span\u003e(disp);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#if LV_USE_LOG != 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emy_print\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ebuf)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Serial.\u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%s \u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, buf);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emy_disp_init\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 绘图缓冲初始化\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//   static lv_disp_draw_buf_t draw_buf;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//   static lv_color_t buf[screenWidth * 10];\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//   lv_disp_draw_buf_init(\u0026amp;draw_buf, buf, NULL, screenWidth * 10);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_disp_draw_buf_t\u003c/span\u003e draw_buf;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_color_t\u003c/span\u003e buf_2_1[screenWidth \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e]; \u003cspan style=\"color:#75715e\"\u003e/*A buffer for 10 rows*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_color_t\u003c/span\u003e buf_2_2[screenWidth \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e]; \u003cspan style=\"color:#75715e\"\u003e/*An other buffer for 10\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    rows*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_disp_draw_buf_init\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003edraw_buf, buf_2_1, buf_2_2,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                          screenWidth \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/*Initialize\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e                          the display buffer*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// TFT驱动初始化\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tft.\u003cspan style=\"color:#a6e22e\"\u003ebegin\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e/* TFT init */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// tft.initDMA();\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tft.\u003cspan style=\"color:#a6e22e\"\u003esetRotation\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/* Landscape orientation, flipped */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 设置LVGL显示设备\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_disp_drv_t\u003c/span\u003e disp_drv;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_disp_drv_init\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003edisp_drv);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/*Change the following line to your display resolution*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    disp_drv.hor_res \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e screenWidth;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    disp_drv.ver_res \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e screenHeight;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    disp_drv.flush_cb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e my_disp_flush;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    disp_drv.draw_buf \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003edraw_buf;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_disp_drv_register\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003edisp_drv);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    touch.\u003cspan style=\"color:#a6e22e\"\u003ebegin\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 设置LVGL输入设备（电阻屏）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elv_indev_drv_t\u003c/span\u003e indev_drv;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_indev_drv_init\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eindev_drv);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    indev_drv.type \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LV_INDEV_TYPE_POINTER;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    indev_drv.read_cb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e my_touchpad_read;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_indev_drv_register\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eindev_drv);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 设置LVGL串口输出设备（调试用）\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#if LV_USE_LOG != 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003elv_log_register_print_cb\u003c/span\u003e(my_print);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003emy_lv_ports.h\u003c/code\u003e\u003c/p\u003e","title":"基于Arduino的LVGL移植"},{"content":" 有空就会做的事；\n记得翻译一下这篇文章 PID精进教程\n完成否？否\n创建于：2023年4月4日20:32:45 完成于： 备注：\n","permalink":"https://fan-pengfei.top/posts/todo/","summary":"\u003cblockquote\u003e\n\u003cp\u003e有空就会做的事；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"记得翻译一下这篇文章\"\u003e记得翻译一下这篇文章\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/\"\u003ePID精进教程\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e完成否？\u003cstrong\u003e否\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e创建于：2023年4月4日20:32:45\n完成于：\n备注：\u003c/p\u003e\n\u003c/blockquote\u003e","title":"TODO"},{"content":" 我这两天又翻出来了之前做的一个ESP32-Pico-D4的板子，之前测是发现会无限重启，结果现在再试，下进去一个新的程序就能用了，这里记录一下开发中遇到的问题；\nPico D4介绍 这个芯片资源挺多的，最主要的是内嵌了晶振和Flash，在板子布线方面容易了许多；\n芯片特性\n原理图设计 原理图设计参考 原理图参考\n实物图片\n重启问题 ESP32 启动时会有如下打印:\nrst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) 其中 rst 简单说明如下:\nPRO APP 源 复位方式 注释\n0x01 0x01 芯片上电复位 系统复位 0x10 0x10 RWDT 系统复位 系统复位 详见 ESP32 技术参考手册 WDT 章节\n0x0F 0x0F 欠压复位 系统复位 详见 ESP32 技术参考手册 Power Management 章节\n0x03 0x03 软件系统复位 内核复位 配置 RTC_CNTL_SW_SYS_RST 寄存器\n0x05 0x05 Deep Sleep Reset 内核复位 详见 ESP32 技术参考手册 Power Management 章节\n0x07 0x07 MWDT0 全局复位 内核复位 详见 ESP32 技术参考手册 WDT 章节\n0x08 0x08 MWDT1 全局复位 内核复位 详见 ESP32 技术参考手册 WDT 章节\n0x09 0x09 RWDT 内核复位 内核复位 详见 ESP32 技术参考手册 WDT 章节\n0x0B MWDT0 CPU 复位 CPU 复位 详见 ESP32 技术参考手册 WDT 章节\n0x0C 软件 CPU 复位 CPU 复位 配置 RTC_CNTL_SW_APPCPU_RST 寄存器\n0x0B MWDT1 CPU 复位 CPU 复位 详见 ESP32 技术参考手册 WDT 章节\n0x0C 软件 CPU 复位 CPU 复位 配置 RTC_CNTL_SW_APPCPU_RST 寄存器\n0x0D 0x0D RWDT CPU 复位 CPU 复位 详见 ESP32 技术参考手册 WDT 章节\n0xE PRO CPU 复位 CPU 复位 表明 PRO CPU 能够通过配置 DPORT_APPCPU_RESETTING 寄存器单独复位 APP CPU\nboot mode说明如下：\nESP32 上电时会判断 strapping 管脚的状态, 并决定 boot mode；\n例如常见的两种上电打印:\n下载固件模式：\nrst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2)) 芯片运行模式：\nrst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) boot 值由 Strapping 管脚 的 5 位值 [MTDI, GPIO0, GPIO2, MTDO, GPIO5] 共同决定。\n下面是关于 Stripping 管脚 相关说明：\n**Strapping 管脚 **\n固件可以通过配置一些寄存器比特位，在启动后改变“内置 LDO (VDD_SDIO) 电压”和“SDIO 从机信号输入输出时序”的设定； ESP32-PICO-D4 集成的外部 SPI flash 工作电压为 3.3 V，因此在上电复位过程中需保持 Strapping 管脚 MTDI 为低电平； 注意供电不足时，打开WiFi会导致重启；\nIO引脚分配 绿色突出显示的管脚可以使用。黄色突出显示的可以使用，但您需要注意，因为它们可能在启动时有意外行为。不建议将红色突出显示的管脚用作输入或输出：\n**IO引脚分配 **\n外设引脚分配 ESP32的大多数外设信号都直接连接到其专用的IO_MUX引脚。但是，也可以使用GPIO矩阵将信号转换到任何其他可用的引脚。如果至少一个信号通过GPIO矩阵转换，则所有信号都将通过GPIO矩阵转换。\nGPIO矩阵引入了转换灵活性，但也带来了以下缺点：\n增加了MISO信号的输入延迟，这更可能违反MISO设置时间。如果SPI需要高速运行，请使用专用的IO_MUX引脚。\n如果使用IO_MUX引脚，则允许信号的时钟频率最多为40 MHz，而时钟频率最高为80 MHz。\nADC（模数转换器）和DAC（数模转换器）功能分配给特定的静态引脚。但是，您可以决定哪些管脚是UART、I2C、SPI、PWM等，您只需要在代码中分配它们。这是可能的，因为ESP32芯片的多路复用功能。\nADC **ADC引脚分配 **\nDAC **DAC引脚分配 **\n触摸传感器 **触摸传感器引脚分配 **\nJTAG **JTAG引脚分配 **\nSD/SDIO/MMC 主机控制器 **SD/SDIO/MMC 主机控制器引脚分配 **\n电机PWM **电机PWM引脚分配 **\nSDIO/SPI 从机控制器 **SDIO/SPI 从机控制器引脚分配 **\nUART **UART引脚分配 **\nI2C ESP32有两个I2C信道，任何管脚都可以设置为SDA或SCL。将ESP32与Arduino IDE一起使用时，默认I2C引脚为： GPIO 21（SDA） GPIO 22（SCL）\n如果要使用其他管脚，在使用导线库时，只需调用：Wire.begin(SDA, SCL);\n**I2C引脚分配 **\nLED PWM ESP32 LED PWM控制器有16个独立信道，可以配置为生成具有不同特性的PWM信号。所有可以作为输出的管脚都可以用作PWM管脚（GPIOs 34到39不能产生PWM）。 要设置脉冲宽度调制信号，需要在代码中定义这些参数： 信号频率； 占空比； 脉宽调制信道； 要输出信号的GPIO。\n**LED PWM引脚分配 **\nI2S **I2S引脚分配 **\n红外遥控器 **红外遥控器引脚分配 **\n通用SPI **通用SPI引脚分配 **\n并行SPI **并行SPI引脚分配 **\n默认情况下，可以用的SPI的引脚映射是：\n引脚对应的GPIO SPI2 SPI3\nCS0 * 15 5\nSCLK 14 18\nMISO 12 19\nMOSI 13 23\nQUADWP 2 22\nQUADHD 4 21\n仅连接到总线的第一个设备可以使用CS0引脚；\nEMAC **EMAC引脚分配 **\n脉冲计数器 **脉冲计数器引脚分配 **\nTWAI **TWAI引脚分配 **\n中断 所有GPIO都可以配置为中断；\n板子实物 正面\n反面\n看看那个FPC焊盘被我刮得光亮，就知道肯定有点问题，再看屏幕方向(???)；\n是的，我犯了一个很严重的错误，把LCD引脚的序号给搞反了；\n好消息是：芯片功能都正常，屏幕显示，触摸，编码器开关等等都没问题；\n坏消息是：屏幕方向搞反了，根本没法正常用，还得重新打板；\n\u0026#x1f622;！！！\n","permalink":"https://fan-pengfei.top/posts/esp32-pico-d4%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e我这两天又翻出来了之前做的一个ESP32-Pico-D4的板子，之前测是发现会无限重启，结果现在再试，下进去一个新的程序就能用了，这里记录一下开发中遇到的问题；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"pico-d4介绍\"\u003ePico D4介绍\u003c/h2\u003e\n\u003cp\u003e这个芯片资源挺多的，最主要的是内嵌了晶振和Flash，在板子布线方面容易了许多；\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/esp32-pico-d4%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e芯片特性\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"原理图设计\"\u003e原理图设计\u003c/h2\u003e\n\u003ch3 id=\"原理图设计参考\"\u003e原理图设计参考\u003c/h3\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/esp32-pico-d4%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e原理图参考\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/esp32-pico-d4%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e实物图片\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"重启问题\"\u003e重启问题\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eESP32\u003c/code\u003e 启动时会有如下打印:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中 \u003ccode\u003erst\u003c/code\u003e 简单说明如下:\u003c/p\u003e\n\u003cp\u003ePRO\nAPP\n源\n复位方式\n注释\u003c/p\u003e\n\u003ch2 id=\"系统复位\"\u003e0x01\n0x01\n芯片上电复位\n系统复位\u003c/h2\u003e\n\u003cp\u003e0x10\n0x10\nRWDT 系统复位\n系统复位\n详见 ESP32 技术参考手册 WDT 章节\u003c/p\u003e\n\u003cp\u003e0x0F\n0x0F\n欠压复位\n系统复位\n详见 ESP32 技术参考手册 Power Management 章节\u003c/p\u003e\n\u003cp\u003e0x03\n0x03\n软件系统复位\n内核复位\n配置 RTC_CNTL_SW_SYS_RST 寄存器\u003c/p\u003e\n\u003cp\u003e0x05\n0x05\nDeep Sleep Reset\n内核复位\n详见 ESP32 技术参考手册 Power Management 章节\u003c/p\u003e\n\u003cp\u003e0x07\n0x07\nMWDT0 全局复位\n内核复位\n详见 ESP32 技术参考手册 WDT 章节\u003c/p\u003e","title":"ESP32-PICO-D4开发记录"},{"content":" docker初体验；\ndocker常用命令 #docker常用命令: 1. 查看容器: docker ps 2. 查看镜像: docker images 3. 删除容器: docker rm 容器name 4. 删除镜像: docker rmi 镜像id 5. 创建容器: docker run --name dockermysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=654321 mysql:5.7.23 6. 启动容器: docker start 容器name #docker start dockermysql 7. 重启容器: docker restart dockermysql 8. 停止容器: docker stop dockermysql 9. 容器交互: docker exec -it dockermysql bash #或 docker attach dockermysql 10.退出交互: Ctrl+P,Ctrl+Q(Ctrl键一直保持按下) 11.设置开机自启: systemctl enable docker 12.容器设置自启,update命令: docker update --restart=always aeccnginx 13.启动docker systemctl start docker 14.重启docker systemctl restart docker ","permalink":"https://fan-pengfei.top/posts/docker%E5%88%9D%E5%B0%9D%E8%AF%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003edocker初体验；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"docker常用命令\"\u003edocker常用命令\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e#docker常用命令:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1. 查看容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker ps\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e2. 查看镜像:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker images\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e3. 删除容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker rm 容器name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4. 删除镜像:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker rmi 镜像id\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e5. 创建容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker run --name dockermysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=654321 mysql:5.7.23\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e6. 启动容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker start 容器name #docker start dockermysql\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e7. 重启容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker restart dockermysql\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e8. 停止容器:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker stop dockermysql\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e9. 容器交互:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker exec -it dockermysql bash #或 docker attach dockermysql\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e10.退出交互:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      Ctrl+P,Ctrl+Q(Ctrl键一直保持按下)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e11.设置开机自启:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      systemctl enable docker\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e12.容器设置自启,update命令:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      docker update --restart=always aeccnginx\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e13.启动docker\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      systemctl start docker\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e14.重启docker\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      systemctl restart docker\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"docker初尝试"},{"content":" 在做毕设过程中，用到了八个相同的传感器，传感器的通信协议都是一样的，固然直接搞八个差不多的文件来驱动传感器是没问题的，但是有一种更简洁的方式，那就是使用C++，来复用通信程序；\n参考：https://zhuanlan.zhihu.com/p/115068898\n环境说明 使用的单片机芯片是STM32F103C6T6；\nKeil编译器版本为AC6；\nKeil的配置如下：\n然后是将C++源文件配对的头文件改为使用C++编译器进行编译；\n源码编写 注意把main文件的后缀也改为.cpp，否则会出错；\n头文件： /* * @Author : fan-pengfei 2253770787@qq.com * @Date : 2023-03-07 13:43:38 * @LastEditors : fan-pengfei 2253770787@qq.com * @LastEditTime : 2023-03-07 14:41:29 * @FilePath : \\Core\\Inc\\ds18b20.h * @Description : * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. */ #ifndef __DS18B20_H #define __DS18B20_H #include \u0026#34;main.h\u0026#34; #include \u0026#34;tim.h\u0026#34; class DS18B20_Class { private: GPIO_TypeDef *BSP_DS18B20_PORT; uint16_t BSP_DS18B20_PIN; float *Temp_Sum; uint8_t LOCATION; public: DS18B20_Class(GPIO_TypeDef *BSP_DS18B20_PORT, uint16_t BSP_DS18B20_PIN, uint8_t LOCATION, float *Temp_data); void delay_us(uint16_t us); GPIO_PinState DS18B20_IN(void); void DS18B20_OUT_1(void); void DS18B20_OUT_0(void); void DS18B20_Mode_OUT_PP(void); void DS18B20_Mode_IN_NP(void); void DS18B20_Reset(void); uint8_t DS18B20_Presence(void); uint8_t DS18B20_ReadBit(void); uint8_t DS18B20_ReadByte(void); void DS18B20_WriteByte(uint8_t dat); void DS18B20_ReadId(uint8_t *ds18b20_id); void DS18B20_SkipRom(void); void DS18B20_MatchRom(void); uint8_t Init(void); float DS18B20_GetTemp_SkipRom(void); float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id); void Start_Convert(void); void Get_Data(uint8_t ds18b20_id[64][8]); }; extern \u0026#34;C\u0026#34; { void User_DS18B20_Init(void); void User_Start_Convert(void); void User_Get_Data(void); } #endif 源文件： /* * @Author : fan-pengfei 2253770787@qq.com * @Date : 2023-03-07 13:43:26 * @LastEditors : fan-pengfei 2253770787@qq.com * @LastEditTime : 2023-03-07 16:41:42 * @FilePath : \\Core\\Src\\ds18b20.cpp * @Description : * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. */ #include \u0026#34;ds18b20.h\u0026#34; #include \u0026#34;main.h\u0026#34; #include \u0026#34;DS18B20_ID.h\u0026#34; extern float Temp_Sum[64]; void DS18B20_Class::delay_us(uint16_t us) { tim_delay_us(us); // uint32_t delay; // delay = (1600 * us); // while (delay--) // { // } } GPIO_PinState DS18B20_Class::DS18B20_IN(void) { return HAL_GPIO_ReadPin(BSP_DS18B20_PORT, BSP_DS18B20_PIN); } void DS18B20_Class::DS18B20_OUT_1(void) { HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_SET); } void DS18B20_Class::DS18B20_OUT_0(void) { HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_RESET); } /** * @brief DS18B20 输出模式 */ void DS18B20_Class::DS18B20_Mode_OUT_PP(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BSP_DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(BSP_DS18B20_PORT, \u0026amp;GPIO_InitStruct); } /** * @brief DS18B20 输入模式 */ void DS18B20_Class::DS18B20_Mode_IN_NP(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BSP_DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(BSP_DS18B20_PORT, \u0026amp;GPIO_InitStruct); } /** * @brief 主机给从机发送复位脉冲 */ void DS18B20_Class::DS18B20_Reset(void) { DS18B20_Mode_OUT_PP(); // 主机输出 DS18B20_OUT_0(); // 主机至少产生 480us 的低电平复位信号 delay_us(750); DS18B20_OUT_1(); // 主机在产生复位信号后，需将总线拉高 // 从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲 delay_us(15); } /** * @brief 检测从机给主机返回的存在脉冲 * @return 0：成功\t1：失败 */ uint8_t DS18B20_Class::DS18B20_Presence(void) { uint8_t pulse_time = 0; DS18B20_Mode_IN_NP(); // 主机设为输入 while (DS18B20_IN() \u0026amp;\u0026amp; (pulse_time = 100) { return 1; } else { pulse_time = 0; } while (!(DS18B20_IN()) \u0026amp;\u0026amp; pulse_time = 240) { return 1; } else { return 0; } } /** * @brief 从DS18B20读取一个bit */ uint8_t DS18B20_Class::DS18B20_ReadBit(void) { uint8_t dat; DS18B20_Mode_OUT_PP(); // 读 0 和读 1 的时间至少要大于 60 us DS18B20_OUT_0(); // 读时间的起始：必须由主机产生 \u0026gt; 1us \u0026gt; 1; // 写 0 和写 1 的时间至少要大于60us if (testb) // 当前位写 1 { DS18B20_OUT_0(); delay_us(5); // 拉低发送写时段信号 DS18B20_OUT_1(); // 读取电平时间保持高电平 delay_us(65); } else // 当前位写 0 { DS18B20_OUT_0(); // 拉低发送写时段信号 delay_us(70); // 读取电平时间保持低电平 DS18B20_OUT_1(); delay_us(2); // 恢复时间 } } } /** * @brief 跳过匹配 DS18B20 ROM */ void DS18B20_Class::DS18B20_SkipRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0XCC); /* 跳过 ROM */ } /** * @brief 执行匹配 DS18B20 ROM */ void DS18B20_Class::DS18B20_MatchRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0X55); /* 匹配 ROM */ } DS18B20_Class::DS18B20_Class(GPIO_TypeDef *BSP_DS18B20_PORT, uint16_t BSP_DS18B20_PIN, uint8_t LOCATION, float *Temp_data) { DS18B20_Class::BSP_DS18B20_PORT = BSP_DS18B20_PORT; DS18B20_Class::BSP_DS18B20_PIN = BSP_DS18B20_PIN; DS18B20_Class::LOCATION = LOCATION; DS18B20_Class::Temp_Sum = Temp_data; } uint8_t DS18B20_Class::Init(void) { DS18B20_Mode_OUT_PP(); DS18B20_OUT_1(); DS18B20_Reset(); return DS18B20_Presence(); } /** * 存储的温度是16 位的带符号扩展的二进制补码形式 * 当工作在12位分辨率时，其中5个符号位，7个整数位，4个小数位 * * |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----| * 低字节 | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) | * * * |-----符号位：0-\u0026gt;正 1-\u0026gt;负-------|-----------整数-----------| * 高字节 | s | s | s | s | s | 2^6 | 2^5 | 2^4 | * * * 温度 = 符号位 + 整数 + 小数*0.0625 */ /** * @brief 在跳过匹配 ROM 情况下获取 DS18B20 温度值 * @param 无 * @retval 温度值 */ float DS18B20_Class::DS18B20_GetTemp_SkipRom(void) { uint8_t tpmsb, tplsb; int16_t s_tem; float f_tem; DS18B20_SkipRom(); DS18B20_WriteByte(0X44); /* 开始转换 */ DS18B20_SkipRom(); DS18B20_WriteByte(0XBE); /* 读温度值 */ tplsb = DS18B20_ReadByte(); tpmsb = DS18B20_ReadByte(); s_tem = tpmsb 要注意其中的`extern \u0026#34;C\u0026#34;`的用法； ### Main中调用： \u0026gt; 注意main文件也是cpp后缀； ```c++ void main(void) { User_DS18B20_Init(); while (1) { User_Start_Convert(); HAL_Delay(200); // 更新速率为200ms，等待转换结束 User_Get_Data(); int i = 0; while (i 使用C编写 ![5a011b38ca2d1e989de04f32bb8c0e5](img-5.png) 使用C++编写 ![4dbfdf38abe4f0d85cc7f98e4133007](img-6.png) **C:** Total RO Size (Code + RO Data) 14016 (13.69kB) Total RW Size (RW Data + ZI Data) 2040 (1.99kB) Total ROM Size (Code + RO Data + RW Data) 14464 ( 14.13kB) **C++:** Total RO Size (Code + RO Data) 11780 (11.50kB) Total RW Size (RW Data + ZI Data) 2864 (2.80kB) Total ROM Size (Code + RO Data + RW Data) 12220 ( 11.93kB) \u0026gt; 相比较而言C用的ROM比较多一些，C++用的RAM比较多一些； **可见在某些情况下，使用C++编写代码可以有效缩减代码体积，且代码更易懂；** ### microlib 使用C++编译的话，就没法再使用MicroLIB，因为MicroLIB为非标准的精简库，会与标准C++产生冲突； ### 中断服务程序 如果中断服务程序是异常的，因为stm32的中断入口矢量是按C的方式进入的，因此需要在整个文档的头部和末尾加上extern “C”{}用大括号把整个代码段扩住，这样中断就可以正常的进入了； ### Cubemx Cubemx在生成头文件中已经加入了： ```c #ifdef __cplusplus extern \u0026#34;C\u0026#34; { #endif #ifdef __cplusplus } #endif 所以在不是其生成的文件中要注意extern \u0026quot;C\u0026quot;的使用；\n当需要使用有C++特性的头文档时可以直接用cpp后缀，没有用到C++特性的可以C后缀也可以以CPP后缀了；\nprintf的实现 半主机模式是ARM的一种机制，实现将来ARM应用程序代码的输入/输出请求传送至运行着调试器的主机。例如设置使用半主机模式下的ARM应用程序，可以使用printf()和scanf()来使用主机的显示器和键盘，而不需要在ARM系统上搭配显示器和键盘。 半主机通过一组定义好的软件指令(如SVC)来实现的，这些指令在程序控制下产生异常，ARM应用程序调用半主机对应的异常处理函数，然后调试代理处理该异常。\n第二段话感觉理解起来有点模糊，但是第一段还是懂它在讲什么的。一般的ARM应用程序中并不需要半主机操作，在这里为确保ARM应用程序中没有链接MicroLib的半主机相关函数，我们要取消ARM的半主机工作模式。\n实现代码：\n在工程中加上如下代码：\n// 取消ARM的半主机工作模式 __asm(\u0026#34;.global __use_no_semihosting\u0026#34;); // 用于AC6编译器 // #pragma import(__use_no_semihosting)//用于AC5编译器 FILE __stdout; void _sys_exit(int x) { x = x; } int fputc(int ch, FILE *f) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)\u0026amp;ch, 1, 0xFFFF); return ch; } 然后就可以愉快的使用printf打印日志了；\n","permalink":"https://fan-pengfei.top/posts/%E4%BD%BF%E7%94%A8c%E5%92%8Cc++%E8%BF%9B%E8%A1%8C%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/","summary":"\u003cblockquote\u003e\n\u003cp\u003e在做毕设过程中，用到了八个相同的传感器，传感器的通信协议都是一样的，固然直接搞八个差不多的文件来驱动传感器是没问题的，但是有一种更简洁的方式，那就是使用C++，来复用通信程序；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e参考：\u003ca href=\"https://zhuanlan.zhihu.com/p/115068898\"\u003ehttps://zhuanlan.zhihu.com/p/115068898\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"环境说明\"\u003e环境说明\u003c/h2\u003e\n\u003cp\u003e使用的单片机芯片是STM32F103C6T6；\u003c/p\u003e\n\u003cp\u003eKeil编译器版本为AC6；\u003c/p\u003e\n\u003cp\u003eKeil的配置如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"9a6b1374da89853d9d500b3792554de\" loading=\"lazy\" src=\"/posts/%E4%BD%BF%E7%94%A8c%E5%92%8Cc++%E8%BF%9B%E8%A1%8C%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"4e46e254a889e95aa63b9372544005a\" loading=\"lazy\" src=\"/posts/%E4%BD%BF%E7%94%A8c%E5%92%8Cc++%E8%BF%9B%E8%A1%8C%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后是将C++源文件配对的头文件改为使用C++编译器进行编译；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"39b78c1a29418c47f0dfe5754d01e79\" loading=\"lazy\" src=\"/posts/%E4%BD%BF%E7%94%A8c%E5%92%8Cc++%E8%BF%9B%E8%A1%8C%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"cfa936edc4b94fe0bf76455e5dd19e2\" loading=\"lazy\" src=\"/posts/%E4%BD%BF%E7%94%A8c%E5%92%8Cc++%E8%BF%9B%E8%A1%8C%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/img-4.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"源码编写\"\u003e源码编写\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注意把main文件的后缀也改为.cpp，否则会出错；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"头文件\"\u003e头文件：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Author       : fan-pengfei 2253770787@qq.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Date         : 2023-03-07 13:43:38\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @LastEditors  : fan-pengfei 2253770787@qq.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @LastEditTime : 2023-03-07 14:41:29\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @FilePath     : \\Core\\Inc\\ds18b20.h\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Description  :\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ifndef __DS18B20_H\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define __DS18B20_H\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;main.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;tim.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Class\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_TypeDef \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eBSP_DS18B20_PORT;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e BSP_DS18B20_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eTemp_Sum;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e LOCATION;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Class(GPIO_TypeDef \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eBSP_DS18B20_PORT, \u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e BSP_DS18B20_PIN, \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e LOCATION, \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eTemp_data);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edelay_us\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e us);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_PinState \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_IN\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_OUT_1\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_OUT_0\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Mode_OUT_PP\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Mode_IN_NP\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Reset\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Presence\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_ReadBit\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_ReadByte\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_WriteByte\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e dat);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_ReadId\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eds18b20_id);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_SkipRom\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_MatchRom\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eInit\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_GetTemp_SkipRom\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_GetTemp_MatchRom\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eds18b20_id);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eStart_Convert\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eGet_Data\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e ds18b20_id[\u003cspan style=\"color:#ae81ff\"\u003e64\u003c/span\u003e][\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eextern\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eUser_DS18B20_Init\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eUser_Start_Convert\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eUser_Get_Data\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"源文件\"\u003e源文件：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Author       : fan-pengfei 2253770787@qq.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Date         : 2023-03-07 13:43:26\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @LastEditors  : fan-pengfei 2253770787@qq.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @LastEditTime : 2023-03-07 16:41:42\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @FilePath     : \\Core\\Src\\ds18b20.cpp\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @Description  :\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;ds18b20.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;main.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;DS18B20_ID.h\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eextern\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e Temp_Sum[\u003cspan style=\"color:#ae81ff\"\u003e64\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003edelay_us(\u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e us)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tim_delay_us(us);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// uint32_t delay;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// delay = (1600 * us);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// while (delay--)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// {\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// }\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eGPIO_PinState DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_IN(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_ReadPin\u003c/span\u003e(BSP_DS18B20_PORT, BSP_DS18B20_PIN);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_OUT_1(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_SET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_OUT_0(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_RESET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief DS18B20 输出模式\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_Mode_OUT_PP(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitTypeDef GPIO_InitStruct;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Pin \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BSP_DS18B20_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Mode \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_MODE_OUTPUT_PP;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Speed \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_SPEED_FREQ_HIGH;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    HAL_GPIO_Init(BSP_DS18B20_PORT, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eGPIO_InitStruct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief DS18B20 输入模式\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_Mode_IN_NP(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitTypeDef GPIO_InitStruct;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Pin \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BSP_DS18B20_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Mode \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_MODE_INPUT;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    GPIO_InitStruct.Pull \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_NOPULL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    HAL_GPIO_Init(BSP_DS18B20_PORT, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eGPIO_InitStruct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief 主机给从机发送复位脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_Reset(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Mode_OUT_PP(); \u003cspan style=\"color:#75715e\"\u003e// 主机输出\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_0();       \u003cspan style=\"color:#75715e\"\u003e// 主机至少产生 480us 的低电平复位信号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    delay_us(\u003cspan style=\"color:#ae81ff\"\u003e750\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_1(); \u003cspan style=\"color:#75715e\"\u003e// 主机在产生复位信号后，需将总线拉高\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    delay_us(\u003cspan style=\"color:#ae81ff\"\u003e15\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  检测从机给主机返回的存在脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @return 0：成功\t\t1：失败\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_Presence(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Mode_IN_NP();                      \u003cspan style=\"color:#75715e\"\u003e// 主机设为输入\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (DS18B20_IN() \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e (pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e(DS18B20_IN()) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e240\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief 从DS18B20读取一个bit\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_ReadBit(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e dat;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Mode_OUT_PP(); \u003cspan style=\"color:#75715e\"\u003e// 读 0 和读 1 的时间至少要大于 60 us\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_0();       \u003cspan style=\"color:#75715e\"\u003e// 读时间的起始：必须由主机产生 \u0026gt; 1us \u0026gt; 1;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// 写 0 和写 1 的时间至少要大于60us\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (testb) \u003cspan style=\"color:#75715e\"\u003e// 当前位写 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            DS18B20_OUT_0();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            delay_us(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e);     \u003cspan style=\"color:#75715e\"\u003e// 拉低发送写时段信号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            DS18B20_OUT_1(); \u003cspan style=\"color:#75715e\"\u003e// 读取电平时间保持高电平\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            delay_us(\u003cspan style=\"color:#ae81ff\"\u003e65\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// 当前位写 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            DS18B20_OUT_0(); \u003cspan style=\"color:#75715e\"\u003e// 拉低发送写时段信号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            delay_us(\u003cspan style=\"color:#ae81ff\"\u003e70\u003c/span\u003e);    \u003cspan style=\"color:#75715e\"\u003e// 读取电平时间保持低电平\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            DS18B20_OUT_1();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            delay_us(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e// 恢复时间\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  跳过匹配 DS18B20 ROM\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_SkipRom(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Reset();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Presence();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_WriteByte(\u003cspan style=\"color:#ae81ff\"\u003e0XCC\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/* 跳过 ROM */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  执行匹配 DS18B20 ROM\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_MatchRom(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Reset();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Presence();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_WriteByte(\u003cspan style=\"color:#ae81ff\"\u003e0X55\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/* 匹配 ROM */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_Class(GPIO_TypeDef \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eBSP_DS18B20_PORT, \u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e BSP_DS18B20_PIN, \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e LOCATION, \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eTemp_data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eBSP_DS18B20_PORT \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BSP_DS18B20_PORT;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eBSP_DS18B20_PIN \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BSP_DS18B20_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eLOCATION \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LOCATION;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eTemp_Sum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Temp_data;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eInit(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Mode_OUT_PP();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_1();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_Reset();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Presence\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * 存储的温度是16 位的带符号扩展的二进制补码形式\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * 当工作在12位分辨率时，其中5个符号位，7个整数位，4个小数位\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *         |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----|\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * 低字节  | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) |\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *         |-----符号位：0-\u0026gt;正  1-\u0026gt;负-------|-----------整数-----------|\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * 高字节  |  s  |  s  |  s  |  s  |    s   |   2^6  |   2^5  |   2^4  |\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * 温度 = 符号位 + 整数 + 小数*0.0625\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  在跳过匹配 ROM 情况下获取 DS18B20 温度值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @param  无\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @retval 温度值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e DS18B20_Class\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eDS18B20_GetTemp_SkipRom(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e tpmsb, tplsb;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint16_t\u003c/span\u003e s_tem;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e f_tem;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_SkipRom();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_WriteByte(\u003cspan style=\"color:#ae81ff\"\u003e0X44\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/* 开始转换 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_SkipRom();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_WriteByte(\u003cspan style=\"color:#ae81ff\"\u003e0XBE\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e/* 读温度值 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tplsb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DS18B20_ReadByte();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tpmsb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DS18B20_ReadByte();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    s_tem \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e tpmsb  \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e要注意其中的`\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eextern\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e`的用法；\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e### Main中调用：\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e注意\u003c/span\u003emain文件也是cpp后缀\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e；\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e main(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    User_DS18B20_Init();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        User_Start_Convert();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        HAL_Delay(\u003cspan style=\"color:#ae81ff\"\u003e200\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e// 更新速率为200ms，等待转换结束\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        User_Get_Data();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (i\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e使用\u003c/span\u003eC编写\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003ea011b38ca2d1e989de04f32bb8c0e5](img\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5.\u003c/span\u003epng)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e使用\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e编写\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003edbfdf38abe4f0d85cc7f98e4133007](img\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e6.\u003c/span\u003epng)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eC:\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RO  Size (Code \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RO Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e14016\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e13.69\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RW  Size (RW Data \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e ZI Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e2040\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.99\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal ROM Size (Code \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RO Data \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RW Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e14464\u003c/span\u003e (  \u003cspan style=\"color:#ae81ff\"\u003e14.13\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++:**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RO  Size (Code \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RO Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e11780\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e11.50\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RW  Size (RW Data \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e ZI Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e2864\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e2.80\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal ROM Size (Code \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RO Data \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e RW Data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e12220\u003c/span\u003e (  \u003cspan style=\"color:#ae81ff\"\u003e11.93\u003c/span\u003ekB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e相比较而言\u003c/span\u003eC用的ROM比较多一些\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e用的\u003c/span\u003eRAM比较多一些\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e；\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e可见在某些情况下，使用\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e编写代码可以有效缩减代码体积，且代码更易懂；\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e### microlib\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e使用\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e编译的话，就没法再使用\u003c/span\u003eMicroLIB\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，因为\u003c/span\u003eMicroLIB为非标准的精简库\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，会与标准\u003c/span\u003eC\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e产生冲突；\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e### 中断服务程序\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e如果中断服务程序是异常的，因为\u003c/span\u003estm32的中断入口矢量是按C的方式进入的\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，因此需要在整个文档的头部和末尾加上\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eextern\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e“\u003c/span\u003eC\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e”\u003c/span\u003e{}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e用大括号把整个代码段扩住，这样中断就可以正常的进入了；\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e### Cubemx\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCubemx在生成头文件中已经加入了\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ec\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ifdef __cplusplus\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eextern\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ifdef __cplusplus\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e所以在不是其生成的文件中要注意\u003ccode\u003eextern \u0026quot;C\u0026quot;\u003c/code\u003e的使用；\u003c/p\u003e","title":"使用C和C++进行混合编程"},{"content":" 做毕设用到了ROS，这里记录ROS相关知识；\n语言基于Python，因为C++短时间学不起来，Python的话还能懂一些；\n工作空间构建 创建一个新的文档夹 我们创建一个新的文档夹作为新的工作空间的开始 给每个新的工作空间创建一个新的文档夹是一个好习惯，取上面名字无关紧要，但是最好能从这个名字上看出这个工作空间是干什么的，例如main_ws，(主要工作空间)：\nmkdir main_ws 将功能包放入src也是一个比较好的习惯，我们创建一个工作空间同时创建一个src文档夹，然后进入这个文档夹内：\nmkdir main_ws/src cd main_ws/src 与ROS1不同，ROS2的工作空间并不需要init；\n自定义包 如何在ROS2中创建一个功能包呢？我们可以使用这个指令：\n$ ros2 pkg create --build-type 在ros2命令中：\npkg：表示功能包相关的功能；\ncreate：表示创建功能包；\nbuild-type：表示新创建的功能包是C++还是Python的，如果使用C++或者C，那这里就跟ament_cmake，如果使用Python，就跟ament_python；\npackage_name：新建功能包的名字。\n比如在终端中分别创建C++和Python版本的功能包：\ncd ~/main_ws/src ros2 pkg create --build-type ament_cmake learning_pkg_c # C++ ros2 pkg create --build-type ament_python learning_pkg_python # Python 对于Python包：\n只需要进到构建的包文件夹中，进入与上一层文件夹名字相同的包中即可，然后写入源文件；\n然后修改package.xml文件：\nfrom setuptools import setup package_name = \u0026#39;serial_test\u0026#39; setup( name=package_name, version=\u0026#39;0.0.0\u0026#39;, packages=[package_name], data_files=[ (\u0026#39;share/ament_index/resource_index/packages\u0026#39;, [\u0026#39;resource/\u0026#39; + package_name]), (\u0026#39;share/\u0026#39; + package_name, [\u0026#39;package.xml\u0026#39;]), ], install_requires=[\u0026#39;setuptools\u0026#39;], zip_safe=True, maintainer=\u0026#39;xioafei\u0026#39;, maintainer_email=\u0026#39;2253770787@qq.com\u0026#39;, description=\u0026#39;TODO: Package description\u0026#39;, license=\u0026#39;TODO: License declaration\u0026#39;, tests_require=[\u0026#39;pytest\u0026#39;], entry_points={ \u0026#39;console_scripts\u0026#39;: [ \u0026#39;interface_object_pub = serial_test.interface_object_pub:main\u0026#39;, # \u0026#39;interface_object_sub = serial_test.interface_object_sub:main\u0026#39;, ], }, ) 编译功能包 在创建好的功能包中，我们可以继续完成代码的编写，之后需要编译和配置环境变量，才能正常运行：\ncd ~/main_ws #在工作空间的根目录编译 colcon build #编译工作空间所有功能包 colcon build --packages-select 包名#编译特定的包 编译成功后，需要source一下，才能让ros识别到这个包：\n注意，最好新开一个终端，而不是在当前终端；\nsource install/local_setup.bash 关于sourceros1和ros2有很大的不同，在install目录下会有local_setup 和setup两个文档。local_setup只会把当前工作空间中的可用包添加到环境当中，setup则会把新建该工作空间时候的底层的工作空间也加入到环境当中，这样就可用同时使用两个工作空间中的包； 因此，先sourceROS2安装时系统的setup，然后再source main_ws的local_setup，和source main_ws的setup的效果是相同的，应为main_ws这个工作空间的创建时的底层就是系统的setup； 对于存在多个工作空间的情况这种区别就会体现出来，官网对于新的工作空间叫overlay，也就是这个一个每个工作空间之间是存在覆盖累计的情况； 个人推荐首先source系统，然后对于自己创建的工作空间选择local_setup这样就会减少自己创建的多个工作空间之间的干扰。这解决了ros1上一个很头疼的问题；\n自定义数据类型 在ROS2中定义接口，需要编写一个接口文档，该文档后缀为msg、srv、action\n在接口文档中定义通信过程中所使用的数据类型和数据名称；\n数据类型有哪些呢？\n原始的数据类型只有九类，其中每一个都可以在后面加上[]将其变成数组形式（从一个变成多个）\nbool byte char float32, float64 int8, uint8 int16, uint16 int32, uint32 int64, uint64 string 使用以下命令可以查看数据组成：\nros2 interface show point_msgs/msg/Ds18b20 如何自定义数据类型呢？\n对于话题接口的定义：\n你会、在包里面创建定制化的.msg和.srv文档，并且它们可在别的包内使用．所涉及的包应该放到同一工作空间下面．\n建立一个新的包：\nros2 pkg create --build-type ament_cmake tutorial_interfaces tutorial_interfaces是新包的名字．注意这是一个CMake类型的包；在单纯python包里面，目前是没有办法生成.msg或者.srv文档的．在CMake型包里面，你可以创建定制化接口，然后在python型节点中使用；\n在一个包中，最后是让.msg 和.srv放在各自文档目录比较好．在目录main_ws/src/tutorial_interfaces创建文档夹：\nmkdir msg mkdir srv 在刚刚创建的tutorial_interfaces/msg文档目录，新建一个名字为Ds18b20.msg的文档，里面放着声明数据结构的一行代码：\nint32 temp1 # 表示第1个温度 int32 temp2 # 表示第2个温度 int32 temp3 # 表示第1个温度 int32 temp4 # 表示第2个温度 int32 temp5 # 表示第1个温度 int32 temp6 # 表示第2个温度 int32 temp7 # 表示第1个温度 int32 temp8 # 表示第2个温度 将八个32位整数称为temp1~8，这就是你的定制化消息；\n3.3 CMakeLists.txt 为了将你定义的接口转为特定语言代码（如c++和python）,使得这些接口可在这些语言里被使用，(为了达到这个目地你)\n参考CMakeLists.txt文档：\ncmake_minimum_required(VERSION 3.8) project(point_msgs) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES \u0026#34;Clang\u0026#34;) add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find_package(ament_cmake REQUIRED) # uncomment the following section in order to fill in # further dependencies manually. # find_package( REQUIRED) find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} \u0026#34;msg/Ds18b20.msg\u0026#34; ) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # uncomment the line when a copyright and license is not present in all source files #set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # uncomment the line when this package is not in a git repo #set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() ament_package() 参考package.xml文档：\npoint_msgs 0.0.0 TODO: Package description xioafei TODO: License declaration ament_cmake ament_lint_auto ament_lint_common rosidl_default_generators rosidl_default_runtime rosidl_interface_packages ament_cmake 既然你定制化接口包的组成部分都齐了，那可以编译这个包了．在工作空间根目录（~/main_ws）下，运行下面指令：\ncolcon build --packages-select tutorial_interfaces 现在，这些接口可以被ros2包找到了；\n在新终端，main_ws工作空间运行下面指令来source一下（该空间环境变量）：\nsource ./install/setup.bash 现在使用ros2 interface show指令来确认你的创建的接口可以使用了．\nros2 interface show tutorial_interfaces/msg/Ds18b20 应该返回：\nint32 temp1 # 表示第1个温度 int32 temp2 # 表示第2个温度 int32 temp3 # 表示第1个温度 int32 temp4 # 表示第2个温度 int32 temp5 # 表示第1个温度 int32 temp6 # 表示第2个温度 int32 temp7 # 表示第1个温度 int32 temp8 # 表示第2个温度 通过发布话题发布自定义数据 参考源码：\nimport rclpy # ROS2 Python接口库 from rclpy.node import Node # ROS2 节点类 from std_msgs.msg import String # 字符串消息类型 from point_msgs.msg import Ds18b20 # 自定义的温度消息 \u0026#34;\u0026#34;\u0026#34; 创建一个发布者节点 \u0026#34;\u0026#34;\u0026#34; class PublisherNode(Node): def __init__(self, name): super().__init__(name) # ROS2节点父类初始化 self.pub = self.create_publisher(Ds18b20, \u0026#34;Ds18b20_temperature\u0026#34;, 100)# 创建发布者对象（消息类型、话题名、队列长度） self.timer = self.create_timer(0.1, self.timer_callback) # 创建一个定时器（单位为秒的周期，定时执行的回调函数） def timer_callback(self): # 创建定时器周期执行的回调函数 temp = Ds18b20() temp.temp1= int(1) temp.temp2= int(2) temp.temp3= int(3) temp.temp4= int(4) temp.temp5= int(5) temp.temp6= int(6) temp.temp7= int(7) temp.temp8= int(8) self.pub.publish(temp) # 发布目标位置 self.get_logger().info(\u0026#39;Publishing: \u0026#34;%d\u0026#34;\u0026#39; % temp.temp1) # 输出日志信息，提示已经完成话题发布 def main(args=None): # ROS2节点主入口main函数 rclpy.init(args=args) # ROS2 Python接口初始化 node = PublisherNode(\u0026#34;interface_object_pub\u0026#34;) # 创建ROS2节点对象并进行初始化 rclpy.spin(node) # 循环等待ROS2退出 node.destroy_node() # 销毁节点对象 rclpy.shutdown() # 关闭ROS2 Python接口 通过以下命令验证：\nros2 topilist ros2 topic echo /DS18B20N8_temperature 通过订阅话题订阅自定义数据 参考源码：\nROS2 TurtleBot3 主机和树莓派的ROS_DOMAIN_ID要设置的一样才可以；\nexport ROS_DOMAIN_ID=30 #TURTLEBOT3 装ROS2 TurtleBot3 的包只需要：\nsudo apt install ros-galatic-turtlebot3-* 其他的包也是类似；\n[TurtleBot] 启动小车\nros2 launch turtlebot3_bringup robot.launch.py [Remote PC] 运行键盘控制节点\nros2 run turtlebot3_teleop teleop_keyboard [Remote PC] 启动Rviz\nros2 launch turtlebot3_bringup rviz2.launch.py [Remote PC] 查看话题\n$ ros2 topic list /battery_state /cmd_vel /imu /joint_states /magnetic_field /odom /parameter_events /robot_description /rosout /scan /sensor_state /tf /tf_static [Remote PC] 查看服务\nros2 service list GAZEBO仿真 用自己做的模型替代例子包中的文件\n注意点： 1、串口设备和激光雷达设备串口会干扰，且好像激光雷达默认串口ttyUSB0，因此要在开机时或者运行launch.py前拔掉自己的串口设备；\n2、ros2 run nav2_map_server map_saver -f ~/map修改为：\nros2 run nav2_map_server map_saver_server -f ~/map ","permalink":"https://fan-pengfei.top/posts/ros%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/","summary":"\u003cblockquote\u003e\n\u003cp\u003e做毕设用到了ROS，这里记录ROS相关知识；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e语言基于Python，因为C++短时间学不起来，Python的话还能懂一些；\u003c/p\u003e\n\u003ch3 id=\"工作空间构建\"\u003e工作空间构建\u003c/h3\u003e\n\u003cp\u003e创建一个新的文档夹\n我们创建一个新的文档夹作为新的工作空间的开始\n给每个新的工作空间创建一个新的文档夹是一个好习惯，取上面名字无关紧要，但是最好能从这个名字上看出这个工作空间是干什么的，例如main_ws，(主要工作空间)：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir main_ws\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e将功能包放入src也是一个比较好的习惯，我们创建一个工作空间同时创建一个src文档夹，然后进入这个文档夹内：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir main_ws/src\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd main_ws/src\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e与ROS1不同，ROS2的工作空间并不需要init；\u003c/p\u003e\n\u003ch3 id=\"自定义包\"\u003e自定义包\u003c/h3\u003e\n\u003cp\u003e如何在ROS2中创建一个功能包呢？我们可以使用这个指令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ ros2 pkg create --build-type\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e在ros2命令中：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003epkg\u003c/strong\u003e：表示功能包相关的功能；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003ecreate\u003c/strong\u003e：表示创建功能包；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003ebuild-type\u003c/strong\u003e：表示新创建的功能包是C++还是Python的，如果使用C++或者C，那这里就跟ament_cmake，如果使用Python，就跟ament_python；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003epackage_name\u003c/strong\u003e：新建功能包的名字。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e比如在终端中分别创建C++和Python版本的功能包：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd ~/main_ws/src\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eros2 pkg create --build-type ament_cmake learning_pkg_c               \u003cspan style=\"color:#75715e\"\u003e# C++\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eros2 pkg create --build-type ament_python learning_pkg_python \u003cspan style=\"color:#75715e\"\u003e# Python\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e对于Python包：\u003c/p\u003e\n\u003cp\u003e只需要进到构建的包文件夹中，进入与上一层文件夹名字相同的包中即可，然后写入源文件；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"c74bd28eec8ebcb6aba8146b743cc0e\" loading=\"lazy\" src=\"/posts/ros%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后修改package.xml文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efrom setuptools import setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epackage_name = \u0026#39;serial_test\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esetup(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name=package_name,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    version=\u0026#39;0.0.0\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    packages=[package_name],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data_files=[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        (\u0026#39;share/ament_index/resource_index/packages\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            [\u0026#39;resource/\u0026#39; + package_name]),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        (\u0026#39;share/\u0026#39; + package_name, [\u0026#39;package.xml\u0026#39;]),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    install_requires=[\u0026#39;setuptools\u0026#39;],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    zip_safe=True,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    maintainer=\u0026#39;xioafei\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    maintainer_email=\u0026#39;2253770787@qq.com\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    description=\u0026#39;TODO: Package description\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    license=\u0026#39;TODO: License declaration\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tests_require=[\u0026#39;pytest\u0026#39;],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    entry_points={\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026#39;console_scripts\u0026#39;: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u0026#39;interface_object_pub  = serial_test.interface_object_pub:main\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        #  \u0026#39;interface_object_sub  = serial_test.interface_object_sub:main\u0026#39;,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"编译功能包\"\u003e编译功能包\u003c/h3\u003e\n\u003cp\u003e在创建好的功能包中，我们可以继续完成代码的编写，之后需要编译和配置环境变量，才能正常运行：\u003c/p\u003e","title":"ROS相关知识"},{"content":" 基于VSCode使用GDB来调试STM32，我感觉比那个Keil还好用，而且更懂底层原理；\n一、调试步骤： 准备工作 已经熟悉arm gcc工具链；\n已经在win中安装好mingw或者arm-none-eabi-gcc工具；\n具有合适的代码工程和编译脚本，且编译输出elf文档时，已添加-g选项来生成调试信息；\n安装jlink调试工具和对应驱动；\n有对应的硬件电路；\n1、启动Jlink GDB Server 打开Jlink诸多工具中的Jlink GDB Server并配置好：\n启动：\n可以看到本地端口为2331，这个一会会用到；\n然后就可以把这个窗口最小化了；\n2、GDB调试 启动GDB程序：\narm-none-eabi-gdb.exe 然后按enter自动进入调试模式；\n输入file H743_demo.elf加载调试文档：\n然后输入target remote localhost:2331，连接gdb server，连接成功后，会在Jlink GDB server中显示对应的状态，如下所示：\n输入monitor reset来复位MCU，从而让MCU处于确定的状态：\n输入load往MCU中加载调试文档（是加载进flash,而不是ram），也就是常见的烧录过程：\n输入break main设置main断点，让MCU执行到main中停止：\n输入c持续运行直至运行到断点处：\nHAL_Init();是main函数的第一行代码，停在这里；\n再次输入c会继续运行；\n若要打断持续运行的状态，只需要按下Ctrl+c即可；\n3、需要注意的地方 每次程序重新编译都要执行一次load以加载新的elf文档；\n如果不使用命令行，而是使用VSCODE中的调试功能，则也需要在程序更新的时候重新load一次；\n二、常用命令 1、p(打印) p+变量名：打印变量值：\n2、s(单步运行) s：单步运行；\n并且可以用 breakpoint+行号进行断点设置；\n3、l(列出) 列出当前位置前后共5行程序；\n4、watch(变量监视) Watchpoints 是用来告诉 GDB 停止执行某个程序的标记。Watchpoints 与数据相关联：放置监视点需要指定一个表达式来描述变量、多个变量或内存地址。\n为数据 change (写) 放置一个观察点：\n(gdb) watch expression 使用描述您要监视的表达式替换 expression；对于变量，expression 等于变量的名称。\n为数据 access (读) 放置一个观察点：\n(gdb) rwatch expression 要针对 任何 数据访问 放置 监视点（读取和写入）：\n(gdb) awatch expression 检查所有观察点和断点的状态：\n(gdb) info br 删除一个监视点：\n(gdb) delete num 将 num 选项替换为 info br 命令报告的编号；\n其他 可以参考这个链接：https://blog.csdn.net/zhuwade/article/details/122473098；\n等我用到了，我再一个一个写；\n三、使用VSCode进行辅助调试 参考：https://www.fan-pengfei.top/2023/02/27/%E5%9F%BA%E4%BA%8ESTM32%E7%9A%84CMAKE%E6%A8%A1%E6%9D%BF/#more\n","permalink":"https://fan-pengfei.top/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e基于VSCode使用GDB来调试STM32，我感觉比那个Keil还好用，而且更懂底层原理；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一调试步骤\"\u003e一、调试步骤：\u003c/h2\u003e\n\u003ch3 id=\"准备工作\"\u003e准备工作\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e已经熟悉arm gcc工具链；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e已经在win中安装好mingw或者arm-none-eabi-gcc工具；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e具有合适的代码工程和编译脚本，且编译输出elf文档时，已添加\u003ccode\u003e-g\u003c/code\u003e选项来生成调试信息；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e安装jlink调试工具和对应驱动；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e有对应的硬件电路；\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"1启动jlink-gdb-server\"\u003e1、启动Jlink GDB Server\u003c/h3\u003e\n\u003cp\u003e打开Jlink诸多工具中的Jlink GDB Server并配置好：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"e72631aa1f4bdb02354f118476623df\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e启动：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"42007dbdc5647b8720335665c84428f\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e可以看到本地端口为\u003cstrong\u003e2331\u003c/strong\u003e，这个一会会用到；\u003c/p\u003e\n\u003cp\u003e然后就可以把这个窗口最小化了；\u003c/p\u003e\n\u003ch3 id=\"2gdb调试\"\u003e2、GDB调试\u003c/h3\u003e\n\u003cp\u003e启动GDB程序：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003earm-none-eabi-gdb.exe\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"236f0b171cb512aca24824528019feb\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后按enter自动进入调试模式；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"89f35eeb649299594752d95cce23b1c\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e输入\u003ccode\u003efile H743_demo.elf\u003c/code\u003e加载调试文档：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"9a0c8d88bbf7814d61eb0344cfb34dd\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后输入\u003ccode\u003etarget remote localhost:2331\u003c/code\u003e，连接gdb server，连接成功后，会在Jlink GDB server中显示对应的状态，如下所示：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"f79eb5111b0dfb33e8b4f1c1b7aafab\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e输入\u003ccode\u003emonitor reset\u003c/code\u003e来复位MCU，从而让MCU处于确定的状态：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"4e26bf8c755ae990458b5659fa47de5\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-7.png\"\u003e\u003c/p\u003e\n\u003cp\u003e输入\u003ccode\u003eload\u003c/code\u003e往MCU中加载调试文档（是加载进flash,而不是ram），也就是常见的烧录过程：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"c6780b35ab50f7e2f5fc5193f69e5ec\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-15.png\"\u003e\u003c/p\u003e\n\u003cp\u003e输入\u003ccode\u003ebreak main\u003c/code\u003e设置main断点，让MCU执行到main中停止：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"aa8600b8137d1acda57d85b34589d8c\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e输入\u003ccode\u003ec\u003c/code\u003e持续运行直至运行到断点处：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"5de1ce9fb6048e1a442a4b876ea36a9\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-10.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eHAL_Init\u003c/strong\u003e();是main函数的第一行代码，停在这里；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e再次输入\u003ccode\u003ec\u003c/code\u003e会继续运行；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"2ab17a5b734305f89d325e492a6ad32\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-11.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e若要打断持续运行的状态，只需要按下\u003ccode\u003eCtrl+c\u003c/code\u003e即可；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"3需要注意的地方\"\u003e3、需要注意的地方\u003c/h3\u003e\n\u003cp\u003e每次程序重新编译都要执行一次\u003ccode\u003eload\u003c/code\u003e以加载新的elf文档；\u003c/p\u003e\n\u003cp\u003e如果不使用命令行，而是使用VSCODE中的调试功能，则也需要在程序更新的时候重新\u003ccode\u003eload\u003c/code\u003e一次；\u003c/p\u003e\n\u003ch2 id=\"二常用命令\"\u003e二、常用命令\u003c/h2\u003e\n\u003ch3 id=\"1p打印\"\u003e1、p(打印)\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003ep+变量名：打印变量值：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"d8c389e160cfaad354a0a486f0a6a72\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-12.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"2s单步运行\"\u003e2、s(单步运行)\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003es：单步运行；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"d026be12be61623d766adf025ba11e5\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-13.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e并且可以用 \u003ccode\u003ebreakpoint+行号\u003c/code\u003e进行断点设置；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"3l列出\"\u003e3、l(列出)\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e列出当前位置前后共5行程序；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"913f3b719d2d60aec6ff628a67ec0bc\" loading=\"lazy\" src=\"/posts/%E5%9C%A8gdb%E4%B8%8B%E8%B0%83%E8%AF%95stm32%E7%9A%84%E8%AE%B0%E5%BD%95/img-14.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"4watch变量监视\"\u003e4、watch(变量监视)\u003c/h3\u003e\n\u003cp\u003eWatchpoints 是用来告诉 \u003cstrong\u003eGDB\u003c/strong\u003e 停止执行某个程序的标记。Watchpoints 与数据相关联：放置监视点需要指定一个表达式来描述变量、多个变量或内存地址。\u003c/p\u003e","title":"在GDB下调试STM32的记录"},{"content":" 这是一个基于STM32单片机的模板；\n示例中的单片机是STM32H743IIT6，调试器使用JlinkOB；\n其中的各个参数可以参考使用STM32CubeMX生成的基于makefile的模板，且后续仍然可以使用STM32CubeMX进行底层代码的构建；\n如果需要进行调试，可以先启动J-Link GDB Server，然后使用VSCode进行调试或者直接使用命令行进行调试；\n# CMAKE_SYSTEM_NAME: 即你目标机target所在的操作系统名称，比如ARM或者Linux你就需要写\u0026#34;Linux\u0026#34;; # 如果Windows平台你就写\u0026#34;Windows\u0026#34;,如果你的嵌入式平台没有相关OS你即需要写成\u0026#34;Generic\u0026#34;; # 只有当CMAKE_SYSTEM_NAME这个变量被设置了，CMake才认为此时正在交叉编译; # 它会额外设置一个变量CMAKE_CROSSCOMPILING为TRUE; set(CMAKE_SYSTEM_NAME Generic) # CMAKE_SYSTEM_NAME和CMAKE_SYSTEM_PROCESSOR是交叉编译的时候必须指定的两个参数; # 如果在cmake命令行定义了CMAKE_SYSTEM_NAME,就必须也定义CMAKE_SYSTEM_PROCESSOR; set(CMAKE_SYSTEM_PROCESSOR cortex-m7) #cmake最低版本 cmake_minimum_required(VERSION 3.1.0) #编译工具 set(CROSS_COMPILE_PREFIX arm-none-eabi) # 顾名思义，即C语言编译器，这里可以将变量设置成完整路径或者文档名; # 设置成完整路径有一个好处就是CMake会去这个路径下去寻找编译相关的其他工具; # 比如linker,binutils等，如果你写的文档名带有arm-elf等等前缀; # CMake会识别到并且去寻找相关的交叉编译器; set(CMAKE_C_COMPILER ${CROSS_COMPILE_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${CROSS_COMPILE_PREFIX}-g++) set(CMAKE_ASM_COMPILER ${CROSS_COMPILE_PREFIX}-gcc) set(CMAKE_OBJCOPY ${CROSS_COMPILE_PREFIX}-objcopy) set(CMAKE_OBJDUMP ${CROSS_COMPILE_PREFIX}-objdump) set(CMAKE_SIZE ${CROSS_COMPILE_PREFIX}-size) # CMake中的命令find_program用于查找程序(program) # 会将查找到的文档路径存在CMakeCache.txt中 find_program(ARM_SIZE_EXECUTABLE ${CROSS_COMPILE_PREFIX}-size) find_program(ARM_GDB_EXECUTABLE ${CROSS_COMPILE_PREFIX}-gdb) find_program(ARM_OBJCOPY_EXECUTABLE ${CROSS_COMPILE_PREFIX}-objcopy) find_program(ARM_OBJDUMP_EXECUTABLE ${CROSS_COMPILE_PREFIX}-objdump) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # search for program/library/include in the build host directories # 1、CMAKE_FIND_ROOT_PATH_MODE_PROGRAM: 对FIND_PROGRAM()起作用， # 有三种取值，NEVER,ONLY,BOTH, # 第一个表示不在你CMAKE_FIND_ROOT_PATH下进行查找， # 第二个表示只在这个路径下查找， # 第三个表示先查找这个路径，再查找全局路径， # 对于这个变量来说，一般都是调用宿主机的程序，所以一般都设置成NEVER # # 2、CMAKE_FIND_ROOT_PATH_MODE_LIBRARY: 对FIND_LIBRARY()起作用， # 表示在链接的时候的库的相关选项，因此这里需要设置成ONLY来保证我们的库是在交叉环境中找的. # # 3、CMAKE_FIND_ROOT_PATH_MODE_INCLUDE: 对FIND_PATH()和FIND_FILE()起作用， # 一般来说也是ONLY,如果你想改变，一般也是在相关的FIND命令中增加option来改变局部设置 # 有NO_CMAKE_FIND_ROOT_PATH,ONLY_CMAKE_FIND_ROOT_PATH,BOTH_CMAKE_FIND_ROOT_PATH set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) #工程名称 # project命令用于指定cmake工程的名称 # 实际上，它还可以指定cmake工程的版本号（VERSION关键字）、 # 简短的描述（DESCRIPTION关键字）、 # 主页URL（HOMEPAGE_URL关键字）、 # 编译工程使用的语言（LANGUAGES关键字）。 project(H743_demo C CXX ASM) set(target \u0026#34;${PROJECT_NAME}\u0026#34;) set(COMPILE_TOOLS GCC) # Target-specific flags #型号 set(MCU_FAMILY STM32H743xx) #布局文档 set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/STM32H743IITx_FLASH.ld) #内核相关 set(CPU \u0026#34;-mcpu=cortex-m7\u0026#34;) set(FPU \u0026#34;-mfpu=fpv5-d16\u0026#34;) set(FLOAT_ABI \u0026#34;-mfloat-abi=hard\u0026#34;) #宏定义 add_definitions(-DUSE_HAL_DRIVER -DSTM32H743xx) # 构建Release或者Debug版本 if(CMAKE_BUILD_TYPE MATCHES Debug) set(DBG_FLAGS \u0026#34;-g3 -gdwarf-2 -O0\u0026#34;) elseif(CMAKE_BUILD_TYPE MATCHES Release) set(DBG_FLAGS \u0026#34;-O3\u0026#34;) endif() ##file语法,前一个参数是固定的 后面一个参数自己定义 ##添加文档的时候注意 相对路径和绝对路径 file(GLOB_RECURSE DRIVE_SRC Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_cortex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc_ex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash_ex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_gpio.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_hsem.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma_ex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_mdma.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr_ex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c_ex.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_exti.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c Core/Src/system_stm32h7xx.c startup_stm32h743xx.s ) file(GLOB_RECURSE USER_SRC Core/Src/main.c Core/Src/gpio.c Core/Src/stm32h7xx_it.c Core/Src/stm32h7xx_hal_msp.c ) # 添加源文件 set(SOURCE_FILES ${DRIVE_SRC} ${USER_SRC}) #添加头文件路径 include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Inc ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Inc/Legacy ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32H7xx/Include ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Include ) #芯片特性 set(MCU_FLAGS \u0026#34;${CPU} -mthumb ${FPU} ${FLOAT_ABI}\u0026#34;) # compiler: language specific flags CFLAGS set(CMAKE_C_FLAGS \u0026#34;${MCU_FLAGS} -std=gnu99 -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} \u0026#34; CACHE INTERNAL \u0026#34;C compiler flags\u0026#34;) #CPP set(CMAKE_CXX_FLAGS \u0026#34;${MCU_FLAGS} -fno-rtti -fno-exceptions -fno-builtin -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} \u0026#34; CACHE INTERNAL \u0026#34;Cxx compiler flags\u0026#34;) #ASFLAGS set(CMAKE_ASM_FLAGS \u0026#34;${MCU_FLAGS} -x assembler-with-cpp ${DBG_FLAGS} \u0026#34; CACHE INTERNAL \u0026#34;ASM compiler flags\u0026#34;) #LDFLAGS -mcpu=cortex-m0plus -mthumb set(CMAKE_EXE_LINKER_FLAGS \u0026#34;${MCU_FLAGS} --specs=nosys.specs -specs=nano.specs -T${LINKER_SCRIPT} -Wl,-Map=${PROJECT_NAME}.map,--cref -Wl,--gc-sections\u0026#34; CACHE INTERNAL \u0026#34;Exe linker flags\u0026#34;) #要链接的库 对应makefile的 LIBS set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS \u0026#34;-lc -lm -lnosys\u0026#34; CACHE INTERNAL \u0026#34;Shared linker flags\u0026#34;) #先定义target 才可以添加define include add_executable(${target}.elf ${SOURCE_FILES}) set(ELF_FILE ${PROJECT_BINARY_DIR}/${target}.elf) set(HEX_FILE ${PROJECT_BINARY_DIR}/${target}.hex) set(BIN_FILE ${PROJECT_BINARY_DIR}/${target}.bin) add_custom_command(TARGET \u0026#34;${target}.elf\u0026#34; POST_BUILD COMMAND ${CMAKE_OBJCOPY} -Obinary ${ELF_FILE} ${BIN_FILE} COMMAND ${CMAKE_OBJCOPY} -Oihex ${ELF_FILE} ${HEX_FILE} COMMENT \u0026#34;Building ${target}.bin and ${target}.hex\u0026#34; COMMAND ${CMAKE_COMMAND} -E copy ${HEX_FILE} \u0026#34;${CMAKE_CURRENT_BINARY_DIR}/${target}.hex\u0026#34; COMMAND ${CMAKE_COMMAND} -E copy ${BIN_FILE} \u0026#34;${CMAKE_CURRENT_BINARY_DIR}/${target}.bin\u0026#34; COMMAND ${CMAKE_SIZE} --format=berkeley ${target}.elf ${target}.hex COMMENT \u0026#34;Invoking: Cross ARM GNU Print Size\u0026#34; ) 使用方式：\nmkdir build cd build/ #cmake .. #在Linux平台下 cmake -G \u0026#34;MinGW Makefiles\u0026#34; .. #在windows平台下 make -j #多线程编译 使用VSCode进行调试的launch.json文件如下所示：\n{ \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;H743_demo\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;cppdbg\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, \u0026#34;program\u0026#34;: \u0026#34;${workspaceFolder}/build/H743_demo.elf\u0026#34;, \u0026#34;args\u0026#34;: [], \u0026#34;stopAtEntry\u0026#34;: false, \u0026#34;cwd\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34;, \u0026#34;environment\u0026#34;: [], \u0026#34;externalConsole\u0026#34;: false, \u0026#34;MIMode\u0026#34;: \u0026#34;gdb\u0026#34;, \u0026#34;miDebuggerPath\u0026#34;: \u0026#34;D:\\\\Program Files (x86)\\\\GNU Tools ARM Embedded\\\\5.4 2016q3\\\\bin\\\\arm-none-eabi-gdb.exe\u0026#34;, \u0026#34;miDebuggerServerAddress\u0026#34;: \u0026#34;localhost:2331\u0026#34; \u0026#34;setupCommands\u0026#34;: [ { \u0026#34;description\u0026#34;: \u0026#34;为 gdb 启用整齐打印\u0026#34;, \u0026#34;text\u0026#34;: \u0026#34;-enable-pretty-printing\u0026#34;, \u0026#34;ignoreFailures\u0026#34;: true } ], } ] } 最后成功进行调试：\n若要使用printf或者sprintf函数，需要自己底层实现一些函数；\n例如使用printf:\n#ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) int _write(int fd, char *pBuffer, int size) { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)pBuffer, size, 0xFFFF); return size; } #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) PUTCHAR_PROTOTYPE { HAL_UART_Transmit(\u0026amp;huart1, (uint8_t *)\u0026amp;ch, 1, 0xFFFF); return ch; } #endif ","permalink":"https://fan-pengfei.top/posts/%E5%9F%BA%E4%BA%8Estm32%E7%9A%84cmake%E6%A8%A1%E6%9D%BF/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个基于STM32单片机的模板；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e示例中的单片机是\u003cstrong\u003eSTM32H743IIT6\u003c/strong\u003e，调试器使用\u003cstrong\u003eJlinkOB\u003c/strong\u003e；\u003c/p\u003e\n\u003cp\u003e其中的各个参数可以参考使用\u003cstrong\u003eSTM32CubeMX\u003c/strong\u003e生成的基于\u003cstrong\u003emakefile\u003c/strong\u003e的模板，且后续仍然可以使用\u003cstrong\u003eSTM32CubeMX\u003c/strong\u003e进行底层代码的构建；\u003c/p\u003e\n\u003cp\u003e如果需要进行调试，可以先启动\u003cstrong\u003eJ-Link GDB Server\u003c/strong\u003e，然后使用VSCode进行调试或者直接使用命令行进行调试；\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-cmake\" data-lang=\"cmake\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# CMAKE_SYSTEM_NAME: 即你目标机target所在的操作系统名称，比如ARM或者Linux你就需要写\u0026#34;Linux\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 如果Windows平台你就写\u0026#34;Windows\u0026#34;,如果你的嵌入式平台没有相关OS你即需要写成\u0026#34;Generic\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 只有当CMAKE_SYSTEM_NAME这个变量被设置了，CMake才认为此时正在交叉编译;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 它会额外设置一个变量CMAKE_CROSSCOMPILING为TRUE;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_SYSTEM_NAME\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eGeneric\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# CMAKE_SYSTEM_NAME和CMAKE_SYSTEM_PROCESSOR是交叉编译的时候必须指定的两个参数;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 如果在cmake命令行定义了CMAKE_SYSTEM_NAME,就必须也定义CMAKE_SYSTEM_PROCESSOR;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_SYSTEM_PROCESSOR\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecortex-m7\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#cmake最低版本\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecmake_minimum_required(\u003cspan style=\"color:#e6db74\"\u003eVERSION\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e3.1.0\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#编译工具\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCROSS_COMPILE_PREFIX\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003earm-none-eabi\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 顾名思义，即C语言编译器，这里可以将变量设置成完整路径或者文档名;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 设置成完整路径有一个好处就是CMake会去这个路径下去寻找编译相关的其他工具;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 比如linker,binutils等，如果你写的文档名带有arm-elf等等前缀;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# CMake会识别到并且去寻找相关的交叉编译器;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_C_COMPILER\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-gcc\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_CXX_COMPILER\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-g++\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_ASM_COMPILER\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-gcc\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_OBJCOPY\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-objcopy\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_OBJDUMP\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-objdump\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_SIZE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-size\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# CMake中的命令find_program用于查找程序(program)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 会将查找到的文档路径存在CMakeCache.txt中\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind_program(\u003cspan style=\"color:#e6db74\"\u003eARM_SIZE_EXECUTABLE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-size\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind_program(\u003cspan style=\"color:#e6db74\"\u003eARM_GDB_EXECUTABLE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-gdb\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind_program(\u003cspan style=\"color:#e6db74\"\u003eARM_OBJCOPY_EXECUTABLE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-objcopy\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind_program(\u003cspan style=\"color:#e6db74\"\u003eARM_OBJDUMP_EXECUTABLE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCROSS_COMPILE_PREFIX\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e-objdump\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_TRY_COMPILE_TARGET_TYPE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eSTATIC_LIBRARY\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# search for program/library/include in the build host directories\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 1、CMAKE_FIND_ROOT_PATH_MODE_PROGRAM: 对FIND_PROGRAM()起作用，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 有三种取值，NEVER,ONLY,BOTH,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#   第一个表示不在你CMAKE_FIND_ROOT_PATH下进行查找，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#   第二个表示只在这个路径下查找，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#   第三个表示先查找这个路径，再查找全局路径，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 对于这个变量来说，一般都是调用宿主机的程序，所以一般都设置成NEVER\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 2、CMAKE_FIND_ROOT_PATH_MODE_LIBRARY: 对FIND_LIBRARY()起作用，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 表示在链接的时候的库的相关选项，因此这里需要设置成ONLY来保证我们的库是在交叉环境中找的.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 3、CMAKE_FIND_ROOT_PATH_MODE_INCLUDE: 对FIND_PATH()和FIND_FILE()起作用，\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 一般来说也是ONLY,如果你想改变，一般也是在相关的FIND命令中增加option来改变局部设置\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 有NO_CMAKE_FIND_ROOT_PATH,ONLY_CMAKE_FIND_ROOT_PATH,BOTH_CMAKE_FIND_ROOT_PATH\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_FIND_ROOT_PATH_MODE_PROGRAM\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eNEVER\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_FIND_ROOT_PATH_MODE_LIBRARY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eONLY\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_FIND_ROOT_PATH_MODE_INCLUDE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eONLY\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_FIND_ROOT_PATH_MODE_PACKAGE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eONLY\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#工程名称\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# project命令用于指定cmake工程的名称\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 实际上，它还可以指定cmake工程的版本号（VERSION关键字）、\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 简短的描述（DESCRIPTION关键字）、\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 主页URL（HOMEPAGE_URL关键字）、\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 编译工程使用的语言（LANGUAGES关键字）。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproject(\u003cspan style=\"color:#e6db74\"\u003eH743_demo\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eC\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCXX\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eASM\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003etarget\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${PROJECT_NAME}\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCOMPILE_TOOLS\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003eGCC\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Target-specific flags\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#型号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eMCU_FAMILY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eSTM32H743xx\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#布局文档\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eLINKER_SCRIPT\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/STM32H743IITx_FLASH.ld\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#内核相关\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCPU\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-mcpu=cortex-m7\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eFPU\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-mfpu=fpv5-d16\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eFLOAT_ABI\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-mfloat-abi=hard\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#宏定义\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadd_definitions(\u003cspan style=\"color:#e6db74\"\u003e-DUSE_HAL_DRIVER\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e-DSTM32H743xx\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 构建Release或者Debug版本\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eif(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_BUILD_TYPE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eMATCHES\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eDebug\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    set(\u003cspan style=\"color:#e6db74\"\u003eDBG_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-g3 -gdwarf-2 -O0\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eelseif(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_BUILD_TYPE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eMATCHES\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eRelease\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    set(\u003cspan style=\"color:#e6db74\"\u003eDBG_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-O3\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eendif()\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##file语法,前一个参数是固定的 后面一个参数自己定义\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##添加文档的时候注意 相对路径和绝对路径\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efile(\u003cspan style=\"color:#e6db74\"\u003eGLOB_RECURSE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eDRIVE_SRC\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_cortex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_gpio.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_hsem.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_mdma.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_exti.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eDrivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCore/Src/system_stm32h7xx.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003estartup_stm32h743xx.s\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efile(\u003cspan style=\"color:#e6db74\"\u003eGLOB_RECURSE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eUSER_SRC\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCore/Src/main.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCore/Src/gpio.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCore/Src/stm32h7xx_it.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCore/Src/stm32h7xx_hal_msp.c\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 添加源文件\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eSOURCE_FILES\u003c/span\u003e  \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eDRIVE_SRC\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eUSER_SRC\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#添加头文件路径\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einclude_directories(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/Core/Inc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/Drivers/STM32H7xx_HAL_Driver/Inc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/Drivers/STM32H7xx_HAL_Driver/Inc/Legacy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/Drivers/CMSIS/Device/ST/STM32H7xx/Include\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_CURRENT_SOURCE_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/Drivers/CMSIS/Include\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#芯片特性\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eMCU_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${CPU} -mthumb ${FPU} ${FLOAT_ABI}\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# compiler: language specific flags CFLAGS\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_C_FLAGS\u003c/span\u003e   \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${MCU_FLAGS} -std=gnu99 -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCACHE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eINTERNAL\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;C compiler flags\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#CPP\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_CXX_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${MCU_FLAGS} -fno-rtti -fno-exceptions -fno-builtin -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCACHE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eINTERNAL\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cxx compiler flags\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ASFLAGS\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_ASM_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${MCU_FLAGS} -x assembler-with-cpp ${DBG_FLAGS} \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCACHE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eINTERNAL\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ASM compiler flags\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#LDFLAGS -mcpu=cortex-m0plus -mthumb\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_EXE_LINKER_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${MCU_FLAGS} --specs=nosys.specs -specs=nano.specs -T${LINKER_SCRIPT} -Wl,-Map=${PROJECT_NAME}.map,--cref -Wl,--gc-sections\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCACHE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eINTERNAL\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Exe linker flags\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#要链接的库 对应makefile的 LIBS\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eCMAKE_SHARED_LIBRARY_LINK_C_FLAGS\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-lc -lm -lnosys\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eCACHE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eINTERNAL\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Shared linker flags\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#先定义target 才可以添加define include\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadd_executable(\u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.elf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eSOURCE_FILES\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eELF_FILE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003ePROJECT_BINARY_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.elf\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eHEX_FILE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003ePROJECT_BINARY_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.hex\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset(\u003cspan style=\"color:#e6db74\"\u003eBIN_FILE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003ePROJECT_BINARY_DIR\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.bin\u003c/span\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadd_custom_command(\u003cspan style=\"color:#e6db74\"\u003eTARGET\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${target}.elf\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ePOST_BUILD\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMAND\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_OBJCOPY\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e-Obinary\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eELF_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eBIN_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMAND\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_OBJCOPY\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e-Oihex\u003c/span\u003e  \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eELF_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eHEX_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMENT\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Building ${target}.bin and ${target}.hex\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMAND\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_COMMAND\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e-E\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eHEX_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${CMAKE_CURRENT_BINARY_DIR}/${target}.hex\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMAND\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_COMMAND\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e-E\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eBIN_FILE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;${CMAKE_CURRENT_BINARY_DIR}/${target}.bin\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMAND\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003eCMAKE_SIZE\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e--format=berkeley\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.elf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e${\u003c/span\u003etarget\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.hex\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eCOMMENT\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Invoking: Cross ARM GNU Print Size\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e使用方式：\u003c/p\u003e","title":"基于STM32的CMAKE模板"},{"content":" CMAKE学习记录；\n新建两个目录：\nmkdir ./src mkdir ./build 源文档编写：src/main.cpp\n#include int main() { std::cout ![b562172f8e925c8150fa45bcbcb7b17](img-1.png) 然后输入以下命令进行编译和运行： ```bash make .\\cmake_study.exe ","permalink":"https://fan-pengfei.top/posts/cmake%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003eCMAKE学习记录；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e新建两个目录：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir ./src\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir ./build\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e源文档编写：\u003ccode\u003esrc/main.cpp\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e[b562172f8e925c8150fa45bcbcb7b17](img\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1.\u003c/span\u003epng)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e然后输入以下命令进行编译和运行：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ebash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\\\u003c/span\u003ecmake_study.exe\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"9e8427360f39e4f01fad94a02d3d160\" loading=\"lazy\" src=\"/posts/cmake%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e","title":"CMAKE学习记录"},{"content":" STM32启动代码原理分析（底层技术）；\n简述 ARM Cortex-M系列MCU的启动代码（使用汇编语言编程则不需要）主要做3件事情：\n初始化并正确放置异常/中断矢量表；\n分散加载；\n初始化C语言运行环境（初始化堆栈以及C Library、浮点等）。\nCortex-M3内核规定，起始地址必须存放堆顶指针，而第二个地址则必须存放复位中断入口矢量地址，这样在Cortex-M3内核复位后，会自动从起始地址的下一个32位空间取出复位中断入口矢量，跳转执行复位中断服务程序。对比ARM7/ARM9内核，Cortex-M3内核则是固定了中断矢量表的位置而起始地址是可变化的。\n源码分析 基于STM32F103C6T6的启动文件startup_stm32f103x6.s的简要说明如下：\n;******************** (C) COPYRIGHT 2017 STMicroelectronics ******************** ;* File Name : startup_stm32f103x6.s ;* Author : MCD Application Team ;* Description : STM32F103x6 Devices vector table for MDK-ARM toolchain. ;* This module performs: ;* - Set the initial SP ;* - Set the initial PC == Reset_Handler ;* - Set the vector table entries with the exceptions ISR address ;* - Configure the clock system ;* - Branches to __main in the C library (which eventually ;* calls main()). ;* After Reset the Cortex-M3 processor is in Thread mode, ;* priority is Privileged, and the Stack is set to Main. ;****************************************************************************** ;* @attention ;* ;* Copyright (c) 2017 STMicroelectronics. ;* All rights reserved. ;* ;* This software component is licensed by ST under BSD 3-Clause license, ;* the \u0026#34;License\u0026#34;; You may not use this file except in compliance with the ;* License. You may obtain a copy of the License at: ;* opensource.org/licenses/BSD-3-Clause ;* ;****************************************************************************** ; Amount of memory (in bytes) allocated for Stack ; Tailor this value to your application needs ; Stack Configuration ; Stack Size (in Bytes) ; Stack_Size\tEQU 0x400 ;声明栈的大小为0x400字节 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size ;开辟一段大小为Stack_Size的内存空间作为栈 __initial_sp ;标号__initial_sp，表示栈空间顶地址。 ; Heap Configuration ; Heap Size (in Bytes) ; Heap_Size EQU 0x200 ;声明栈的大小为0x200字节 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ;标号__heap_base，表示堆空间起始地址。 Heap_Mem SPACE Heap_Size ;开辟一段大小为Heap_Size的内存空间作为堆。 __heap_limit ;标号__heap_limit，表示堆空间结束地址。 ;-------------------------------------------- ;第一部分： ;启动代码最重要的工作是把异常中断向量表放到正确的Flash地址上 ;把向量表定义为只读数据段，并导出向量表标号(Symbol)，让链接器识别此标号并根据分散加载文件正确的放置向量表 ;__Vectors标号需要与分散加载文件合起来看，才会明白其真正的功能 ;-------------------------------------------- PRESERVE8 ;告诉编译器以8字节对齐。 THUMB ;告诉编译器使用THUMB指令集。 ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY ;声明权限为“READONLY”的名称为“RESET”的数据段 ;（假设STM32从FLASH启动，则此中断矢量表起始地址即为0x8000000） EXPORT __Vectors ;将标号__Vectors声明为全局标号，这样外部文档就可以使用这个标号。 EXPORT __Vectors_End EXPORT __Vectors_Size ;标号__Vectors，表示中断矢量表入口地址 ;创建中断矢量表 ;--------------------------------------- ;第二部分: ;__initial_sp ; 1、栈顶指针地址，此语法跟MDK编译器的底层相关，是ARMCC编译器才能识别的语法 ; GCC与IAR的底层编译器ICCARM编译器不能识别; ; 2、__initial_sp 是一个链接器Image Symbol; ; 3、此处__initial_sp相当于是顶地址，或者此处直接把顶地址写到此处也行(如:0x20004000); ; 4、__initial_sp具体是多少，在此种写法下，是由分散加载文件决定的，下文会有详细论述; ; ;Reset_Handler: ; 1、Reset_Handler函数地址，此处相当于把Reset_Handler函数地址赋值给PC，即调用Reset_Handler函数; ; 2、此处也可以是其他函数，只是把复位函数放于此处最符合实际应用场景。 ; 重要关键节点: ; 1、绝大多数cortex-M微控制器(M0、M3、M4都是这样)复位后先进入厂商BOOTROM，此时所有用户行为均无法介入处理器; ; 2、厂商BOOTROM(有些厂商会有其他名称来称呼此功能) 主要负责处理一些芯片最初级初始化、 ; 加密以及一些对MCU的差异化设置等工作: ; 3、BOOTROM顺利完成后，MCu控制权会交给用户，即启动代码; ; 4、启动代码(运行汇编语言则不需要此启动代码)，最重要的工作在于设置MSP (主堆栈指针)以及PC(程序计数器)的值; ; 5、Cortex-M微控制器会默认把0x00000000地址里面的值设置为MSP的值，0x00000004地址里面的值设置为PC的值; ; 6、5中的默认地址可以通过修改Cortex-M中的VTOR寄存器来重新映射，比如改到0x20000000地址或其他; ; ;关于Exception(异常) 与Interrupt (中断) 的区别说明 ; 1、Exception(异常)与Interrupt(中断)是不同的，是两个不同的概念，很多人会混淆两者, ; 把他们都按照中断来看待，这是错误的; ; 2、Exception(异常)是向量表的前16个向量，其优先级为负数，高于所有中断，而且不可调整优先级也不可关闭, ; 可以打断正常程序与Interrupt(中断) 的运行: ; 3、从第16个向量以后才是Interrupt(中断)，可以设置优先级，不用时可以关闭，但优先级永远低于Exception(异常); ; 4、从这里大家就可以理解Pendsy Handler与svsTick Handler为什么会被用于嵌入式操作系统， ; 因为其优先级高于所有中断,可以确保操作系统拥有高于普通用户程序执行的超级权限, ; SVC_Handler有时也会用于操作系统，原理相同; ; 5、用户应用程序应尽量避免使用Exception(异常); ;--------------------------------------- __Vectors DCD __initial_sp ; Top of Stack 栈顶地址 DCD Reset_Handler ; Reset Handler 复位向量 DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler 操作系统会用到的异常向量 DCD SysTick_Handler ; SysTick Handler 操作系统会用到的心跳定时器异常向量(没有操作系统时可以用作普通定时器中断) ;--------------------------------------- ;第三部分: ; 1、这里开始是中断向量表; ; 2、各个向量的顺序是芯片设计的时候就定义好的，不能更改; ;--------------------------------------- ; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line 0 DCD EXTI1_IRQHandler ; EXTI Line 1 DCD EXTI2_IRQHandler ; EXTI Line 2 DCD EXTI3_IRQHandler ; EXTI Line 3 DCD EXTI4_IRQHandler ; EXTI Line 4 DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 DCD ADC1_2_IRQHandler ; ADC1_2 DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD TIM1_BRK_IRQHandler ; TIM1 Break DCD TIM1_UP_IRQHandler ; TIM1 Update DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD 0 ; Reserved DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD 0 ; Reserved DCD 0 ; Reserved DCD SPI1_IRQHandler ; SPI1 DCD 0 ; Reserved DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD 0 ; Reserved DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend __Vectors_End __Vectors_Size EQU __Vectors_End - __Vectors ;--------------------------------------- ;第四部分: ;这部分开始可以称作Reset Handler实体，芯片上电后，经过BOOTROM后进入的用户可控最开始处的地方; ; 1、如果想让Mcu正常使用c语言，务必在此处调用 main函数; ; 2、__main() 不是main() 两者有着本质性的区别; ; 3、__main()是c Library中的函数，Kei1开发环境中自带的c Library中的函数; ; 4、main()是被 __main()调用的，__main()工作完成后最后一步就是调用main(); ; 5、__main()被调用之前，可以根据需要插入一个或多个其他功能函数; ;--------------------------------------- AREA |.text|, CODE, READONLY ;定义只读的代码段 ; Reset handler routine ;复位中断服务程序，PROC…ENDP结构表示程序的开始和结束。 Reset_Handler PROC EXPORT Reset_Handler [WEAK] ;声明复位中断矢量Reset_Handler为全局属性 ;这样外部文档就可以调用此复位中断服务。 IMPORT __main ;声明__main标号。 IMPORT SystemInit ;声明SystemInit标号。 LDR R0, =SystemInit ;跳转SystemInit地址执行 BLX R0 LDR R0, =__main ;跳转__main地址执行 BX R0 ENDP ; Dummy Exception Handlers (infinite loops which can be modified) ;--------------------------------------------------------------- ;第五部分: ; 1、[weak]指定了一个这个函数为\u0026#34;弱函数”; ; 2、这些中断服务函教定义成弱函数的意义是，当中断出现时，需要有一个中断服务函数予以响应，但真实的 ; 用户程序往往只会使用一部分中断，甚至不使用中断，所以以下这些函数给出了异常/中断服务函数的 ; 默认实现，很简单，默认实现就是死循环汇编中的\u0026#34;B.\u0026#34;语句,相当于while(1);因为不知道用户是否会 ; 用到多少中断，但这些服务函数又很重要，所以就把这些函数都\u0026#34;实现\u0026#34;并声明为弱函数; ; 3、弱函数的意思是如果用户定义了同样名称的另一个函数，那么默认实现的弱函数就会被覆盖，比如 ; HardFault_Handler异常在下面有一个默认的实现，但这种默认的实现不能满足我的需要的时候，我可以 ; 再重新定义一个HardEault Handler函数这个新定义的HardFault_Handler函数会覆盖原有的被声明 ; 为[WEAK]的弱函数; ; 4、有很多种适合使用弱函数的场合，默认的异常/中断服务函数只是一种应用场景; ; 5、在C语言中声明弱函数是在函数后加“__attribute((weak))”; ;---------------------------------------------------------------- NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP HardFault_Handler\\ PROC EXPORT HardFault_Handler [WEAK] B . ENDP MemManage_Handler\\ PROC EXPORT MemManage_Handler [WEAK] B . ENDP BusFault_Handler\\ PROC EXPORT BusFault_Handler [WEAK] B . ENDP UsageFault_Handler\\ PROC EXPORT UsageFault_Handler [WEAK] B . ENDP SVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDP DebugMon_Handler\\ PROC EXPORT DebugMon_Handler [WEAK] B . ENDP PendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDP SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP Default_Handler PROC EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] EXPORT TAMPER_IRQHandler [WEAK] EXPORT RTC_IRQHandler [WEAK] EXPORT FLASH_IRQHandler [WEAK] EXPORT RCC_IRQHandler [WEAK] EXPORT EXTI0_IRQHandler [WEAK] EXPORT EXTI1_IRQHandler [WEAK] EXPORT EXTI2_IRQHandler [WEAK] EXPORT EXTI3_IRQHandler [WEAK] EXPORT EXTI4_IRQHandler [WEAK] EXPORT DMA1_Channel1_IRQHandler [WEAK] EXPORT DMA1_Channel2_IRQHandler [WEAK] EXPORT DMA1_Channel3_IRQHandler [WEAK] EXPORT DMA1_Channel4_IRQHandler [WEAK] EXPORT DMA1_Channel5_IRQHandler [WEAK] EXPORT DMA1_Channel6_IRQHandler [WEAK] EXPORT DMA1_Channel7_IRQHandler [WEAK] EXPORT ADC1_2_IRQHandler [WEAK] EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] EXPORT CAN1_RX1_IRQHandler [WEAK] EXPORT CAN1_SCE_IRQHandler [WEAK] EXPORT EXTI9_5_IRQHandler [WEAK] EXPORT TIM1_BRK_IRQHandler [WEAK] EXPORT TIM1_UP_IRQHandler [WEAK] EXPORT TIM1_TRG_COM_IRQHandler [WEAK] EXPORT TIM1_CC_IRQHandler [WEAK] EXPORT TIM2_IRQHandler [WEAK] EXPORT TIM3_IRQHandler [WEAK] EXPORT I2C1_EV_IRQHandler [WEAK] EXPORT I2C1_ER_IRQHandler [WEAK] EXPORT SPI1_IRQHandler [WEAK] EXPORT USART1_IRQHandler [WEAK] EXPORT USART2_IRQHandler [WEAK] EXPORT EXTI15_10_IRQHandler [WEAK] EXPORT RTC_Alarm_IRQHandler [WEAK] EXPORT USBWakeUp_IRQHandler [WEAK] ;--------------------------------------------------- ;第六部分 ; 这部分比较简单，看一下第五部分的代码就可以理解下面的部分; ;--------------------------------------------------- WWDG_IRQHandler PVD_IRQHandler TAMPER_IRQHandler RTC_IRQHandler FLASH_IRQHandler RCC_IRQHandler EXTI0_IRQHandler EXTI1_IRQHandler EXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler DMA1_Channel1_IRQHandler DMA1_Channel2_IRQHandler DMA1_Channel3_IRQHandler DMA1_Channel4_IRQHandler DMA1_Channel5_IRQHandler DMA1_Channel6_IRQHandler DMA1_Channel7_IRQHandler ADC1_2_IRQHandler USB_HP_CAN1_TX_IRQHandler USB_LP_CAN1_RX0_IRQHandler CAN1_RX1_IRQHandler CAN1_SCE_IRQHandler EXTI9_5_IRQHandler TIM1_BRK_IRQHandler TIM1_UP_IRQHandler TIM1_TRG_COM_IRQHandler TIM1_CC_IRQHandler TIM2_IRQHandler TIM3_IRQHandler I2C1_EV_IRQHandler I2C1_ER_IRQHandler SPI1_IRQHandler USART1_IRQHandler USART2_IRQHandler EXTI15_10_IRQHandler RTC_Alarm_IRQHandler USBWakeUp_IRQHandler B . ENDP ALIGN ;******************************************************************************* ; User Stack and Heap initialization ;******************************************************************************* ;IF…ELSE…ENDIF结构，判断是否使用DEF:__MICROLIB（此处为不使用）。 ;若使用DEF:__MICROLIB，则将__initial_sp，__heap_base，__heap_limit ;亦即栈顶地址，堆始末地址赋予全局属性，使外部程序可以使用。 IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory ;定义全局标号__use_two_region_memory。 EXPORT __user_initial_stackheap ;声明全局标号__user_initial_stackheap， ;这样外程序也可调用此标号 __user_initial_stackheap ;标号__user_initial_stackheap，表示用户堆栈初始化程序入口 ;分别保存栈顶指针和栈大小，堆始地址和堆大小至R0，R1，R2，R3寄存器。 LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF END;程序完毕 ;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE***** 部分解释 以上便是STM32的启动代码的完整解析，接下来对几个小地方做解释：\n1、AREA指令：伪指令，用于定义代码段或数据段，后跟属性标号。其中比较重要的一个标号为“READONLY”或者“READWRITE”，其中“READONLY”表示该段为只读属性，联系到STM32的内部存储介质，可知具有只读属性的段保存于FLASH区，即0x8000000地址后。而“READONLY”表示该段为“可读写”属性，可知“可读写”段保存于SRAM区，即0x2000000地址后。由此可以从栈堆定义的代码知道，堆栈段位于SRAM空间。从AREA RESET, DATA, READONLY可知，中断矢量表放置于FLASH区，而这也是整片启动代码中最先被放进FLASH区的数据。因此可以得到一条重要的信息：0x8000000地址存放的是栈顶地址__initial_sp，0x8000004地址存放的是复位中断矢量Reset_Handler（STM32使用32位总线，因此存储空间为4字节对齐）。\n2、 DCD指令：作用是开辟一段空间，其意义等价于C语言中的地址符“\u0026amp;”。因此从__Vectors行开始创建的中断矢量表则类似于使用C语言定义了一个指针数组，其每一个成员都是一个函数指针，分别指向各个中断服务函数。\n3、 标号：前文多处使用了“标号”一词。标号主要用于表示一片内存空间的某个位置，等价于C语言中的“地址”概念。地址仅仅表示存储空间的一个位置，从C语言的角度来看，变量的地址，数组的地址或是函数的入口地址在本质上并无区别。\n4、 IMPORT __main的__main标号并不表示C程序中的main函数入口地址，因此接下来的BX R0也并不是跳转至main函数开始执行C程序。__main标号表示C/C++标准实时库函数里的一个初始化子程序__main的入口地址。该程序的一个主要作用是初始化堆栈（对于程序清单一来说则是跳转__user_initial_stackheap标号进行初始化堆栈的），并初始化映像文档，最后跳转C程序中的main函数。这就解释了为何所有的C程序必须有一个main函数作为程序的起点——因为这是由C/C++标准实时库所规定的——并且不能更改，因为C/C++标准实时库并不对外界开发源代码。因此，实际上在用户可见的前提下，程序在BX R0后就跳转至.c文档中的main函数，开始执行C程序了。\n至此可以总结一下STM32的启动文档和启动过程。首先对栈和堆的大小进行定义，并在代码区的起始处创建中断矢量表，其第一个表项是栈顶地址，第二个表项是复位中断服务入口地址。然后在复位中断服务程序中跳转C/C++标准实时库的__main函数，完成用户堆栈等的初始化后，跳转.c文档中的main函数开始执行C程序。\n假设STM32被设置为从内部FLASH启动（这也是最常见的一种情况），中断矢量表起始地位为0x8000000，则栈顶地址存放于0x8000000处，而复位中断服务入口地址存放于0x8000004处。当STM32遇到复位信号后，则从0x80000004处取出复位中断服务入口地址，继而执行复位中断服务程序，然后跳转__main函数，最后进入mian函数，来到C的世界。\n图片示意 函数的调用过程： 启动流程1（使用标准库，不使用Microlib） 如下图：\n启动流程2（使用Microlib） microlib 是缺省 C 库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。这些应用程序不在操作系统中运行。 microlib 进行了高度优化以使代码变得很小。它的功能比缺省 C 库少，并且根本不具备某些 ISOC 特性。某些库函数的运行速度也比较慢，例如， memcpy() 。 microlib与缺省C库之间的主要差异是： microlib不符合ISO C库标准。不支持某些ISO特性，并且其他特性具有的功能也较少； microlib不符合IEEE 754二进制浮点算法标准； microlib进行了高度优化以使代码变得很小； 无法对区域设置进行配置。缺省C区域设置是唯一可用的区域设置； 不能将main()声明为使用参数，并且不能返回内容； 不支持stdio，但未缓冲的stdin、stdout和stderr除外； microlib对C99函数提供有限的支持； microlib不支持操作系统函数； microlib不支持与位置无关的代码； microlib不提供互斥锁来防止非线程安全的代码； microlib不支持宽字符或多字节字符串； 与stdlib不同，microlib不支持可选择的单或双区内存模型。microlib只提供双区内存模型，即单独的堆栈和堆区。\n启动流程如下图：\n","permalink":"https://fan-pengfei.top/posts/stm32%E5%90%AF%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/","summary":"\u003cblockquote\u003e\n\u003cp\u003eSTM32启动代码原理分析（底层技术）；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"简述\"\u003e简述\u003c/h2\u003e\n\u003cp\u003eARM Cortex-M系列MCU的启动代码（使用汇编语言编程则不需要）主要做3件事情：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e初始化并正确放置异常/中断矢量表；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e分散加载；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e初始化C语言运行环境（初始化堆栈以及C Library、浮点等）。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCortex-M3内核规定，起始地址必须存放堆顶指针，而第二个地址则必须存放复位中断入口矢量地址，这样在Cortex-M3内核复位后，会自动从起始地址的下一个32位空间取出复位中断入口矢量，跳转执行复位中断服务程序。对比ARM7/ARM9内核，Cortex-M3内核则是固定了中断矢量表的位置而起始地址是可变化的。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"源码分析\"\u003e源码分析\u003c/h2\u003e\n\u003cp\u003e基于\u003ccode\u003eSTM32F103C6T6\u003c/code\u003e的启动文件\u003ccode\u003estartup_stm32f103x6.s\u003c/code\u003e的简要说明如下：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;******************** (C) COPYRIGHT 2017 STMicroelectronics ********************\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* File Name          : startup_stm32f103x6.s\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* Author             : MCD Application Team\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* Description        : STM32F103x6 Devices vector table for MDK-ARM toolchain.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      This module performs:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      - Set the initial SP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      - Set the initial PC == Reset_Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      - Set the vector table entries with the exceptions ISR address\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      - Configure the clock system\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      - Branches to __main in the C library (which eventually\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                        calls main()).\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      After Reset the Cortex-M3 processor is in Thread mode,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                      priority is Privileged, and the Stack is set to Main.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;******************************************************************************\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* @attention\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* Copyright (c) 2017 STMicroelectronics.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* All rights reserved.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* This software component is licensed by ST under BSD 3-Clause license,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* the \u0026#34;License\u0026#34;; You may not use this file except in compliance with the\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;* License. You may obtain a copy of the License at:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*                        opensource.org/licenses/BSD-3-Clause\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;******************************************************************************\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; Amount of memory (in bytes) allocated for Stack\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; Tailor this value to your application needs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;  Stack Configuration\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;    Stack Size (in Bytes)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eStack_Size\t\tEQU     0x400       ;声明栈的大小为0x400字节\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                AREA    STACK, NOINIT, READWRITE, ALIGN=3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eStack_Mem       SPACE   Stack_Size  ;开辟一段大小为Stack_Size的内存空间作为栈\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__initial_sp                        ;标号__initial_sp，表示栈空间顶地址。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;  Heap Configuration\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;     Heap Size (in Bytes)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHeap_Size       EQU     0x200     ;声明栈的大小为0x200字节\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                AREA    HEAP, NOINIT, READWRITE, ALIGN=3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__heap_base                       ;标号__heap_base，表示堆空间起始地址。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHeap_Mem        SPACE   Heap_Size ;开辟一段大小为Heap_Size的内存空间作为堆。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__heap_limit                      ;标号__heap_limit，表示堆空间结束地址。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;--------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第一部分：\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;启动代码最重要的工作是把异常中断向量表放到正确的Flash地址上\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;把向量表定义为只读数据段，并导出向量表标号(Symbol)，让链接器识别此标号并根据分散加载文件正确的放置向量表\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;__Vectors标号需要与分散加载文件合起来看，才会明白其真正的功能\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;--------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PRESERVE8         ;告诉编译器以8字节对齐。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                THUMB             ;告诉编译器使用THUMB指令集。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; Vector Table Mapped to Address 0 at Reset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                AREA    RESET, DATA, READONLY   ;声明权限为“READONLY”的名称为“RESET”的数据段\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                       ;（假设STM32从FLASH启动，则此中断矢量表起始地址即为0x8000000）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  __Vectors      ;将标号__Vectors声明为全局标号，这样外部文档就可以使用这个标号。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  __Vectors_End\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  __Vectors_Size\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;标号__Vectors，表示中断矢量表入口地址\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;创建中断矢量表\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第二部分:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;__initial_sp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、栈顶指针地址，此语法跟MDK编译器的底层相关，是ARMCC编译器才能识别的语法\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      GCC与IAR的底层编译器ICCARM编译器不能识别;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、__initial_sp 是一个链接器Image Symbol;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   3、此处__initial_sp相当于是顶地址，或者此处直接把顶地址写到此处也行(如:0x20004000);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   4、__initial_sp具体是多少，在此种写法下，是由分散加载文件决定的，下文会有详细论述;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;Reset_Handler:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、Reset_Handler函数地址，此处相当于把Reset_Handler函数地址赋值给PC，即调用Reset_Handler函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、此处也可以是其他函数，只是把复位函数放于此处最符合实际应用场景。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      重要关键节点:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、绝大多数cortex-M微控制器(M0、M3、M4都是这样)复位后先进入厂商BOOTROM，此时所有用户行为均无法介入处理器;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、厂商BOOTROM(有些厂商会有其他名称来称呼此功能) 主要负责处理一些芯片最初级初始化、\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      加密以及一些对MCU的差异化设置等工作:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   3、BOOTROM顺利完成后，MCu控制权会交给用户，即启动代码;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   4、启动代码(运行汇编语言则不需要此启动代码)，最重要的工作在于设置MSP (主堆栈指针)以及PC(程序计数器)的值;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   5、Cortex-M微控制器会默认把0x00000000地址里面的值设置为MSP的值，0x00000004地址里面的值设置为PC的值;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   6、5中的默认地址可以通过修改Cortex-M中的VTOR寄存器来重新映射，比如改到0x20000000地址或其他;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;关于Exception(异常) 与Interrupt (中断) 的区别说明\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、Exception(异常)与Interrupt(中断)是不同的，是两个不同的概念，很多人会混淆两者,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      把他们都按照中断来看待，这是错误的;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、Exception(异常)是向量表的前16个向量，其优先级为负数，高于所有中断，而且不可调整优先级也不可关闭,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      可以打断正常程序与Interrupt(中断) 的运行:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   3、从第16个向量以后才是Interrupt(中断)，可以设置优先级，不用时可以关闭，但优先级永远低于Exception(异常);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   4、从这里大家就可以理解Pendsy Handler与svsTick Handler为什么会被用于嵌入式操作系统，\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      因为其优先级高于所有中断,可以确保操作系统拥有高于普通用户程序执行的超级权限,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      SVC_Handler有时也会用于操作系统，原理相同;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   5、用户应用程序应尽量避免使用Exception(异常);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__Vectors       DCD     __initial_sp               ; Top of Stack  栈顶地址\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     Reset_Handler              ; Reset Handler 复位向量\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     NMI_Handler                ; NMI Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     HardFault_Handler          ; Hard Fault Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     MemManage_Handler          ; MPU Fault Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     BusFault_Handler           ; Bus Fault Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     UsageFault_Handler         ; Usage Fault Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     SVC_Handler                ; SVCall Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DebugMon_Handler           ; Debug Monitor Handler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     PendSV_Handler             ; PendSV Handler 操作系统会用到的异常向量\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     SysTick_Handler            ; SysTick Handler 操作系统会用到的心跳定时器异常向量(没有操作系统时可以用作普通定时器中断)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第三部分:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、这里开始是中断向量表;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、各个向量的顺序是芯片设计的时候就定义好的，不能更改;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ; External Interrupts\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     WWDG_IRQHandler            ; Window Watchdog\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TAMPER_IRQHandler          ; Tamper\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     RTC_IRQHandler             ; RTC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     FLASH_IRQHandler           ; Flash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     RCC_IRQHandler             ; RCC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI0_IRQHandler           ; EXTI Line 0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI1_IRQHandler           ; EXTI Line 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI2_IRQHandler           ; EXTI Line 2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI3_IRQHandler           ; EXTI Line 3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI4_IRQHandler           ; EXTI Line 4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     ADC1_2_IRQHandler          ; ADC1_2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM1_UP_IRQHandler         ; TIM1 Update\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM2_IRQHandler            ; TIM2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     TIM3_IRQHandler            ; TIM3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     I2C1_EV_IRQHandler         ; I2C1 Event\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     I2C1_ER_IRQHandler         ; I2C1 Error\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     SPI1_IRQHandler            ; SPI1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     USART1_IRQHandler          ; USART1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     USART2_IRQHandler          ; USART2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     0                          ; Reserved\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     RTC_Alarm_IRQHandler        ; RTC Alarm through EXTI Line\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__Vectors_End\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__Vectors_Size  EQU  __Vectors_End - __Vectors\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第四部分:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;这部分开始可以称作Reset Handler实体，芯片上电后，经过BOOTROM后进入的用户可控最开始处的地方;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、如果想让Mcu正常使用c语言，务必在此处调用 main函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、__main() 不是main() 两者有着本质性的区别;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   3、__main()是c Library中的函数，Kei1开发环境中自带的c Library中的函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   4、main()是被 __main()调用的，__main()工作完成后最后一步就是调用main();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   5、__main()被调用之前，可以根据需要插入一个或多个其他功能函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                AREA    |.text|, CODE, READONLY             ;定义只读的代码段\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; Reset handler routine\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;复位中断服务程序，PROC…ENDP结构表示程序的开始和结束。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eReset_Handler    PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 EXPORT  Reset_Handler             [WEAK]   ;声明复位中断矢量Reset_Handler为全局属性\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                                            ;这样外部文档就可以调用此复位中断服务。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     IMPORT  __main                                         ;声明__main标号。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     IMPORT  SystemInit                                     ;声明SystemInit标号。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R0, =SystemInit                    ;跳转SystemInit地址执行\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 BLX     R0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R0, =__main                        ;跳转__main地址执行\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 BX      R0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; Dummy Exception Handlers (infinite loops which can be modified)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第五部分:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   1、[weak]指定了一个这个函数为\u0026#34;弱函数”;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   2、这些中断服务函教定义成弱函数的意义是，当中断出现时，需要有一个中断服务函数予以响应，但真实的\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      用户程序往往只会使用一部分中断，甚至不使用中断，所以以下这些函数给出了异常/中断服务函数的\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      默认实现，很简单，默认实现就是死循环汇编中的\u0026#34;B.\u0026#34;语句,相当于while(1);因为不知道用户是否会\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      用到多少中断，但这些服务函数又很重要，所以就把这些函数都\u0026#34;实现\u0026#34;并声明为弱函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   3、弱函数的意思是如果用户定义了同样名称的另一个函数，那么默认实现的弱函数就会被覆盖，比如\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      HardFault_Handler异常在下面有一个默认的实现，但这种默认的实现不能满足我的需要的时候，我可以\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      再重新定义一个HardEault Handler函数这个新定义的HardFault_Handler函数会覆盖原有的被声明\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;      为[WEAK]的弱函数;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   4、有很多种适合使用弱函数的场合，默认的异常/中断服务函数只是一种应用场景;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   5、在C语言中声明弱函数是在函数后加“__attribute((weak))”;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;----------------------------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eNMI_Handler     PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  NMI_Handler                [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHardFault_Handler\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  HardFault_Handler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMemManage_Handler\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  MemManage_Handler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBusFault_Handler\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  BusFault_Handler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUsageFault_Handler\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  UsageFault_Handler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSVC_Handler     PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  SVC_Handler                [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDebugMon_Handler\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DebugMon_Handler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePendSV_Handler  PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  PendSV_Handler             [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSysTick_Handler PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  SysTick_Handler            [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDefault_Handler PROC\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  WWDG_IRQHandler            [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  PVD_IRQHandler             [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TAMPER_IRQHandler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  RTC_IRQHandler             [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  FLASH_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  RCC_IRQHandler             [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI0_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI1_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI2_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI3_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI4_IRQHandler           [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel1_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel2_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel3_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel4_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel5_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel6_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  DMA1_Channel7_IRQHandler   [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  ADC1_2_IRQHandler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  USB_HP_CAN1_TX_IRQHandler  [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  USB_LP_CAN1_RX0_IRQHandler [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  CAN1_RX1_IRQHandler        [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  CAN1_SCE_IRQHandler        [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI9_5_IRQHandler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM1_BRK_IRQHandler        [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM1_UP_IRQHandler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM1_TRG_COM_IRQHandler    [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM1_CC_IRQHandler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM2_IRQHandler            [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  TIM3_IRQHandler            [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  I2C1_EV_IRQHandler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  I2C1_ER_IRQHandler         [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  SPI1_IRQHandler            [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  USART1_IRQHandler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  USART2_IRQHandler          [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  EXTI15_10_IRQHandler       [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  RTC_Alarm_IRQHandler        [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                EXPORT  USBWakeUp_IRQHandler       [WEAK]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;第六部分\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;   这部分比较简单，看一下第五部分的代码就可以理解下面的部分;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;---------------------------------------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWWDG_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePVD_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTAMPER_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eRTC_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFLASH_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eRCC_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI0_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI1_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI2_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI3_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI4_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel1_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel2_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel3_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel4_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel5_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel6_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDMA1_Channel7_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eADC1_2_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUSB_HP_CAN1_TX_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUSB_LP_CAN1_RX0_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCAN1_RX1_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCAN1_SCE_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI9_5_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM1_BRK_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM1_UP_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM1_TRG_COM_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM1_CC_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM2_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTIM3_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eI2C1_EV_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eI2C1_ER_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSPI1_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUSART1_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUSART2_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEXTI15_10_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eRTC_Alarm_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUSBWakeUp_IRQHandler\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                B       .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ENDP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ALIGN\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*******************************************************************************\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e; User Stack and Heap initialization\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;*******************************************************************************\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;IF…ELSE…ENDIF结构，判断是否使用DEF:__MICROLIB（此处为不使用）。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;若使用DEF:__MICROLIB，则将__initial_sp，__heap_base，__heap_limit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;亦即栈顶地址，堆始末地址赋予全局属性，使外部程序可以使用。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 IF      :DEF:__MICROLIB\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 EXPORT  __initial_sp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 EXPORT  __heap_base\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 EXPORT  __heap_limit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 ELSE\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 IMPORT  __use_two_region_memory    ;定义全局标号__use_two_region_memory。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 EXPORT  __user_initial_stackheap   ;声明全局标号__user_initial_stackheap，\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                                    ;这样外程序也可调用此标号\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__user_initial_stackheap                    ;标号__user_initial_stackheap，表示用户堆栈初始化程序入口\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 ;分别保存栈顶指针和栈大小，堆始地址和堆大小至R0，R1，R2，R3寄存器。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R0, =  Heap_Mem\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R1, =(Stack_Mem + Stack_Size)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R2, = (Heap_Mem +  Heap_Size)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 LDR     R3, = Stack_Mem\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 BX      LR\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 ALIGN\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 ENDIF\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 END;程序完毕\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"部分解释\"\u003e部分解释\u003c/h2\u003e\n\u003cp\u003e以上便是STM32的启动代码的完整解析，接下来对几个小地方做解释：\u003c/p\u003e","title":"STM32启动代码原理分析"},{"content":" STM32单片机是如何从上电运行到main()函数的；\n用三张图片基本就能理解了：\n参考： https://blog.csdn.net/weixin_39118482/article/details/79508747?spm=1001.2014.3001.5502 https://www.modb.pro/db/548699 https://www.cnblogs.com/yucloud/p/stm32_SystemInit_to_main.html\n1、函数的调用过程： 2、启动流程1（使用标准库，不使用Microlib） 如下图：\n3、启动流程2（使用Microlib） microlib 是缺省 C 库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。这些应用程序不在操作系统中运行。 microlib 进行了高度优化以使代码变得很小。它的功能比缺省 C 库少，并且根本不具备某些 ISOC 特性。某些库函数的运行速度也比较慢，例如， memcpy() 。 microlib与缺省C库之间的主要差异是： microlib不符合ISO C库标准。不支持某些ISO特性，并且其他特性具有的功能也较少； microlib不符合IEEE 754二进制浮点算法标准； microlib进行了高度优化以使代码变得很小； 无法对区域设置进行配置。缺省C区域设置是唯一可用的区域设置； 不能将main()声明为使用参数，并且不能返回内容； 不支持stdio，但未缓冲的stdin、stdout和stderr除外； microlib对C99函数提供有限的支持； microlib不支持操作系统函数； microlib不支持与位置无关的代码； microlib不提供互斥锁来防止非线程安全的代码； microlib不支持宽字符或多字节字符串； 与stdlib不同，microlib不支持可选择的单或双区内存模型。microlib只提供双区内存模型，即单独的堆栈和堆区。\n启动流程如下图：\n4、其他 关于stm32的启动文件： https://www.fan-pengfei.top/2023/02/25/STM32%E5%90%AF%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/#more\n","permalink":"https://fan-pengfei.top/posts/stm32%E7%9A%84%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/","summary":"\u003cblockquote\u003e\n\u003cp\u003eSTM32单片机是如何从上电运行到main()函数的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e用三张图片基本就能理解了：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e参考：\n\u003ca href=\"https://blog.csdn.net/weixin_39118482/article/details/79508747?spm=1001.2014.3001.5502\"\u003ehttps://blog.csdn.net/weixin_39118482/article/details/79508747?spm=1001.2014.3001.5502\u003c/a\u003e\n\u003ca href=\"https://www.modb.pro/db/548699\"\u003ehttps://www.modb.pro/db/548699\u003c/a\u003e\n\u003ca href=\"https://www.cnblogs.com/yucloud/p/stm32_SystemInit_to_main.html\"\u003ehttps://www.cnblogs.com/yucloud/p/stm32_SystemInit_to_main.html\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1函数的调用过程\"\u003e1、函数的调用过程：\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/stm32%E7%9A%84%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/img-1.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2启动流程1使用标准库不使用microlib\"\u003e2、启动流程1（使用标准库，不使用Microlib）\u003c/h2\u003e\n\u003cp\u003e如下图：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/stm32%E7%9A%84%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/img-2.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"3启动流程2使用microlib\"\u003e3、启动流程2（使用Microlib）\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003emicrolib 是缺省 C 库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。这些应用程序不在操作系统中运行。 microlib 进行了高度优化以使代码变得很小。它的功能比缺省 C 库少，并且根本不具备某些 ISOC 特性。某些库函数的运行速度也比较慢，例如， memcpy() 。\nmicrolib与缺省C库之间的主要差异是：\nmicrolib不符合ISO C库标准。不支持某些ISO特性，并且其他特性具有的功能也较少；\nmicrolib不符合IEEE 754二进制浮点算法标准；\nmicrolib进行了高度优化以使代码变得很小；\n无法对区域设置进行配置。缺省C区域设置是唯一可用的区域设置；\n不能将main()声明为使用参数，并且不能返回内容；\n不支持stdio，但未缓冲的stdin、stdout和stderr除外；\nmicrolib对C99函数提供有限的支持；\nmicrolib不支持操作系统函数；\nmicrolib不支持与位置无关的代码；\nmicrolib不提供互斥锁来防止非线程安全的代码；\nmicrolib不支持宽字符或多字节字符串；\n与stdlib不同，microlib不支持可选择的单或双区内存模型。microlib只提供双区内存模型，即单独的堆栈和堆区。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e启动流程如下图：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/stm32%E7%9A%84%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/img-3.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"4其他\"\u003e4、其他\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e关于stm32的启动文件：\n\u003ca href=\"https://www.fan-pengfei.top/2023/02/25/STM32%E5%90%AF%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/#more\"\u003ehttps://www.fan-pengfei.top/2023/02/25/STM32%E5%90%AF%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/#more\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","title":"STM32的启动过程"},{"content":" 分散加载（scatter）文档是一个文本文档，它可以用来描述ARM连接器生成映像文档时所需要的信息； 参考：https://blog.csdn.net/KXue0703/article/details/114018759\n一、基础知识 为了充分理解分散加载文档的魅力，需要对工程编译后的内容有详细的了解。 Keil 编译后的内容如下所示：\nCode：为程序代码部分；\nRO-Data：表示程序定义的常量及 const 型数据；\nRW-Data：表示已经初始化的静态变量，变量有初值；\nZI-Data：表示未初始化的静态变量，变量无初值；\n当 Keil 工程编译完成后，查看其 map 文档，可得到结果如下程序清单：\nCode (inc. data) RO Data RW Data ZI Data Debug 4194 230 714 16 1640 72715 Grand Totals 4194 230 714 16 1640 72715 ELF Image Totals 4194 230 714 16 0 0 ROM Totals Total RO Size (Code + RO Data) 4908 (4.79kB) Total RW Size (RW Data + ZI Data) 1656 (1.62kB) Total ROM Size (Code + RO Data + RW Data) 4924 (4.81kB) 由map文档可以看出： ROM Size = Code＋RO-Data＋RW-Data = 4.81kB； RAM Size = RW-Data＋ZI-Data = 1.62kB；\n大小都很小，是因为这只是一个基础工程，是一个点灯程序；\n为什么上述的 RW-Data 既占用 Flash 又占用 RAM 呢？变量不是放在 RAM 中的吗？为什么还会占用 Flash？因为 RW 数据不能像 ZI 那样“无中生有”的，ZI 段数据只要求其所在的区域全部初始化为零，所以只需要程序根据编译器给出的 ZI 基址及大小来将相应的 RAM清零。但 RW 段数据却不这样做，所以编译器为了完成所有 RW 段数据赋值，其先将 RW 段的所有初值，先保存到 Flash 中，程序执行时，再 Flash 中的数据搬运到 RAM 中，所以 RW 段即占用 Flash 又占用 RAM，且占用的空间大小是相等的；\n这里有必要再了解一下，ZI 和 RW 段数据的赋值在某个工程中是在什么地方实现的呢？首先变量必先要初始化才能使用，否则初值不正确，而在 main() 函数后变量已经可以正常使用，那就是说变量的初始化是在之前完成的，查看这之前的代码只有__main() 一个函数，除了赋初值外，都还做了什么呢？\n函数__main()主要由以下两部分功能组成，如下所示:\n__main()：完成代码和数据的拷贝，并把 ZI 数据区清零。代码拷贝可将代码拷贝到另外一个映射空间并执行 (例如将代码拷贝到 RAM 执行)； 数据拷贝完成 RW 段数据赋值；数据区清零完成 ZI 段数据赋值。以上的代码和分散加载文档密切相关；\nrt_entry()：进行 STACK 和 HEAP 等的初始化。最后_rt_entry跳进 main()函数入口。当 main()函数执行完后， _rt_entry 又将控制权交还给调试器。；\n二、什么是分散加载文档 分散加载（scatter）文档是一个文本文档，它可以用来描述ARM连接器生成映像文档时所需要的信息；\n如果不用scatter文档指定，那么ARM连接器会按照默认的方式来生成映像文档，一般情况下我们是不需要使用分散加载文档的，但在某些场合，我们希望把某些数据放在指定的地址处，那么这时候scatter文档就发挥了非常大的作用。而且scatter文档用起来非常简单好用；\n在分散加载文档中可以指定下列信息：\n各个加载时域（load region）加载时的起始地址（load address）和最大尺寸（max size）；\n各个加载时域的属性；\n从每个加载时域中分割出来的运行时域；\n各个运行时域（excution region）的运行起始地址（excution address）和最大尺寸（max size）；\n各个运行时域的存储访问特性；\n各个运行时域的属性；\n各个运行时域中包含的输入段；\n三、为什么需要分散加载文档 一般情况下，我们可以不独自编写分散加载文档，ARM连接器直接按照默认的方式来生成映像文档即可，但是在某些场合，我们希望将某些数据放在指定的位置，此时分散加载文档就发挥了非常发的作用；\n比如在下面几种情况就充分体现了分散加载文档的优势：\n==复杂内存映射==：如果必须将代码和数据放在多个不同的内存区域中，则需要使用详细指令指定将哪些数据放在哪个内存空间中；\n==不同类型的内存==：许多系统都包含多种不同的物理内存设备，如闪存、 ROM、 SDRAM 和快速 SRAM。分散加载描述可以将代码和数据与最适合的内存类型相匹配。例如，可以将中断代码放在快速 SRAM 中以缩短中断等待时间，而将不经常使用的配置信息放在较慢的闪存中；\n==位于固定位置的函数==：可以将函数放在内存中的固定位置，即使已修改并重新编译周围的应用程序；\n==使用符号标识堆和堆栈==：链接应用程序时，可以为堆和堆栈位置定义一些符号；\n四、分散加载文档的基本特点 编译后输出的映像文档中各段是首尾相连的，中间没有空闲的区域，他们的先后关系是根据链接时参数的先后次序决定的armlinker -file1.o file2.o …\nscatter用于将编译后的映像文档中的特定段加载到多个分散的指定内存区域；\n两类域(region)：执行域(execution region)和加载域(load region)；\n加载域，该映像文档开始运行前存放的区域，即当系统启动或加载时应用程序存放的区域；\n执行域，映像文档运行时的区域，即系统启动后，应用程序进行执行和数据访问的存储器区域，系统在实时运行时可以有一个或多个执行块；\nscatter本身并不能对映像实现“解压缩”，编译器读入scatter文档之后会根据其中的各种地址生成启动代码了，实现对映像的加载，而这一段代码就是*(InRoot$$Sections)它是__main()的一部分。这就是在汇编启动代码的最后跳转到__main()而不是跳向main()的原因之一；\n起始地址与加载域重合的执行域称为root region，*(InRootSections)必须放在这个执行域中，否则链接的时候会报错；\n五、分散加载文档的语法 分散加载文档一般由1个加载时域和1到多个运行时域组成（当然也可以包含2个以上的加载时域）。其大致的结构如下图所示：\n5.1 加载时域描述 加载时域语法格式如下所示:\nload_region_name(base_address|(\u0026#34;+\u0026#34;offset))[attribute_list][max_size] { execution_region_description+ } ==load_region_name==：为本加载时域的名称，名称可以按照用户意愿自己定义，该名称中只有前 31 个字符有意义。它仅仅用来唯一的标识一个加载时域，而不像运行时域的名称除了唯一的标识一个运行时域外，还用来构成连接器连接生成的连接符号。\n==base_designator==：用来表示本加载时域的起始地址，可以有下面两种格式中的一种： base_address：表示本加载时域中的对象在连接时的起始地址，地址必须是字对齐的；\n+offset：表示本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移量 offset 字节处。本加载时域是第一个加载时域，则它的起始地址即为 offset， offset 的值必须能被 4 整除。\n==attribute_list==：指定本加载时域内容的属性，包含以下几种， 默认加载时域的属性是ABSOLUTE。 PI – 位置无关属性。\nRELOC – 重定位。\nOVERLAY – 覆盖。\nABSOLUTE – 起始地址由base_designator指定（默认属性）。\n==max_size==：指定本加载时域的最大尺寸。如果本加载时域的实际尺寸超过了该值，连接器将报告错误， 默认取值为 0xFFFFFFFF。\n==execution_region_description==：表示运行时域，后面有个+号，表示其可以有一个或者多个运行时域，关于运行时域的介绍请看后面。\n5.2 运行时域描述 运行时域语法格式如下所示:\nexec_region_name(base_address|\u0026#34;+\u0026#34;offset)[attribute_list][max_size|\u0026#34; \u0026#34;length] { input_section_description* } ==exec_region_name==： 为为本加载时域的名称，名称可以按照用户意愿自己定义， 该名称中只有前 31 个字符有意义。它除了唯一的标识一个运行时域外，还用来构成连接器生成的连接符号。\n==base_designator==：用来表示本加载时域的起始地址，可以有下面两种格式中的一种： base_address：表示本运行时域中的对象在连接时的起始地址，地址必须是字对齐的；\n+offset：表示本运行时域中的对象在连接时的起始地址是在前一个加运行时域的结束地址后偏移量 offset 字节处。本运行时域是第一个加载时域，则它的起始地址即为 offset， offset 的值必须能被 4 整除。\n==attribute_list==：指定本加载时域内容的属性，包含以下几种， 默认加载时域的属性是ABSOLUTE。 PI – 位置无关属性。\nRELOC – 重定位。\nOVERLAY – 覆盖。\nABSOLUTE – 起始地址由base_designator指定（默认属性）。\nFIXED – 固定地址。此时该域加载时域地址和运行时域地址是相同的，而且都是通过base_designator指定的，而且base_designator必须是绝对地址或者offset为0。\n==max_size==：指定本运行时域的最大尺寸。如果本运行时域的实际尺寸超过了该值，连接器将报告错误， 默认取值为 0xFFFFFFFF。\n==length==：如果指定的长度为负值，则将 base_address 作为区结束地址。它通常与EMPTY 一起使用，以表示在内存中变小的堆栈。\n5.3 输入段描述 输入段语法描述如下所示：\nmodule_select_pattern [ \u0026#34;(\u0026#34; input_section_selector ( \u0026#34;,\u0026#34; input_section_selector )* \u0026#34;)\u0026#34; ] (\u0026#34;+\u0026#34; input_section_attr | input_section_pattern | input_symbol_pattern) ==module_select_pattern==：目标文档滤波器，支持使用通配符“”与“?”。其中符号“”代表零个或多个字符，符号“？”代表单个字符。进行匹配时所有字符不区分大小写。\n==nput_section_attr==：属性选择器与输入段属性相匹配。每个 input_section_attr 的前面有一个“+”号。如果指定一个模式以匹配输入段名称，名称前面必须有一个“+”号。可以省略紧靠“+”号前面的任何逗号。 选择器不区分大小写（可以识别的为属性First、Last）。\n通过使用特殊模块选择器模式.ANY ，可以将输入段分配给执行区，而无需考虑其父模块。可以使用一个或多个.ANY 模式以任意分配方式填充运行时域。在大多数情况下，使用单个.ANY 等效于使用*模块选择器。\n六、 分散加载应用举例 6.1 一个加载域多个执行域的情况 以ST的CortexM4核的低功耗STM32L476VC芯片为例，其资源如下：\nFlash基地址：0x08000000，小为256kB； RAM基地址：0x20000000，大小为96kB；\nLR_IROM1 0x08000000 0x00040000 { ; 定义一个加载时域，域基址：0x08000000，域大小为 0x00040000 ER_IROM1 0x08000000 0x00040000 { ; 定义一个运行时域，第一个运行时域必须和加载 ; 时域起始地址相同，否则库不能加载到该时域的 ; 错误，其域大小一般也和加载时域大小相同 *.o (RESET, +First) ; 将 RESET 段最先加载到本域的起始地址外，即 ; RESET 的起始地址为 0， RESET 存储的是矢量表 } ER_IROM2 + 0 { ; 自定义的应用程序信息（软硬件版本、发布时间、升级信息等） ; 将其固定的放在中断矢量表之后，便于通过.bin文档查看程序的信息 *.o (SECTION_APP_INFO, +First) } ER_IROM3 + 0 { ; 初始化相关代码 *(InRoot$$Sections) .ANY (+RO) ; 加载所有匹配目标文档的只读属性数据，包含： ; Code、 RW-Code、 RO-Data。 } RW_IRAM1 0x20000000 0x00018000 { ; 定义一个运行时域，域基址： 0x20000000 ，域大 ; 小为 0x00018000 ，对应实际 RAM 大小 .ANY (+RW +ZI) ; 这里也可以用 * 号替代 .ANY } } 如上分散加载文档所示：它包含1个加载时域，3个运行时域。其第一个运行时域与加载时域的基地址一致（在嵌入式系统中，必须首先加载中断矢量表，且必须与加载时域的基地址保持一致，否则编译时会报错）。\n==SECTION_APP_INFO==：用户自定义的一块区域，记录终端的版本、升级等相关信息，它是一块固定的位置，紧随中断矢量变之后。它为一个结构体形式。 结构体形式如下：\n/** * @brief 应用程序信息(必须为4字节对齐。同时用于IAP和APP) */ typedef struct { UINT8 ucFactory[FACTORY_LENGTH]; /* 厂商标志 */ UINT8 ucProduct[PRODUCT_LENGTH]; /* 产品标志 */ UINT8 ucProtocol[PROTOCOL_LENGTH]; /* 规约标志 */ UINT8 ucVerSW[VER_SW_LENGTH]; /* 软件版本 */ UINT8 ucVerDateSW[DATE_SW_LENGTH]; /* 软件发布日期 */ UINT8 ucVerHW[VER_SW_LENGTH]; /* 硬件版本 */ UINT8 ucVerDateHW[DATE_HW_LENGTH]; /* 硬件发布日期 */ UINT64 udwUpGradeFlag; /* 升级标志。主要存放升级方式。该值在升级完成后，改为 以上定义的各种值，以指示升级方式 */ UINT64 udwAppFlag; /* 应用程序标志。该值在升级中动态改变。升级完成后必为APP_FLAG_UPGRADED */ UINT64 udwCRC; /* 程序本身的 CRC。用来保证数据的完整性 */ UINT64 udwLength; /* 程序本身的长度。仅指计算 CRC 的长度 */ } APP_INFO, IAP_INFO; 实现形式如下：\nconst APP_INFO stAppInfo __attribute__((section(\u0026#34;SECTION_IAP_INFO\u0026#34;))) = { FACTORY,\t/* 厂商标识，宏值 */ PRODUCT,\t/* 产品标识，宏值 */ PROTOCOL,\t/* 规约标识，宏值 */ VER_SW,\t/* 软件版本，宏值 */ DATE_SW,\t/* 软件版本发布日期，宏值 */ VER_HW,\t/* 硬件版本，宏值 */ DATE_HW,\t/* 硬件版本发布日期，宏值 */ UPDATE_FLAG,\t/* 升级标志，宏值 */ APP_FLAG,\t/* 应用程序标识，宏值 */ }; .ANY (+RW +ZI)可以由*（+RW +ZI）代替。\n6.2 以属性FIXED实现一个加载域多个执行域的情况 下面的这种情况是对6.1中分散加载文档的略微修改，它将第2个加载时域（ER_IROM2 ）的基地址固定为了0x08000400 。这样为中断矢量表预留的空间大小为0x400字节，冗余的部分以0x00填充（这样写会导致生成的映像文档变大）。\nLR_IROM1 0x08000000 0x00040000 { ; load region size_region ER_IROM1 0x08000000 0x400 { ; load address = execution address *.o (RESET, +First) } ER_IROM2 0x08000400 FIXED 0x0003C000 { ; 应用程序信息 *.o (SECTION_APP_INFO, +First) ;First标识该输入段是运行该运行时域的第一个输入段 } ER_IROM3 + 0 { ; 初始化相关代码 *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00018000 { ; RW data .ANY (+RW +ZI) } } 对应的.bin文档如下图所示：\n下面的这种情况也是是对6.1中分散加载文档的略微修改，它将第2个加载时（ER_IROM2 ）的基地址固定为了0x08000400 ，第3个加载域（ER_IROM23）的基地址固定为了0x08000800，这样为中断矢量表和SECTION_APP_INFO均预留的空间大小为0x400字节，冗余的部分以0x00填充；\nLR_IROM1 0x08000000 0x00040000 { ; load region size_region ER_IROM1 0x08000000 0x400 { ; load address = execution address *.o (RESET, +First) } ER_IROM2 0x08000400 FIXED 0x0003C000 { ; 应用程序信息 *.o (SECTION_APP_INFO, +First) } ER_IROM3 0x08000800 FIXED 0x00038000 { ; 初始化相关代码 *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00018000 { ; RW data .ANY (+RW +ZI) } } 6.3 多块 RAM 的分散加载文档配置 还是上述的 MCU，假设其增加了另外一块 RAM，其资源如下：\nFlash基地址：0x08000000，小为256KB\nRAM基地址：0x10000000，大小为24KB\nRAM基地址：0x20000000，大小为24KB\n若想将两块RAM都使用起来（可使用64KB），那么分散加载文档的写法应该如下：\nLR_IROM1 0x08000000 0x00040000 { ; load region size_region ER_IROM1 0x08000000 0x400 { ; load address = execution address *.o (RESET, +First) } ER_IROM2 0x08000400 FIXED 0x0003C000 { ; 应用程序信息 *.o (SECTION_APP_INFO, +First) ;First标识该输入段是运行该运行时域的第一个输入段 } ER_IROM3 + 0 { ; 初始化相关代码 *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x100000000 0x6000 { ; 定义的RAM1的运行时域 .ANY (+RW +ZI) ; 使用.ANY 进行随意分配变量，这里不能使用*号 ; 替代， *表示匹配有所有的目标文档，这样变量就 ; 无法分配到第二块 RAM 空间了 } RW_IRAM2 0x200000000 0x6000 { ; 定义的RAM1的运行时域 .ANY (+RW +ZI) ; 同样使用.ANY 随意分配变量的方式 } ; 如果还有另多的 RAM 块，在这里增加新的运行 ; 时域即可，格式和 RAM2 的定义相同 } 那么问题来了，以上的分散加载机制确实可以使两个RAM空间（64KB）都使用起来，但是它并不等同于一个64KB的RAM。在实际应用中我们可能会遇到如下的情况：\n比如我们在UpComm.c中定义了一个30KB的数组Test，如下面的函数清单所示：\n// main.c 文档 //...... unsigned char Test[30 * 1024]; // 定义一个 30KB 的数组 //unsigned char Test1[15 * 1024]; // 定义第一个 15KB 数组 //unsigned char Test2[15 * 1024]; // 定义第二个 15KB 数组 //...... // end of file 程序在编译时会出现错误，并提示没有足够的空间，为什么呢？原因为数组是一个整体，其内部元素的地址是连续的，不能分割的， 但是在两个不连续的24KB 空间中，是没办法分配出一个连续的 30KB 地址空间，所以编译会提示空间不足，分配 30KB 数组失败。\n去掉Test[30 * 1024]，改为两个15KB的数组Test1[15 * 1024]和Test2[15 * 1024]，编译时依然出现错误，并体会没有足够的空间，这又是为什么呢？\n以上的问题还得从.ANY通配符的作用开始说起，ANY 是一个通配符， 当其与以下内容之一相匹配时将进行选择:\n包含段和目标文档的名称；\n库成员名称（不带前导路径名）；\n库的完整名称（包括路径名）；\n第一个匹配项为：包含段和目标文档,“目标文档”，不是其它，即是说一个 C 文档编译后，其所有的变量、代码都会作为一个整体。所以定义两个15KB 和定义了一个 30KB，在编译器看来都是一样，就是这个C文档总共定义了30KB 的空间，我要用 20KB 的空间来分配它，因此会出现同样的错误。\n关于大数组分配的解决方法，有两种，分别如下：\n将数组分开在不同的 C 文档中定义，避免在同一个 C 文档定义的数据大小总量超过其中最大的分区；\n将一个 C 数组，使用段定义，使其从该 C 文档中独立出来，这样编译器就不会将它们作为一个整体来划分空间了（如下所示）。==关于段的详细说明见下面的附录==；\n#pragma arm section zidata = \u0026#34;SRAM\u0026#34; // 在 C 文档中定义新的段 unsigned char Test1[15 * 1024]; // 定义第一个15KB数组 #pragma arm section // 恢复原有的段 unsigned char Test2[15 * 1024]; // 定义第二个15KB数组，这20KB数组不会和 // Test1 作为一个整体来划分空间 6.4 多块 Flash 的分散加载文档配置 假设有一个MCU，它有两块独立的Flash，一个RAM，资源分配如下：\nFlash1 基址： 0x00000000，大小：256 Kbyte；\nFlash2 基址： 0x20000000，大小：2048 Kbyte；\nRAM 基址： 0x10000000，大小： 32 Kbyte；\n注意这里多增加的一块的不是RAM，而是Flash，其情况会如何呢？假设其相同，那写法应该就是如程序清单所示的样子：\nLR_IROM1 0x00000000 0x00040000 { ER_IROM1 0x00000000 0x00040000 { ; 定义 Flash1 运行时域 *.o (RESET, +First) ; 先加载矢量表 *(InRoot$$Sections) .ANY (+RO) ; 随意分配只读数据 } ER_IROM2 0x20000000 0x00200000 { ; 定义 Flash2 运行时域 .ANY (+RO) ; 随意分配只读数据 } RW_IRAM1 0x10000000 0x00008000 { .ANY (+RW +ZI) } } 令人遗憾的是，编译出错，为什么会出错呢，仔细分析一下：代码加载时，仅加载到了ROM1中（加载地址是ROM1），但是运行时只读数据却是在ROM1、ROM2随机分配的。当内核访问ROM2的数据时，那必须先将数据复制到ROM2中才能访问（但这一操作是不能实现的），当然导致编译出错。\n正确的编写方式应该如下：\nLR_IROM1 0x00000000 0x00040000 { ; 定义 Flash1 的加载域 ER_IROM1 0x00000000 0x00040000 { *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) ; 随机分配只读数据 } RW_IRAM1 0x10000000 0x00008000 { .ANY (+RW +ZI) } } LR_IROM2 0x20000000 0x00200000 { ; 定义 Flash2 的加载域 ER_IROM2 0x20000000 0x00200000 { .ANY (+RO) ; 随机分配只读数据，代码不会进行拷贝 } } 这样写能够解决双Flash加载的问题，但是同样有一个问题，那就是，编译时会生成2份bin文档，需要分别两次烧录代码。\n6.5 关于加载时域与第一个运行时域的关系的说明 第一个运行时域存放的代码不会进行额外拷贝因为分散加载文档有一项很强大的功能，就是可以将 Flash的代码拷贝到 RAM中运行，这一段拷贝代码就存在于__main()函数中，但拷贝代码不能拷贝自身，所以必须规定有一个运行时域中存放的代码是不会被拷贝的，这个指的就是第一个运行时域。一段代码必须先完成拷贝，才能被执行。换句理解就是拷贝代码前包括自身的所有代码都不能拷贝，也就是说这些代码全部都必须放在第一个运行时域中。\n规定其余运行时域中存放的代码均会被拷贝一个加载时域，只需要一个不拷贝的运行时域即可。所以规定其余所有的运行时域中的代码均会被拷贝。\n第一个运行时域的基址必须与加载域基址相同为了保证第一个运行时域的代码能够被正确存储和执行，因此要求第一个运行时域的基址必须和加载时域的基址相同。\n","permalink":"https://fan-pengfei.top/posts/%E5%88%86%E6%95%A3%E5%8A%A0%E8%BD%BD%E6%96%87%E6%A1%A3/","summary":"\u003cblockquote\u003e\n\u003cp\u003e分散加载（scatter）文档是一个文本文档，它可以用来描述ARM连接器生成映像文档时所需要的信息；\n参考：\u003ca href=\"https://blog.csdn.net/KXue0703/article/details/114018759\"\u003ehttps://blog.csdn.net/KXue0703/article/details/114018759\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一基础知识\"\u003e一、基础知识\u003c/h2\u003e\n\u003cp\u003e为了充分理解分散加载文档的魅力，需要对工程编译后的内容有详细的了解。\nKeil 编译后的内容如下所示：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eCode：为程序代码部分；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eRO-Data：表示程序定义的常量及 const 型数据；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eRW-Data：表示已经初始化的静态变量，变量有初值；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eZI-Data：表示未初始化的静态变量，变量无初值；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e当 Keil 工程编译完成后，查看其 map 文档，可得到结果如下程序清单：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCode (inc. data)   RO Data    RW Data    ZI Data      Debug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4194        230        714         16       1640      72715   Grand Totals\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4194        230        714         16       1640      72715   ELF Image Totals\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e4194        230        714         16          0          0   ROM Totals\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RO  Size (Code + RO Data)                 4908 (4.79kB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal RW  Size (RW Data + ZI Data)              1656 (1.62kB)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTotal ROM Size (Code + RO Data + RW Data)       4924 (4.81kB)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e由map文档可以看出：\nROM Size = Code＋RO-Data＋RW-Data = 4.81kB；\nRAM Size = RW-Data＋ZI-Data = 1.62kB；\u003c/p\u003e","title":"分散加载文档"},{"content":" 今天用到了git rebase命令来进行合并两次提交，顺带梳理下rebase命令的用法； 参考：https://www.jianshu.com/p/4a8f4af4e803\nrebase在git中是一个非常有魅力的命令，使用得当会极大提高自己的工作效率；相反，如果乱用，会给团队中其他人带来麻烦。它的作用简要概括为：可以对某一段线性提交历史进行编辑、删除、复制、粘贴；因此，合理使用rebase命令可以使我们的提交历史干净、简洁！\n前提：不要通过rebase对任何已经提交到公共仓库中的commit进行修改（你自己一个人玩的分支除外）；\n一、合并多个commit为一个完整commit 当我们在本地仓库中提交了多次，在我们把本地提交push到公共仓库中之前，为了让提交记录更简洁明了，我们希望把如下分支B、C、D三个提交记录合并为一个完整的提交，然后再push到公共仓库。\n现在我们在测试分支上添加了四次提交，我们的目标是把最后三个提交合并为一个提交：\n这里我们使用命令:\ngit rebase -i [startpoint] [endpoint] 其中-i的意思是--interactive，即弹出交互式的界面让用户编辑完成合并操作，[startpoint] [endpoint]则指定了一个编辑区间，如果不指定[endpoint]，则该区间的终点默认是当前分支HEAD所指向的commit(注：该区间指定的是一个前开后闭的区间)。 在查看到了log日志后，我们运行以下命令：\ngit rebase -i 36224db 或:\ngit rebase -i HEAD~3 然后我们会看到如下界面:\n上面未被注释的部分列出的是我们本次rebase操作包含的所有提交，下面注释部分是git为我们提供的命令说明。每一个commit id 前面的pick表示指令类型，git 为我们提供了以下几个命令:\npick：保留该commit（缩写:p） reword：保留该commit，但我需要修改该commit的注释（缩写:r） edit：保留该commit, 但我要停下来修改该提交(不仅仅修改注释)（缩写:e） squash：将该commit和前一个commit合并（缩写:s） fixup：将该commit和前一个commit合并，但我不要保留该提交的注释信息（缩写:f） exec：执行shell命令（缩写:x） drop：我要丢弃该commit（缩写:d）\n根据我们的需求，我们将commit内容编辑如下:\n然后是注释修改界面:\n编辑完保存即可完成commit的合并了：\n二、将某一段commit粘贴到另一个分支上 当我们项目中存在多个分支，有时候我们需要将某一个分支中的一段提交同时应用到其他分支中，就像下图：\n我们希望将develop分支中的C~E部分复制到master分支中，这时我们就可以通过rebase命令来实现（如果只是复制某一两个提交到其他分支，建议使用更简单的命令:git cherry-pick）。\n在实际模拟中，我们创建了master和develop两个分支:\nmaster分支:\ndevelop分支:\n我们使用命令的形式为:\ngit rebase [startpoint] [endpoint] --onto [branchName] 其中，[startpoint] [endpoint]仍然和上一个命令一样指定了一个编辑区间(前开后闭)，--onto的意思是要将该指定的提交复制到哪个分支上。\n所以，在找到C(90bc0045b)和E(5de0da9f2)的提交id后，我们运行以下命令：\ngit rebase 90bc0045b^ 5de0da9f2 --onto master 注:因为[startpoint] [endpoint]指定的是一个前开后闭的区间，为了让这个区间包含C提交，我们将区间起始点向后退了一步。\n运行完成后查看当前分支的日志:\n可以看到，C~E部分的提交内容已经复制到了G的后面了，大功告成？NO！我们看一下当前分支的状态:\n当前HEAD处于游离状态，实际上，此时所有分支的状态应该是这样:\n所以，虽然此时HEAD所指向的内容正是我们所需要的，但是master分支是没有任何变化的，git只是将C~E部分的提交内容复制一份粘贴到了master所指向的提交后面，我们需要做的就是将master所指向的提交id设置为当前HEAD所指向的提交id就可以了，即:\ngit checkout master git reset --hard 0c72e64 这样就大功告成了！\n","permalink":"https://fan-pengfei.top/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/","summary":"\u003cblockquote\u003e\n\u003cp\u003e今天用到了git rebase命令来进行合并两次提交，顺带梳理下rebase命令的用法；\n参考：\u003ca href=\"https://www.jianshu.com/p/4a8f4af4e803\"\u003ehttps://www.jianshu.com/p/4a8f4af4e803\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003erebase在git中是一个非常有魅力的命令，使用得当会极大提高自己的工作效率；相反，如果乱用，会给团队中其他人带来麻烦。它的作用简要概括为：可以对某一段线性提交历史进行编辑、删除、复制、粘贴；因此，合理使用rebase命令可以使我们的提交历史干净、简洁！\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e前提：不要通过rebase对任何已经提交到公共仓库中的commit进行修改（你自己一个人玩的分支除外）；\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"一合并多个commit为一个完整commit\"\u003e一、合并多个commit为一个完整commit\u003c/h3\u003e\n\u003cp\u003e当我们在本地仓库中提交了多次，在我们把本地提交push到公共仓库中之前，为了让提交记录更简洁明了，我们希望把如下分支B、C、D三个提交记录合并为一个完整的提交，然后再push到公共仓库。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"0ba5c5a59eac26db62f1de57ccd6040\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e现在我们在测试分支上添加了四次提交，我们的目标是把最后三个提交合并为一个提交：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"70c9817cfaf9fe0b38bf3bbb97197bd\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这里我们使用命令:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase -i  \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003estartpoint\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e  \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eendpoint\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中\u003ccode\u003e-i\u003c/code\u003e的意思是\u003ccode\u003e--interactive\u003c/code\u003e，即弹出交互式的界面让用户编辑完成合并操作，\u003ccode\u003e[startpoint]\u003c/code\u003e  \u003ccode\u003e[endpoint]\u003c/code\u003e则指定了一个编辑区间，如果不指定\u003ccode\u003e[endpoint]\u003c/code\u003e，则该区间的终点默认是当前分支\u003ccode\u003eHEAD\u003c/code\u003e所指向的\u003ccode\u003ecommit\u003c/code\u003e(注：该区间指定的是一个前开后闭的区间)。\n在查看到了log日志后，我们运行以下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase -i 36224db\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e或:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase -i HEAD~3\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后我们会看到如下界面:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"326677b29707761988e3d89c2a5ef58\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e上面未被注释的部分列出的是我们本次rebase操作包含的所有提交，下面注释部分是git为我们提供的命令说明。每一个commit id 前面的\u003ccode\u003epick\u003c/code\u003e表示指令类型，git 为我们提供了以下几个命令:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003epick：保留该commit（缩写:p）\nreword：保留该commit，但我需要修改该commit的注释（缩写:r）\nedit：保留该commit, 但我要停下来修改该提交(不仅仅修改注释)（缩写:e）\nsquash：将该commit和前一个commit合并（缩写:s）\nfixup：将该commit和前一个commit合并，但我不要保留该提交的注释信息（缩写:f）\nexec：执行shell命令（缩写:x）\ndrop：我要丢弃该commit（缩写:d）\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e根据我们的需求，我们将commit内容编辑如下:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"5939a788fc6c0730435233001dc4cf4\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后是注释修改界面:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"d1cc8502accee35249a4a248663085c\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e编辑完保存即可完成commit的合并了：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"3a0723ad06ce0cff218b931b1a0b422\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-6.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"二将某一段commit粘贴到另一个分支上\"\u003e二、将某一段commit粘贴到另一个分支上\u003c/h3\u003e\n\u003cp\u003e当我们项目中存在多个分支，有时候我们需要将某一个分支中的一段提交同时应用到其他分支中，就像下图：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"d4b3c401612bba5cd4cac2efb15450c\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-7.png\"\u003e\u003c/p\u003e\n\u003cp\u003e我们希望将develop分支中的C~E部分复制到master分支中，这时我们就可以通过rebase命令来实现（如果只是复制某一两个提交到其他分支，建议使用更简单的命令:\u003ccode\u003egit cherry-pick\u003c/code\u003e）。\u003c/p\u003e\n\u003cp\u003e在实际模拟中，我们创建了master和develop两个分支:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003emaster分支:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"55dda3c4a0bf2cd83d03b5714ec9107\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003edevelop分支:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"c2a745be2db81114facc5c699da5170\" loading=\"lazy\" src=\"/posts/git%E4%B8%AD%E7%9A%84rebase%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93/img-9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e我们使用命令的形式为:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003estartpoint\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eendpoint\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e --onto \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ebranchName\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中，\u003ccode\u003e[startpoint]\u003c/code\u003e  \u003ccode\u003e[endpoint]\u003c/code\u003e仍然和上一个命令一样指定了一个编辑区间(前开后闭)，\u003ccode\u003e--onto\u003c/code\u003e的意思是要将该指定的提交复制到哪个分支上。\u003c/p\u003e\n\u003cp\u003e所以，在找到C(90bc0045b)和E(5de0da9f2)的提交id后，我们运行以下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit  rebase 90bc0045b^ 5de0da9f2 --onto master\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e注:因为\u003ccode\u003e[startpoint]\u003c/code\u003e  \u003ccode\u003e[endpoint]\u003c/code\u003e指定的是一个前开后闭的区间，为了让这个区间包含C提交，我们将区间起始点向后退了一步。\u003c/p\u003e","title":"git中的rebase用法小结"},{"content":" 当需要同时读取多个点的温度数据时，DS18B20就是一个很好的选择，不仅精度高，而且还可以单总线挂载多个传感器以节省IO口的使用；\n初始化函数 DS18B20的通信协议为单总线通信协议；\n首先由主机发送一个复位脉冲约480-960us；然后总线被拉高；在15-60us之后传感器向单片机发送一个约60-240us的存在脉冲，然后总线被拉高。\n/** * @brief 主机给从机发送复位脉冲 */ static void DS18B20_Reset(void) { DS18B20_Mode_OUT_PP(); // 主机输出 DS18B20_OUT_0; // 主机至少产生 480us 的低电平复位信号 delay_us(750); DS18B20_OUT_1; // 主机在产生复位信号后，需将总线拉高 // 从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲 delay_us(15); } /** * @brief 检测从机给主机返回的存在脉冲 * @return 0：成功\t1：失败 */ static uint8_t DS18B20_Presence(void) { uint8_t pulse_time = 0; DS18B20_Mode_IN_NP(); // 主机设为输入 // 等待存在脉冲的到来，存在脉冲为一个 60 ~ 240 us 的低电平信号 // 如果存在脉冲没有来则做超时处理，从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲 while (DS18B20_IN \u0026amp;\u0026amp; (pulse_time = 100) { return 1; } else { pulse_time = 0; } // 响应脉冲（低电平）到来，且存在的时间不能超过 240 us while (!(DS18B20_IN) \u0026amp;\u0026amp; pulse_time = 240) { return 1; } else { return 0; } } /** * @brief DS18B20 初始化函数 * @reurn 0：成功\t1：失败 */ uint8_t DS18B20_Init(void) { DS18B20_Mode_OUT_PP(); DS18B20_OUT_1; DS18B20_Reset(); return DS18B20_Presence(); } 配置写函数 当主机将数据线从高逻辑级别拉到低逻辑级别时，将启动写入时隙。有两种类型的写时槽：写1时槽和写0时槽。所有写入时隙的持续时间必须至少为60µs，且每个写入周期之间的恢复时间至少为1µs以上。在DQ线下降后，DS18B20在15µs到60µs的窗口中对DQ线进行采样。\n如果DQ为高，则会出现Write1。如果DQ为低低，则会出现Write0。\n要使主机生成写1时隙，必须将数据线拉到逻辑低级别，然后释放，允许数据线在写时隙开始后的15µs内拉到高级别。\n要使主机生成写0时隙，必须将数据线拉到逻辑低级别，并在低级别保持60µs。\n/** * @brief 写一个字节到 DS18B20，低位先行 */ static void DS18B20_WriteByte(uint8_t dat) { uint8_t i, testb; DS18B20_Mode_OUT_PP(); for (i = 0; i \u0026gt; 1; // 写 0 和写 1 的时间至少要大于60us if (testb) // 当前位写 1 { DS18B20_OUT_0; delay_us(5); // 拉低发送写时段信号 DS18B20_OUT_1; // 读取电平时间保持高电平 delay_us(65); } else // 当前位写 0 { DS18B20_OUT_0; // 拉低发送写时段信号 delay_us(70); // 读取电平时间保持低电平 DS18B20_OUT_1; delay_us(2); // 恢复时间 } } } 配置读函数 当要从DS18B20读取数据时，主机生成读取时隙。当主机将数据线从逻辑高级级别到逻辑低级别时，启动读取时隙。数据线必须保持在低逻辑级别至少1µs；来自DS18B20的输出数据在读取时隙边缘下降后15µs有效。因此，主机必须将DQ引脚拉低，以便从读取槽的开始读取其15µs的状态。在读取时隙结束时，DQ引脚将通过外部上拉电阻重新拉高。所有读取时间槽的持续时间必须至少为60µs，每个读取时间槽之间的恢复时间至少为1-µs。\n/** * @brief 从DS18B20读取一个bit */ static uint8_t DS18B20_ReadBit(void) { uint8_t dat; DS18B20_Mode_OUT_PP(); // 读 0 和读 1 的时间至少要大于 60 us DS18B20_OUT_0; // 读时间的起始：必须由主机产生 \u0026gt; 1us 要搭配硬件电路使用 ```c void Power_Select(uint8_t value) { DS18B20_WriteByte(0xB4); if(value == 1) { DS18B20_WriteByte(1); } if(value == 0) { DS18B20_WriteByte(0); } } 读取芯片ID /** * @brief 读取ID * @param ds18b20_id：用于存放 DS18B20 串行号的数组的首地址 */ void DS18B20_ReadId(uint8_t *ds18b20_id) { uint8_t uc; DS18B20_WriteByte(0x33); // 读取串行号 for (uc = 0; uc ![4](img-4.png) ```c /** * 存储的温度是16 位的带符号扩展的二进制补码形式 * 当工作在12位分辨率时，其中5个符号位，7个整数位，4个小数位 * * |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----| * 低字节 | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) | * * * |-----符号位：0-\u0026gt;正 1-\u0026gt;负-------|-----------整数-----------| * 高字节 | s | s | s | s | s | 2^6 | 2^5 | 2^4 | * * * 温度 = 符号位 + 整数 + 小数*0.0625 */ /** * @brief 在匹配 ROM 情况下获取 DS18B20 温度值 * @param ds18b20_id：存放 DS18B20 串行号的数组的首地址 * @retval 温度值 */ float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id) { uint8_t tpmsb, tplsb, i; int16_t s_tem; float f_tem; DS18B20_MatchRom(); /* 匹配ROM */ for (i = 0; i ![5](img-5.png) ```c /** * 存储的温度是16 位的带符号扩展的二进制补码形式 * 当工作在12位分辨率时，其中5个符号位，7个整数位，4个小数位 * * |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----| * 低字节 | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) | * * * |-----符号位：0-\u0026gt;正 1-\u0026gt;负-------|-----------整数-----------| * 高字节 | s | s | s | s | s | 2^6 | 2^5 | 2^4 | * * * 温度 = 符号位 + 整数 + 小数*0.0625 */ /** * @brief 在跳过匹配 ROM 情况下获取 DS18B20 温度值 * @param 无 * @retval 温度值 */ float DS18B20_GetTemp_SkipRom(void) { uint8_t tpmsb, tplsb; int16_t s_tem; float f_tem; DS18B20_SkipRom(); DS18B20_WriteByte(0X44); /* 开始转换 */ DS18B20_SkipRom(); DS18B20_WriteByte(0XBE); /* 读温度值 */ tplsb = DS18B20_ReadByte(); tpmsb = DS18B20_ReadByte(); s_tem = tpmsb = 100) { return 1; } else { pulse_time = 0; } // 响应脉冲（低电平）到来，且存在的时间不能超过 240 us while (!(DS18B20_IN) \u0026amp;\u0026amp; pulse_time = 240) { return 1; } else { return 0; } } /** * @brief DS18B20 初始化函数 * @reurn 0：成功\t1：失败 */ uint8_t DS18B20_Init(void) { DS18B20_Mode_OUT_PP(); DS18B20_OUT_1; DS18B20_Reset(); return DS18B20_Presence(); } /** * @brief 从DS18B20读取一个bit */ static uint8_t DS18B20_ReadBit(void) { uint8_t dat; DS18B20_Mode_OUT_PP(); // 读 0 和读 1 的时间至少要大于 60 us DS18B20_OUT_0; // 读时间的起始：必须由主机产生 \u0026gt; 1us \u0026gt; 1; // 写 0 和写 1 的时间至少要大于60us if (testb) // 当前位写 1 { DS18B20_OUT_0; delay_us(5); // 拉低发送写时段信号 DS18B20_OUT_1; // 读取电平时间保持高电平 delay_us(65); } else // 当前位写 0 { DS18B20_OUT_0; // 拉低发送写时段信号 delay_us(70); // 读取电平时间保持低电平 DS18B20_OUT_1; delay_us(2); // 恢复时间 } } } /** * @brief 跳过匹配 DS18B20 ROM */ static void DS18B20_SkipRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0XCC); /* 跳过 ROM */ } /** * @brief 执行匹配 DS18B20 ROM */ static void DS18B20_MatchRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0X55); /* 匹配 ROM */ } /** * 存储的温度是16 位的带符号扩展的二进制补码形式 * 当工作在12位分辨率时，其中5个符号位，7个整数位，4个小数位 * * |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----| * 低字节 | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) | * * * |-----符号位：0-\u0026gt;正 1-\u0026gt;负-------|-----------整数-----------| * 高字节 | s | s | s | s | s | 2^6 | 2^5 | 2^4 | * * * 温度 = 符号位 + 整数 + 小数*0.0625 */ /** * @brief 在跳过匹配 ROM 情况下获取 DS18B20 温度值 * @param 无 * @retval 温度值 */ float DS18B20_GetTemp_SkipRom(void) { uint8_t tpmsb, tplsb; int16_t s_tem; float f_tem; DS18B20_SkipRom(); DS18B20_WriteByte(0X44); /* 开始转换 */ DS18B20_SkipRom(); DS18B20_WriteByte(0XBE); /* 读温度值 */ tplsb = DS18B20_ReadByte(); tpmsb = DS18B20_ReadByte(); s_tem = tpmsb ","permalink":"https://fan-pengfei.top/posts/%E5%8D%95%E6%80%BB%E7%BA%BF%E6%8C%82%E8%BD%BD%E5%A4%9A%E4%B8%AAds18b20%E5%B9%B6%E8%AF%BB%E5%8F%96%E6%B8%A9%E5%BA%A6/","summary":"\u003cblockquote\u003e\n\u003cp\u003e当需要同时读取多个点的温度数据时，DS18B20就是一个很好的选择，不仅精度高，而且还可以单总线挂载多个传感器以节省IO口的使用；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"初始化函数\"\u003e初始化函数\u003c/h2\u003e\n\u003cp\u003eDS18B20的通信协议为单总线通信协议；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"1\" loading=\"lazy\" src=\"/posts/%E5%8D%95%E6%80%BB%E7%BA%BF%E6%8C%82%E8%BD%BD%E5%A4%9A%E4%B8%AAds18b20%E5%B9%B6%E8%AF%BB%E5%8F%96%E6%B8%A9%E5%BA%A6/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e首先由主机发送一个复位脉冲约480-960us；然后总线被拉高；在15-60us之后传感器向单片机发送一个约60-240us的存在脉冲，然后总线被拉高。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief 主机给从机发送复位脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Reset\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Mode_OUT_PP\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e// 主机输出\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_0; \u003cspan style=\"color:#75715e\"\u003e// 主机至少产生 480us 的低电平复位信号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edelay_us\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e750\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_1; \u003cspan style=\"color:#75715e\"\u003e// 主机在产生复位信号后，需将总线拉高\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edelay_us\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e15\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  检测从机给主机返回的存在脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @return 0：成功\t\t1：失败\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Presence\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Mode_IN_NP\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e// 主机设为输入\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 等待存在脉冲的到来，存在脉冲为一个 60 ~ 240 us 的低电平信号\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 如果存在脉冲没有来则做超时处理，从机接收到主机的复位信号后，会在 15 ~ 60 us 后给主机发一个存在脉冲\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (DS18B20_IN \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e (pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 响应脉冲（低电平）到来，且存在的时间不能超过 240 us\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e(DS18B20_IN) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e pulse_time \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e240\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief  DS18B20 初始化函数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @reurn  0：成功\t\t1：失败\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Init\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Mode_OUT_PP\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DS18B20_OUT_1;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Reset\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDS18B20_Presence\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"配置写函数\"\u003e配置写函数\u003c/h2\u003e\n\u003cp\u003e当主机将数据线从高逻辑级别拉到低逻辑级别时，将启动写入时隙。有两种类型的写时槽：写1时槽和写0时槽。所有写入时隙的持续时间必须至少为60µs，且每个写入周期之间的恢复时间至少为1µs以上。在DQ线下降后，DS18B20在15µs到60µs的窗口中对DQ线进行采样。\u003c/p\u003e","title":"单总线挂载多个DS18B20并读取温度"},{"content":" 尽量用较少的IO口来驱动较多的LED灯；\n当IO口数量较少，而又需要驱动较多的LED灯时，就需要想办法通过修改硬件或者软件的方案来进行；\n硬件方案的话，就是用串转并芯片例如74HC595或者其他的数码管驱动芯片来控制，当然会增加硬件成本，如果只是用在个人项目中，小小的成本增加并没有什么，但是如果是用在量产项目中，小小的成本增加就会吃掉一大部分盈利；\n所以尽量还是使用软件方案，并不需要什么74HC595芯片；\n下面介绍的这种方法叫做查理复用：\n查理复用（Charlieplex）是一种在驱动大量LED时有效地节约IO口的方法，理论上可以用N个IO驱动N*(N-1)个LED，也有接入二极管用来做按键检测的，理论上可实现用N个IO驱动N*(N-1)个按键； 因而7个脚用满理论上可管理是42个LED，极大节省了IO口的使用；\n但是对单片机的IO口有一个要求，也就是这种LED是由单片机I/O口直接驱动，I/O口要在工作在3态（高、低电平和高阻）；\n使用六个IO口驱动30个LED的原理图如下（第六行并未完整画出）：\n可以在程序里面每次间隔1ms，扫描一行，总共扫描6行后（6ms），一帧完整就画面结束了，也是利用人眼的视觉暂留画面； 一定要记得，每行扫描的时候，需要亮灯的高低电平点亮，不亮灯的IO口一定要设为悬浮（高阻模式）。\n","permalink":"https://fan-pengfei.top/posts/%E5%A6%82%E4%BD%95%E7%94%A87%E4%B8%AAio%E5%8F%A3%E9%A9%B1%E5%8A%A842%E4%B8%AAled/","summary":"\u003cblockquote\u003e\n\u003cp\u003e尽量用较少的IO口来驱动较多的LED灯；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e当IO口数量较少，而又需要驱动较多的LED灯时，就需要想办法通过修改硬件或者软件的方案来进行；\u003c/p\u003e\n\u003cp\u003e硬件方案的话，就是用串转并芯片例如74HC595或者其他的数码管驱动芯片来控制，当然会增加硬件成本，如果只是用在个人项目中，小小的成本增加并没有什么，但是如果是用在量产项目中，小小的成本增加就会吃掉一大部分盈利；\u003c/p\u003e\n\u003cp\u003e所以尽量还是使用软件方案，并不需要什么74HC595芯片；\u003c/p\u003e\n\u003cp\u003e下面介绍的这种方法叫做查理复用：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e查理复用（Charlieplex）是一种在驱动大量LED时有效地节约IO口的方法，理论上可以用N个IO驱动\u003ccode\u003eN*(N-1)\u003c/code\u003e个LED，也有接入二极管用来做按键检测的，理论上可实现用N个IO驱动\u003ccode\u003eN*(N-1)\u003c/code\u003e个按键；  因而7个脚用满理论上可管理是42个LED，极大节省了IO口的使用；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e但是对单片机的IO口有一个要求，也就是这种LED是由单片机I/O口直接驱动，I/O口要在工作在3态（高、低电平和高阻）；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e使用六个IO口驱动30个LED的原理图如下（第六行并未完整画出）：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%A6%82%E4%BD%95%E7%94%A87%E4%B8%AAio%E5%8F%A3%E9%A9%B1%E5%8A%A842%E4%B8%AAled/img-1.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e可以在程序里面每次间隔1ms，扫描一行，总共扫描6行后（6ms），一帧完整就画面结束了，也是利用人眼的视觉暂留画面；\n一定要记得，\u003cstrong\u003e每行扫描的时候，需要亮灯的高低电平点亮，不亮灯的IO口一定要设为悬浮（高阻模式）。\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","title":"如何用7个IO口驱动42个LED"},{"content":" 用单个IO口来检测两个按键的状态； 一、ADC方案 上面是原理图，这个方案很好理解，主要就是利用电阻分压原理来判断多个按键被按下的状态，如果ADC的位数足够多，可以判断的按键数也会很多；\n因为原理很简单，在这里就不再多说；\n二、非ADC方案 这个方案适用于无ADC引脚或者ADC引脚被其他外设占用的情况，只以单IO口检测两个按键的状态的方案为例；\n原理图如下所示： EN是单片机内部的上拉使能开关，S1和S2是待检测的按键；\n通过查阅STM32F103C8T6数据手册可以得知：\n内部的上拉电阻阻值等效为40K欧姆电阻，高低电平的范围也在数据手册中有给出：\n当MCU供电为3.3V时候：\nIO口低电平电压范围：-0.5-0.8V；\nIO口高电平电压范围： 2.0-3.8V；\n因此得到最开始的检测电路；但有两个注意事项：\n这里特别要注意在使用该电路时，电路参数须满足MCU的IO口高低电平的电气特性要求；\n电路如果需要具备两个按键同时按的功能要求，需自行调整电路，该电路参数不满足该要求；\n电路分析如下： 当EN 闭合时： S1 按下时， V_IO 接近0V，此时IO口为低电平。 S2 按下时， V_IO = 3.3V * R103 / (R+R103) V_IO = 3.3V * 510K/ (510K+（40K//2M）) = 3.06V 此时IO口为高电平。\n当EN 断开时： S1 按下时， V_IO 接近0V，此时IO口为低电平。 S2 按下时， V_IO = 3.3V * R103 / (R+R103) V_IO = 3.3V * 510K/ (510K+ 2M) = 0.67V 此时IO口为低电平。\nMCU检测过程：\nEN闭合-\u0026gt;如果IO口为低电平-\u0026gt;此时判定为S1按下；\nEN断开-\u0026gt;如果IO口为低电平-\u0026gt;此时判定为S2按下（此时S1不能被按下）；\n只有三种情况：\nS1 S2\n0 0\n1 0\n0 1\n伪代码如下： EN=1;//使能上拉电阻 if(IO==0)//如果IO口检测为0，判定为S1被按下 { S1=1; } else//其他情况，S1未被按下 { S1=0; } EN=0;//取消使能上拉电阻 if(IO==0)//如果IO口检测为0，判定为S1或S2被按下 { if(S1==1)//如果S1被按下，则S2未被按下，否则S2被按下 { S2=0; } else { S2=0; } } else//其他情况，S2未被按下 { S2=0; } ","permalink":"https://fan-pengfei.top/posts/%E5%A6%82%E4%BD%95%E7%94%A8%E4%B8%80%E4%B8%AAio%E5%8F%A3%E6%A3%80%E6%B5%8B%E4%B8%A4%E4%B8%AA%E6%8C%89%E9%94%AE%E7%9A%84%E7%8A%B6%E6%80%81/","summary":"\u003cblockquote\u003e\n\u003ch2 id=\"用单个io口来检测两个按键的状态\"\u003e用单个IO口来检测两个按键的状态；\u003c/h2\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一adc方案\"\u003e一、ADC方案\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%A6%82%E4%BD%95%E7%94%A8%E4%B8%80%E4%B8%AAio%E5%8F%A3%E6%A3%80%E6%B5%8B%E4%B8%A4%E4%B8%AA%E6%8C%89%E9%94%AE%E7%9A%84%E7%8A%B6%E6%80%81/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e上面是原理图，这个方案很好理解，主要就是利用电阻分压原理来判断多个按键被按下的状态，如果ADC的位数足够多，可以判断的按键数也会很多；\u003c/p\u003e\n\u003cp\u003e因为原理很简单，在这里就不再多说；\u003c/p\u003e\n\u003ch2 id=\"二非adc方案\"\u003e二、非ADC方案\u003c/h2\u003e\n\u003cp\u003e这个方案适用于无ADC引脚或者ADC引脚被其他外设占用的情况，只以单IO口检测两个按键的状态的方案为例；\u003c/p\u003e\n\u003ch3 id=\"原理图如下所示\"\u003e原理图如下所示：\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"图二\" loading=\"lazy\" src=\"/posts/%E5%A6%82%E4%BD%95%E7%94%A8%E4%B8%80%E4%B8%AAio%E5%8F%A3%E6%A3%80%E6%B5%8B%E4%B8%A4%E4%B8%AA%E6%8C%89%E9%94%AE%E7%9A%84%E7%8A%B6%E6%80%81/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003eEN是单片机内部的上拉使能开关，S1和S2是待检测的按键；\u003c/p\u003e\n\u003cp\u003e通过查阅STM32F103C8T6数据手册可以得知：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"751d413dec771489a3d5835a2d0855e\" loading=\"lazy\" src=\"/posts/%E5%A6%82%E4%BD%95%E7%94%A8%E4%B8%80%E4%B8%AAio%E5%8F%A3%E6%A3%80%E6%B5%8B%E4%B8%A4%E4%B8%AA%E6%8C%89%E9%94%AE%E7%9A%84%E7%8A%B6%E6%80%81/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e内部的上拉电阻阻值等效为40K欧姆电阻，高低电平的范围也在数据手册中有给出：\u003c/p\u003e\n\u003cp\u003e当MCU供电为3.3V时候：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eIO口低电平电压范围：-0.5-0.8V；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eIO口高电平电压范围： 2.0-3.8V；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e因此得到最开始的检测电路；但有两个注意事项：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e这里特别要注意在使用该电路时，电路参数须满足MCU的IO口高低电平的电气特性要求；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e电路如果需要具备两个按键同时按的功能要求，需自行调整电路，该电路参数不满足该要求；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"电路分析如下\"\u003e电路分析如下：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e当EN 闭合时：\nS1 按下时， V_IO 接近0V，此时IO口为低电平。\nS2 按下时， V_IO = 3.3V * R103 / (R+R103)\nV_IO = 3.3V * 510K/ (510K+（40K//2M）) = 3.06V\n此时IO口为高电平。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e当EN 断开时：\nS1 按下时， V_IO 接近0V，此时IO口为低电平。\nS2 按下时， V_IO = 3.3V * R103 / (R+R103)\nV_IO = 3.3V * 510K/ (510K+ 2M) = 0.67V\n此时IO口为低电平。\u003c/p\u003e","title":"如何用一个IO口检测两个按键的状态"},{"content":" 翻译的一篇文章； Commit messages matter. Here’s how to write them well. 提交的备注信息很重要，这里将教给你如何写好它们.\n简介：为什么好的提交信息很重要 如果你随机浏览任何一个 Git 存储库的日志，你可能会发现它的提交消息或多或少是一团糟。\n例如，下面是我早期致力于 Spring 时的提交信息：\n$ git log --oneline -5 --author cbeams --before \u0026#34;Fri Mar 26 2009\u0026#34; e5f4b49 Re-adding ConfigurationPostProcessorTests after its brief removal in r814. @Ignore-ing the testCglibClassesAreLoadedJustInTimeForEnhancement() method as it turns out this was one of the culprits in the recent build breakage. The classloader hacking causes subtle downstream effects, breaking unrelated tests. The test method is still useful, but should only be run on a manual basis to ensure CGLIB is not prematurely classloaded, and should not be run as part of the automated build. 2db0f12 fixed two build-breaking issues: + reverted ClassMetadataReadingVisitor to revision 794 + eliminated ConfigurationPostProcessorTests until further investigation determines why it causes downstream tests to fail (such as the seemingly unrelated ClassPathXmlApplicationContextTests) 147709f Tweaks to package-info.java files 22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils 7f96f57 polishing 啊哈，然后将其与来自同一存储库的这些最近的提交进行比较：\n$ git log --oneline -5 --author pwebb --before \u0026#34;Sat Aug 30 2014\u0026#34; 5ba3db6 Fix failing CompositePropertySourceTests 84564a0 Rework @PropertySource early parsing logic e142fd1 Add tests for ImportSelector meta-data 887815f Update docbook dependency and generate epub ac8326d Polish mockito usage 你会觉得哪个更容易阅读一些呢？\n前者的长度和形式各不相同；后者简洁一致。 前者是默认情况下发生的（未进行润色）；后者是有意进行书写的（进行了润色）。\n虽然许多存储库的日志看起来更像前者；但也有例外， Linux 内核和 Git 本身就是很好的例子。查看 Spring Boot，或 Tim Pope 管理的任何存储库。\n这些存储库的贡献者知道，精心设计的 Git 提交消息是向其他开发人员（以及他们未来的自己）传达有关更改的上下文的最佳方式。差异会告诉你代码发生了什么变化，但只有提交消息才能正确告诉你原因。 Peter Hutterer 很好地说明了这一点：\n重新去理解一段代码的上下文关系是一种浪费；虽然我们不能完全避免它，但是我们的努力应该去尽可能地减少它。一个好的提交消息可以做到这一点，因此，提交消息表明了一个开发人员是否是一个好的合作者。\n如果你没有考虑过什么是好的 Git 提交消息，可能是因为你没有花太多时间使用 git log 和相关工具。这里有一个恶性循环：因为提交历史是非结构化和不一致的，所以人们不会花太多时间使用或照顾它。而且因为它没有被使用或照顾，所以它仍然是非结构化和不一致的。\n但是精心照料的日志是一件美丽而有用的东西。 git blame、revert、rebase、log、shortlog和其他子命令变得生动起来。审查其他人的提交和拉取请求成为一件值得做的事情，并且突然可以独立完成。了解为什么几个月或几年前发生的事情不仅可能而且有效。\n一个项目的长期成功取决于（除其他外）它的可维护性，维护者没有比他的项目日志更强大的工具了。花时间学习如何正确维护一个日志是值得的。一开始可能很麻烦的事情很快就会变成习惯，并最终成为所有相关人员的骄傲和生产力的源泉。\n在这篇文章中，我只讨论保持健康提交历史的最基本要素：如何编写单独的提交消息。还有其他一些重要的实践，比如提交压缩，我没有在这里讨论。也许我会在随后的帖子中这样做。\n大多数编程语言都有关于什么构成惯用风格的既定约定，即命名、格式等。当然，这些约定会有所不同，但大多数开发人员都认为，选择一个并坚持它比每个人都做自己的事情时随之而来的混乱要好得多。\n团队处理其提交日志的方法应该没有什么不同。为了创建有用的修订历史记录，团队应该首先就提交消息约定达成一致，该约定至少定义以下三件事：\n风格：\n标记语法、换行边距、语法、大写、标点符号。把这些事情拼写出来，消除猜测，让一切尽可能简单。最终结果将是一个非常一致的日志，它不仅阅读起来很愉快，而且确实会定期阅读。\n内容：\n提交消息的正文（如果有的话）应该包含什么样的信息？它不应该包含什么？\n元数据：\n应该如何引用问题跟踪 ID、拉取请求编号等？\n幸运的是，对于什么是地道的 Git 提交消息，有一些完善的约定。事实上，其中许多都是以某些 Git 命令的方式假设的。你无需重新发明任何东西。只需遵循以下七个规则，你就可以像专业人士一样投入工作。\nGit 提交信息的七大规则： 请记住：这一切都已经说过了。\n1、用空行将主题与正文分开；\n2、将主题行限制为 50 个字符；\n3、大写主题行；\n4、不要以句号结束主题行；\n5、在主题行中使用祈使语气；\n6、将正文包裹在 72 个字符处；\n7、使用主体来解释什么以及为什么与如何；\n例如：\nSummarize changes in around 50 characters or less More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of the commit and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); various tools like `log`, `shortlog` and `rebase` can get confused if you run the two together. Explain the problem that this commit is solving. Focus on why you are making this change as opposed to how (the code explains that). Are there side effects or other unintuitive consequences of this change? Here\u0026#39;s the place to explain them. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here If you use an issue tracker, put references to them at the bottom, like this: Resolves: #123 See also: #456, #789 1. Separate subject from body with a blank line 用空行将主题与正文分开 虽然不是必需的，但最好以一个简短的（少于 50 个字符）行开始提交消息，总结更改，然后是一个空行，然后是更详尽的描述。提交消息中直到第一个空行的文本被视为提交标题，并且该标题在整个 Git 中使用。例如，Git-format-patch(1) 将提交转换为电子邮件，并在主题行中使用标题，在正文中使用提交的其余部分。\n首先，并非每个提交都需要主题和主体。有时单行就可以了，尤其是当更改非常简单以至于不需要进一步的上下文时。例如：\nFix typo in introduction to user guide 无需多说；如果读者想知道拼写错误是什么，她可以简单地查看更改本身，即使用 git show 或 git diff 或 git log -p。 如果你在命令行提交这样的东西，很容易使用 git commit 的 -m 选项：\n$ git commit -m “修复用户指南介绍中的拼写错误” 但是，当提交需要一些解释和上下文时，你需要编写一个正文。例如：\nDerezz the master control program MCP turned out to be evil and had become intent on world domination. This commit throws Tron\u0026#39;s disc into MCP (causing its deresolution) and turns it back into a chess game. 使用 -m 选项编写带有正文的提交消息并不那么容易。你最好在适当的文本编辑器中编写消息。如果你还没有为在命令行中使用 Git 而设置的编辑器，请阅读 Pro Git 的这一部分。 无论如何，在浏览日志时将主题与正文分开是有好处的。这是完整的日志条目：\n$ git log commit 42e769bdf4894310333942ffc5a15151222a87be Author: Kevin Flynn Date: Fri Jan 01 00:00:00 1982 -0200 Derezz the master control program MCP turned out to be evil and had become intent on world domination. This commit throws Tron\u0026#39;s disc into MCP (causing its deresolution) and turns it back into a chess game. 现在 git log --oneline，它只打印出主题行：\n$ git log --oneline 42e769 Derezz the master control program 或者，git shortlog，它按用户分组提交，同样只显示简洁的主题行：\n$ git shortlog Kevin Flynn (1): Derezz the master control program Alan Bradley (1): Introduce security program \u0026#34;Tron\u0026#34; Ed Dillinger (3): Rename chess program to \u0026#34;MCP\u0026#34; Modify chess program Upgrade chess program Walter Gibbs (1): Introduce protoype chess program 在 Git 中还有许多其他上下文，其中主题行和正文之间的区别开始出现——但如果没有中间的空行，它们都无法正常工作。\n将主题行限制在 50 个字符以内 50 个字符不是硬性限制，只是一个经验法则。将主题行保持在这个长度可以确保它们的可读性，并迫使作者思考一下以最简洁的方式来解释正在发生的事情。\n提示：如果你很难总结，你可能在一次提交中提交了太多更改，争取原子提交（一个单独更改的主题）。\nGitHub 的 UI 完全了解这些约定。如果你超过 50 个字符的限制，它会警告你：\n并将用省略号截断任何超过 72 个字符的主题行：\n50个字符可能太短了，但最多不要超过72个字符。\n大写主题行 这听起来很简单。所有主题行都以大写字母开头。 例如：\nAccelerate to 88 miles per hour 而不是：\nAccelerate to 88 miles per hour 不要以句号结束主题行 主题行中不需要尾随标点符号。此外，当你试图将它们控制在 50 个字符或更少时，空间非常宝贵。 例子：\nOpen the pod bay doors 而不是：\nOpen the pod bay doors. 在主题行中使用祈使语气 祈使语气就是“口头或书面上发出命令或指示”。几个例子：\nClean your room\nClose the door\nTake out the trash\n你现在正在阅读的七个规则中的每一个都是用祈使语气写的（“将正文包裹在 72 个字符处”，等等）。 祈使语气听起来有点粗鲁；这就是为什么我们不经常使用它。但它非常适合 Git 提交主题行。\n原因之一是 Git 本身在代表你创建提交时就使用祈使语气。\n例如，使用 git merge 时创建的默认消息是：\nMerge branch \u0026#39;myfeature\u0026#39; 当使用 git revert 时：\nRevert \u0026#34;Add the thing with the stuff\u0026#34; This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d. 或者在 GitHub 拉取请求上单击“合并”按钮时：\nMerge pull request #123 from someuser/somebranch 因此，当你以祈使语气编写提交消息时，你就是在遵循 Git 自己的内置约定。例如：\nRefactor subsystem X for readability\nUpdate getting started documentation\nRemove deprecated methods\nRelease version 1.0.0\n以这种方式编写一开始可能有点尴尬。我们更习惯于以指示性语气说话，这都是关于报告事实的。这就是为什么提交消息通常最终会像这样阅读：\nFixed bug with Y\nChanging behavior of X\n有时提交消息被写成对其内容的描述：\nMore fixes for broken stuff\nSweet new API methods\n为了消除任何混淆，这里有一个简单的规则，每次都正确。 格式正确的 Git 提交主题行应该始终能够完成以下句子：\nIf applied, this commit will *your subject line here 例如：\nIf applied, this commit will refactor subsystem X for readability\nIf applied, this commit will update getting started documentation\nIf applied, this commit will remove deprecated methods\nIf applied, this commit will release version 1.0.0\nIf applied, this commit will merge pull request #123 from user/branch\n请注意这对其他非祈使语气形式不起作用：\nIf applied, this commit will fixed bug with Y\nIf applied, this commit will changing behavior of X\nIf applied, this commit will more fixes for broken stuff\nIf applied, this commit will sweet new API methods\n记住：祈使语气的使用仅在主题行中很重要。你可以在编写正文时放宽此限制。\n将正文包裹在72个字符处 Git 从不自动换行文本。当你编写提交消息的正文时，你必须注意它的右边距，并手动换行。\n建议以 72 个字符执行此操作，以便 Git 有足够的空间来缩进文本，同时仍将所有内容总体保持在 80 个字符以下。\n一个好的文本编辑器可以在这里提供帮助。很容易配置 Vim，例如，在你编写 Git 提交时将文本换行为 72 个字符。然而，传统上，IDE 在为提交消息中的文本换行提供智能支持方面一直很糟糕（尽管在最近的版本中，IntelliJ IDEA 终于在这方面做得更好）。\n用主体部分来解释what、why vs. how Bitcoin Core 的这个提交是一个很好的例子，它解释了修改的内容和原因：\ncommit eb0b56b19017ab5c16c745e6da39c53126924ed6 Author: Pieter Wuille Date: Fri Aug 1 22:57:55 2014 +0200 Simplify serialize.h\u0026#39;s exception handling Remove the \u0026#39;state\u0026#39; and \u0026#39;exceptmask\u0026#39; from serialize.h\u0026#39;s stream implementations, as well as related methods. As exceptmask always included \u0026#39;failbit\u0026#39;, and setstate was always called with bits = failbit, all it did was immediately raise an exception. Get rid of those variables, and replace the setstate with direct exception throwing (which also removes some dead code). As a result, good() is never reached after a failure (there are only 2 calls, one of which is in tests), and can just be replaced by !eof(). fail(), clear(n) and exceptions() are just never called. Delete them. 看看完整的差异，想想作者通过花时间在这里和现在提供这个上下文来节省同事和未来的提交者多少时间。如果他不这样做，它可能会永远丢失。\n在大多数情况下，你可以省略有关如何进行更改的详细信息。在这方面，代码通常是不言自明的（如果代码太复杂以至于需要用更多文字来解释，那是代码注释的目的）。只需专注于弄清楚你最初进行更改的原因——更改之前的工作方式（以及其中的问题）、它们现在的工作方式以及你决定按照你的方式解决问题的原因。\n未来感谢你的维护者可能就是你自己！\nTips 学会爱上命令行。抛弃IDE。\n出于与 Git 子命令一样多的原因，拥抱命令行是明智的。 Git 非常强大； IDE 也是如此，但各有不同的方式。我每天都使用一个 IDE (IntelliJ IDEA) 并广泛使用其他 IDE (Eclipse)，但我从未见过 Git 的 IDE 集成可以开始与命令行的易用性和强大功能相匹配（一旦你熟悉它）。\n某些与 Git 相关的 IDE 功能非常宝贵，例如在删除文件时调用 git rm，以及在重命名文件时使用 git做正确的事情。当你开始尝试通过 IDE commmit， merge， rebase或进行复杂的历史分析时，一切都会崩溃。\n当谈到使用 Git 的全部功能时，它一直都是指命令行。\n请记住，无论你使用 Bash、Zsh 还是 Powershell，都有tab补全命令可以减轻你记住子命令和其他用法的痛苦。\n","permalink":"https://fan-pengfei.top/posts/%E5%A6%82%E4%BD%95%E5%86%99%E5%A5%BDcommitmessage/","summary":"\u003cblockquote\u003e\n\u003cp\u003e翻译的一篇文章；\n\u003cstrong\u003eCommit messages matter. Here’s how to write them well.\u003c/strong\u003e\n提交的备注信息很重要，这里将教给你如何写好它们.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"How to Write a Git Commit Message\" loading=\"lazy\" src=\"/posts/%E5%A6%82%E4%BD%95%E5%86%99%E5%A5%BDcommitmessage/img-1.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"简介为什么好的提交信息很重要\"\u003e简介：为什么好的提交信息很重要\u003c/h2\u003e\n\u003cp\u003e如果你随机浏览任何一个 Git 存储库的日志，你可能会发现它的提交消息或多或少是一团糟。\u003c/p\u003e\n\u003cp\u003e例如，下面是我早期致力于 Spring 时的提交信息：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ git log --oneline -5 --author cbeams --before \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Fri Mar 26 2009\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ee5f4b49 Re-adding ConfigurationPostProcessorTests after its brief removal in r814. @Ignore-ing the testCglibClassesAreLoadedJustInTimeForEnhancement\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e method as it turns out this was one of the culprits in the recent build breakage. The classloader hacking causes subtle downstream effects, breaking unrelated tests. The test method is still useful, but should only be run on a manual basis to ensure CGLIB is not prematurely classloaded, and should not be run as part of the automated build.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e2db0f12 fixed two build-breaking issues: + reverted ClassMetadataReadingVisitor to revision \u003cspan style=\"color:#ae81ff\"\u003e794\u003c/span\u003e + eliminated ConfigurationPostProcessorTests \u003cspan style=\"color:#66d9ef\"\u003euntil\u003c/span\u003e further investigation determines why it causes downstream tests to fail \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003esuch as the seemingly unrelated ClassPathXmlApplicationContextTests\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e147709f Tweaks to package-info.java files\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e7f96f57 polishing\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e啊哈，然后将其与来自同一存储库的这些最近的提交进行比较：\u003c/p\u003e","title":"如何写好CommitMessage"},{"content":" 今天写代码的时候再次用到了与或非等逻辑运算符来完成一些二进制处理应用，总结记录一下；\n一、缘起 以下是一个显示板的原理图，由于LED数量较多，因此在LED控制方案的选择上是选用了一块串转并的芯片74HC595；\n对于74HC595的使用在这里就不再赘述；\n74HC595串转并是转出8个输出端，再加上三个直接连接单片机引脚的COM端，理论上是可以很容易控制24个LED灯亮灭，就像是控制三个数码管一样，利用人眼的暂留现象，就可以使这24个LED灯的亮灭随意组合；\n每一个COM端和8个LED组成一个组合，然后轮询点亮这三组LED，速度够快的话，就能看到三组LED被同时点亮；\n二、解决 为了消除残影问题，在每一组的LED点亮后，应该马上写入使LED全部熄灭的命令，以消除可能会出现的残影问题；\n首先定义三个uint8_t变量，例如代码中的LED_NUM1、LED_NUM2、LED_NUM3，然后通过控制COM端，分别向每组LED中写入这三个数据：\n第一组 COM引脚排布：100 待写入74HC595中的数据：LED_NUM1\n第二组 COM引脚排布：010 待写入74HC595中的数据：LED_NUM2\n第三组 COM引脚排布：001 待写入74HC595中的数据：LED_NUM3\n然后对于每一个单独LED的控制，就需要用到逻辑运算，例如图中的LED1，其属于第三组LED，因为其控制COM引脚为COM3，\n因而代码可以写为：\n//只点亮LED1，而不影响本组内其他LED的显示 LED_NUM3 = LED_NUM3 | 0x01; //只熄灭LED1，而不影响本组内其他LED的显示 LED_NUM3 = LED_NUM3 \u0026amp; (~0x01); 其他的LED控制也是一样的道理；\n可以提取出公式，如LED的编号为07，分别接在74HC595的Q0Q1引脚上，设任意一引脚为n号引脚，则其控制代码为：\n//只点亮LEDn，而不影响本组内其他LED的显示 LED_NUM = LED_NUM | (0x01 PF0 STCP -\u0026gt; PA12 DS -\u0026gt; PB5 COM1-\u0026gt;PA15 COM2-\u0026gt;PB3 COM3-\u0026gt;PB4 */ /** * @brief 模拟SPI向74HC595芯片发送数据 * @param SendVal 待发送八位数据 * @return 无 */ void HC595SendData(uint8_t SendVal) { uint8_t i; for (i = 0; i 0) { LED_NUM1 = LED_NUM1 | 0x08; } else { LED_NUM1 = LED_NUM1 \u0026amp; (~0x08); } HC595SendData(LED_NUM1); HC595SendData(0x00); HAL_GPIO_WritePin(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_RESET); if (LED_second_case \u0026gt; 1) { LED_NUM2 = LED_NUM2 | 0x08; } else { LED_NUM2 = LED_NUM2 \u0026amp; (~0x08); } HC595SendData(LED_NUM2); HAL_Delay(0); HC595SendData(0x00); HAL_GPIO_WritePin(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_SET); if (LED_second_case \u0026gt; 2) { LED_NUM3 = LED_NUM3 | 0x08; } else { LED_NUM3 = LED_NUM3 \u0026amp; (~0x08); } if (LED_power) { LED_NUM3 = LED_NUM3 | 0x40; } else { LED_NUM3 = LED_NUM3 \u0026amp; (~0x40); } if (LED_smart == 1) { if (pwm_high \u0026gt; pwm_t) { LED_NUM3 = LED_NUM3 | (0x20); } else { LED_NUM3 = LED_NUM3 \u0026amp; (~0x20); } } if (pwm_temp % 100 == 0) { if (flag == 1) { pwm_t--; if (pwm_t == 0) { flag = !flag; } } else { pwm_t++; if (pwm_t == 10) { flag = !flag; } } } pwm_high++; if (pwm_high == 10) { pwm_high = 0; } switch (LED_first_case) { case 1: /* code */ LED_NUM3 = LED_NUM3 | (0x01); LED_NUM3 = LED_NUM3 \u0026amp; (~0x02); LED_NUM3 = LED_NUM3 \u0026amp; (~0x10); LED_NUM3 = LED_NUM3 \u0026amp; (~0x04); break; case 2: /* code */ LED_NUM3 = LED_NUM3 \u0026amp; (~0x01); LED_NUM3 = LED_NUM3 | (0x02); LED_NUM3 = LED_NUM3 \u0026amp; (~0x10); LED_NUM3 = LED_NUM3 \u0026amp; (~0x04); break; case 3: /* code */ LED_NUM3 = LED_NUM3 \u0026amp; (~0x01); LED_NUM3 = LED_NUM3 \u0026amp; (~0x02); LED_NUM3 = LED_NUM3 | (0x10); LED_NUM3 = LED_NUM3 \u0026amp; (~0x04); break; case 4: /* code */ LED_NUM3 = LED_NUM3 \u0026amp; (~0x01); LED_NUM3 = LED_NUM3 \u0026amp; (~0x02); LED_NUM3 = LED_NUM3 \u0026amp; (~0x10); LED_NUM3 = LED_NUM3 | (0x04); break; default: break; } HC595SendData(LED_NUM3); HAL_Delay(0); HC595SendData(0x00); } ，\n","permalink":"https://fan-pengfei.top/posts/%E4%B8%8E%E6%88%96%E9%9D%9E%E7%AD%89%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97%E5%9C%A8%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/","summary":"\u003cblockquote\u003e\n\u003cp\u003e今天写代码的时候再次用到了与或非等逻辑运算符来完成一些二进制处理应用，总结记录一下；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一缘起\"\u003e一、缘起\u003c/h2\u003e\n\u003cp\u003e以下是一个显示板的原理图，由于LED数量较多，因此在LED控制方案的选择上是选用了一块串转并的芯片74HC595；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E4%B8%8E%E6%88%96%E9%9D%9E%E7%AD%89%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97%E5%9C%A8%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/img-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e对于74HC595的使用在这里就不再赘述；\u003c/p\u003e\n\u003cp\u003e74HC595串转并是转出8个输出端，再加上三个直接连接单片机引脚的COM端，理论上是可以很容易控制24个LED灯亮灭，就像是控制三个数码管一样，利用人眼的暂留现象，就可以使这24个LED灯的亮灭随意组合；\u003c/p\u003e\n\u003cp\u003e每一个COM端和8个LED组成一个组合，然后轮询点亮这三组LED，速度够快的话，就能看到三组LED被同时点亮；\u003c/p\u003e\n\u003ch2 id=\"二解决\"\u003e二、解决\u003c/h2\u003e\n\u003cp\u003e为了消除残影问题，在每一组的LED点亮后，应该马上写入使LED全部熄灭的命令，以消除可能会出现的残影问题；\u003c/p\u003e\n\u003cp\u003e首先定义三个\u003ccode\u003euint8_t\u003c/code\u003e变量，例如代码中的\u003ccode\u003eLED_NUM1\u003c/code\u003e、\u003ccode\u003eLED_NUM2\u003c/code\u003e、\u003ccode\u003eLED_NUM3\u003c/code\u003e，然后通过控制COM端，分别向每组LED中写入这三个数据：\u003c/p\u003e\n\u003cp\u003e第一组\nCOM引脚排布：100\n待写入74HC595中的数据：LED_NUM1\u003c/p\u003e\n\u003cp\u003e第二组\nCOM引脚排布：010\n待写入74HC595中的数据：LED_NUM2\u003c/p\u003e\n\u003cp\u003e第三组\nCOM引脚排布：001\n待写入74HC595中的数据：LED_NUM3\u003c/p\u003e\n\u003cp\u003e然后对于每一个单独LED的控制，就需要用到逻辑运算，例如图中的LED1，其属于第三组LED，因为其控制COM引脚为COM3，\u003c/p\u003e\n\u003cp\u003e因而代码可以写为：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//只点亮LED1，而不影响本组内其他LED的显示\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//只熄灭LED1，而不影响本组内其他LED的显示\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其他的LED控制也是一样的道理；\u003c/p\u003e\n\u003cp\u003e可以提取出公式，如LED的编号为0\u003cdel\u003e7，分别接在74HC595的Q0\u003c/del\u003eQ1引脚上，设任意一引脚为n号引脚，则其控制代码为：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//只点亮LEDn，而不影响本组内其他LED的显示\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLED_NUM \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e PF0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTCP \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e PA12\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDS  \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e PB5\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCOM1\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ePA15\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCOM2\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ePB3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCOM3\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ePB4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief 模拟SPI向74HC595芯片发送数据\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @param SendVal 待发送八位数据\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @return 无\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e SendVal)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; i  \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM1 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM1 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(LED_NUM1);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0x00\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_SET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_RESET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (LED_second_case \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM2 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM2 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(LED_NUM2);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_Delay\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0x00\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_RESET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_WritePin\u003c/span\u003e(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_SET);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (LED_second_case \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x08\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (LED_power)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x40\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x40\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (LED_smart \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (pwm_high \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003epwm_t\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x20\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x20\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (pwm_temp \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (flag \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003epwm_t\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003epwm_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003eflag;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003epwm_t\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003epwm_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003eflag;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    pwm_high\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (pwm_high \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        pwm_high \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eswitch\u003c/span\u003e (LED_first_case)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e/* code */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x02\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x04\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e/* code */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x02\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x04\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e/* code */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x02\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x04\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e/* code */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x01\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x02\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0x10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LED_NUM3 \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e0x04\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edefault\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(LED_NUM3);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHAL_Delay\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHC595SendData\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0x00\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e，\u003c/p\u003e","title":"与或非等逻辑运算在程序中的应用"},{"content":" 对于我来说，2022年是极为重要的一年；\n关于读研 关键词：迷茫\n在去年这个时候，我还在考虑读研的事情，我记得自己大三上学期所有科目成绩出来的那一天，我拿计算器算了一遍又一遍，最后得到一个心凉的结果：即使我大三下学期的核心课程考试都是100分，我也够不到保研的名额；算出来这个结果之后，说真的是心凉了一截；\n虽然自己之前也大概估计着保研比较悬（每次考试都是考前突击，怎么可能稳保研），但是当自己真的算出来保研无望的那一刻，心里还是失落的；失落归失落，自己总还是要做出一个选择：考研或者工作；当然初期我根本没考虑工作的事情，一是对读研确实还有向往，二是我认识的同学或者学长他们绝大多数的选择都是考研；算是迷茫也算是随大流吧，我就打算考研；当然那个时候还比较早，才是一二月份，寒假在家那些天带着失落，自己也没开始复习，在家还是搞一点东西，在咸鱼接一些单子，自己搞一点东西做，在家也算开心；\n开学之后回到学校，就真要开始准备复习了，自己开始也没买什么纸质资料，网上找了些英语数学的考研课程就开始复习了，前些天倒也算努力，还制定了一个简单的复习时间表，英语看的刘晓燕老师的视频课程，数学是看的李永乐老师的视频教程和习题册；准备了一两个月吧，那些天上课时候偷偷复习，晚上也经常自己去中楼的自习室复习；复习这么久，有进展没有嘞，说实话，没什么进展，说是考研复习，自己心里没什么劲头，也只是看同学他们都开始复习，自己跟风罢了；\n复习时候，感觉很难受，可能也是自从高中之后自己再也没有像这样坐在那一坐一整天地复习，学来学去，进到自己脑袋的东西也不知道有多少；那时候为了复习还拒绝了很多事情，跟老师说我要考研，推掉本来要我做的项目，跟学姐说我要考研，推掉介绍给我的外快，跟父母说我要考研，让他们安心不要经常给我打电话，看起来自己是为考研做足了准备，貌似我就是要认真复习准备考研了；\n在刚回到学校不久，班助学长他们组织了一个考研就业动员大会，从这里也能看出考研和工作的人数比例，我记得讲考研经验的有三四个人，而讲工作经验的只有一个人（这位学长应该是拿了TP-LINK SP或者更高档次的Offer），我还用手机的记事本记了很多考研经验，以备我后边用；\n那我是什么时候改变想法了呢，我想是那天听的一个讲座，是毕业工作几年的学长们开的讲座，中间也讲到了工作和读研的抉择，最打动我的是一句话：你应该考虑读研三年和工作三年哪一个对自己的帮助大，你不应该拿着你现在去跟读研三年之后的你比，而应该拿读研三年之后的你和工作三年之后的你比；听了之后对我触动很大，我说，那就去试试找工作吧；我想，几年后，我再回来看今天，会发现今天将是改变我一生的日子（现在一年后来看，的确是这样）；\n有一句话，很符合我那天的想法： “当你老了，回顾一生，就会发觉：什么时候出国读书、什么时候决定做第一份职业、何时选定了对象而恋爱、什么时候结婚，其实都是命运的巨变。只是当时站在三岔路口，眼见风云千樯，你作出抉择的那一日，在日记上，相当沉闷和平凡，当时还以为是生命中普通的一天。”\n然后就放弃了考研，不知道算不算半途而废，因为自己都还没有走到半途；自己的这个打算对自己将来发展是利是弊，现在还未可知也；\n至此，我的考研结束。\n关于工作 关键词：期待\n工作的念头出现的比较晚，就是在上面提到的那个讲座之后，自己才开始考虑找工作的事情；\n最先当然是写简历，简历花了挺多时间写，不过也不是一天搞完的，我采取的方法是一天进步一点点，也就是一天写一部分，从个人信息、项目经历、个人荣誉一直写到专业技能，然后又润色润色，搞了差不多一个多星期才输出一份我自己比较满意的简历；\n然后就是找合适的公司投递简历，那个时候还比较早，很多公司的提前批都还没开始，所以就先准备投个实习练练手，说起来也没有投几个，我记得就投了海康威视和OPPO这两个公司的实习，OPPO那个是一直没什么什么进展，前几天去看才发现我连简历筛选都没过，可能是跟JD不太匹配吧；至于海康威视，倒是一步步都正常进行，简历筛选过了，然后HR打电话问了我的一些基本情况，然后是技术面和HR面，当然也都顺利通过了，最后我也在杭州海康威视实习了整两个月的时间，虽然最后因为实习时间不够的缘故未能参加最后的转正答辩，但是我还是很感谢我在海康的那段实习经历，第一次让我接触到了实际的工作内容；\n有一个比较神奇的地方，就是我后来拿到的大疆Offer的面试甚至比海康面试还要早一些，当我海康一面的时候，我就已经完成了大疆三面的流程在等最后结果了；\n大疆是我很早就接触到的公司，我在大二参加了RoboMaster比赛，而这个比赛就是大疆举办的，所以我与大疆还是有一些小缘分的；大疆提前批很早，我记得是4月25那天就开放投递简历了，大疆的提前批其实更准确的说法应该是“RM专属通道”，是为曾经参加过RM比赛的同学们开通的一个投递通道，我刚好参加过一年这个比赛，所以就顺理成章地投递了简历，后来我才知道我竟然还是投递专属通道的前一百位同学中的一个，还收到了大疆寄给我的一份礼物；\n大疆的面试流程走的很快，基本一周一个进度，所以在面试海康的时候我就已经完成了大疆的面试流程，面试大疆的面试经验对后边我面试海康起到了非常大的帮助；具体的时间表可以看一下我之前写的面经；\n当我拿到海康实习的Offer后，我就决定去了，毕竟大疆的最后面试结果还没有出来，自己还要为后面的秋招准备，想着是有个实习经验，后面找工作会好一些，所以就去了杭州实习，其实实习时候自己还是比较迷茫的，对于自己到底要找什么样的工作，需要做一些什么样的准备都不太清楚，刷题刷了一些，面试经验也看了不少，但是又怀疑自己的学历到底够不够，硕士那么多，自己一个末流985本科找工作能找到吗，天天也是很焦虑，害怕自己找不到什么好的工作，怀疑自己放弃读研去找工作是不是正确的选择；焦虑归焦虑，日子还是一天天的过，看看书，做一点任务，刷刷题目，天天吃的也不少，可能也是海康餐厅的伙食不错；\n转折点就在七月份的一天，我接到了大疆HR小姐姐打来的Offer Call，也收到了邮件发来的录用意向书；\n我当时就想：好哦，我的秋招结束了；\n知道自己拿到大疆Offer的那一刻，自己明显不那么焦虑了，当然学东西也还在学，但是更偏向自己兴趣而不是那些八股文了，因为在我看来，大疆的确是我比较好的工作去向了，不论是我面试的感受还是平常了解到的，大疆确实是我很喜欢的一家公司；\n工作找到了，在海康又待了一个月，实习也结束了，又回到了学校；虽然拿到了录用意向书，但是又听说很多公司会有毁意向书的情况存在，又开始焦虑了，然后自己大四上学期又基本没有什么课程任务，就自己开始一天天摆弄自己的小玩意还有等待着最后的正式Offer，然后就在十月底的一天等到了，这样秋招才是正式结束了，悬着的心放下了，自己的秋招之旅也是有了一个满意的结果；\n我对工作这件事感觉很陌生，自己上了快20年的学，终于也要到了工作的时候了，有期待也有不安；\n但是日子还是一天天的过下去，一眼望到头的生活我不喜欢；\n我希望自己成长，能成为自己小时候梦想的工程师那样：无所不能。\n关于未来 关键词：无所不能\n我前几天看到了一段话，感觉挺有道理：\n生活不可能像你想象得那么好，但也不会像你想象得那么糟。我觉得人的脆弱和坚强都超乎自己的想象。有时，我可能脆弱得一句话就泪流满面，有时，也发现自己咬着牙走了很长的路。 ——莫泊桑《一生》\n越往前走越能发现自己的路变得清晰；回想自己刚上大学时候那迷茫的样子，再看自己现在关于未来的想法，路变得越来越清晰了；\n沿着这条路走下去，我也能成为自己期望的那种人；\n新的一年，我希望我的家人平安幸福；\n新的一年，我希望自己能在梦想的路上越走越远；\n我想，我会成为自己想成为的那个工程师，掌握很多技能，面对问题，自己总能想到办法；\n我相信我会成为这样的人：聪明、乐观、幽默、不言放弃、坚信问题终会被解决；\n","permalink":"https://fan-pengfei.top/posts/%E6%88%91%E7%9A%84%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%932022/","summary":"\u003cblockquote\u003e\n\u003cp\u003e对于我来说，2022年是极为重要的一年；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"18dfe9c8cdfa86554cec30e2abf961e\" loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%932022/img-1.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"关于读研\"\u003e关于读研\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e关键词：迷茫\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在去年这个时候，我还在考虑读研的事情，我记得自己大三上学期所有科目成绩出来的那一天，我拿计算器算了一遍又一遍，最后得到一个心凉的结果：\u003cstrong\u003e即使我大三下学期的核心课程考试都是100分，我也够不到保研的名额\u003c/strong\u003e；算出来这个结果之后，说真的是心凉了一截；\u003c/p\u003e\n\u003cp\u003e虽然自己之前也大概估计着保研比较悬（每次考试都是考前突击，怎么可能稳保研），但是当自己真的算出来保研无望的那一刻，心里还是失落的；失落归失落，自己总还是要做出一个选择：考研或者工作；当然初期我根本没考虑工作的事情，一是对读研确实还有向往，二是我认识的同学或者学长他们绝大多数的选择都是考研；算是迷茫也算是随大流吧，我就打算考研；当然那个时候还比较早，才是一二月份，寒假在家那些天带着失落，自己也没开始复习，在家还是搞一点东西，在咸鱼接一些单子，自己搞一点东西做，在家也算开心；\u003c/p\u003e\n\u003cp\u003e开学之后回到学校，就真要开始准备复习了，自己开始也没买什么纸质资料，网上找了些英语数学的考研课程就开始复习了，前些天倒也算努力，还制定了一个简单的复习时间表，英语看的刘晓燕老师的视频课程，数学是看的李永乐老师的视频教程和习题册；准备了一两个月吧，那些天上课时候偷偷复习，晚上也经常自己去中楼的自习室复习；复习这么久，有进展没有嘞，说实话，没什么进展，说是考研复习，自己心里没什么劲头，也只是看同学他们都开始复习，自己跟风罢了；\u003c/p\u003e\n\u003cp\u003e复习时候，感觉很难受，可能也是自从高中之后自己再也没有像这样坐在那一坐一整天地复习，学来学去，进到自己脑袋的东西也不知道有多少；那时候为了复习还拒绝了很多事情，跟老师说我要考研，推掉本来要我做的项目，跟学姐说我要考研，推掉介绍给我的外快，跟父母说我要考研，让他们安心不要经常给我打电话，看起来自己是为考研做足了准备，貌似我就是要认真复习准备考研了；\u003c/p\u003e\n\u003cp\u003e在刚回到学校不久，班助学长他们组织了一个考研就业动员大会，从这里也能看出考研和工作的人数比例，我记得讲考研经验的有三四个人，而讲工作经验的只有一个人（这位学长应该是拿了TP-LINK SP或者更高档次的Offer），我还用手机的记事本记了很多考研经验，以备我后边用；\u003c/p\u003e\n\u003cp\u003e那我是什么时候改变想法了呢，我想是那天听的一个讲座，是毕业工作几年的学长们开的讲座，中间也讲到了工作和读研的抉择，最打动我的是一句话：\u003cstrong\u003e你应该考虑读研三年和工作三年哪一个对自己的帮助大，你不应该拿着你现在去跟读研三年之后的你比，而应该拿读研三年之后的你和工作三年之后的你比\u003c/strong\u003e；听了之后对我触动很大，我说，那就去试试找工作吧；我想，几年后，我再回来看今天，会发现今天将是改变我一生的日子（现在一年后来看，的确是这样）；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e有一句话，很符合我那天的想法：\n“当你老了，回顾一生，就会发觉：什么时候出国读书、什么时候决定做第一份职业、何时选定了对象而恋爱、什么时候结婚，其实都是命运的巨变。只是当时站在三岔路口，眼见风云千樯，你作出抉择的那一日，在日记上，相当沉闷和平凡，当时还以为是生命中普通的一天。”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e然后就放弃了考研，不知道算不算半途而废，因为自己都还没有走到半途；自己的这个打算对自己将来发展是利是弊，现在还未可知也；\u003c/p\u003e\n\u003cp\u003e至此，我的考研结束。\u003c/p\u003e\n\u003ch2 id=\"关于工作\"\u003e关于工作\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e关键词：期待\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e工作的念头出现的比较晚，就是在上面提到的那个讲座之后，自己才开始考虑找工作的事情；\u003c/p\u003e\n\u003cp\u003e最先当然是写简历，简历花了挺多时间写，不过也不是一天搞完的，我采取的方法是一天进步一点点，也就是一天写一部分，从个人信息、项目经历、个人荣誉一直写到专业技能，然后又润色润色，搞了差不多一个多星期才输出一份我自己比较满意的简历；\u003c/p\u003e\n\u003cp\u003e然后就是找合适的公司投递简历，那个时候还比较早，很多公司的提前批都还没开始，所以就先准备投个实习练练手，说起来也没有投几个，我记得就投了海康威视和OPPO这两个公司的实习，OPPO那个是一直没什么什么进展，前几天去看才发现我连简历筛选都没过，可能是跟JD不太匹配吧；至于海康威视，倒是一步步都正常进行，简历筛选过了，然后HR打电话问了我的一些基本情况，然后是技术面和HR面，当然也都顺利通过了，最后我也在杭州海康威视实习了整两个月的时间，虽然最后因为实习时间不够的缘故未能参加最后的转正答辩，但是我还是很感谢我在海康的那段实习经历，第一次让我接触到了实际的工作内容；\u003c/p\u003e\n\u003cp\u003e有一个比较神奇的地方，就是我后来拿到的大疆Offer的面试甚至比海康面试还要早一些，当我海康一面的时候，我就已经完成了大疆三面的流程在等最后结果了；\u003c/p\u003e\n\u003cp\u003e大疆是我很早就接触到的公司，我在大二参加了RoboMaster比赛，而这个比赛就是大疆举办的，所以我与大疆还是有一些小缘分的；大疆提前批很早，我记得是4月25那天就开放投递简历了，大疆的提前批其实更准确的说法应该是“RM专属通道”，是为曾经参加过RM比赛的同学们开通的一个投递通道，我刚好参加过一年这个比赛，所以就顺理成章地投递了简历，后来我才知道我竟然还是投递专属通道的前一百位同学中的一个，还收到了大疆寄给我的一份礼物；\u003c/p\u003e\n\u003cp\u003e大疆的面试流程走的很快，基本一周一个进度，所以在面试海康的时候我就已经完成了大疆的面试流程，面试大疆的面试经验对后边我面试海康起到了非常大的帮助；具体的时间表可以看一下我之前写的\u003ca href=\"https://www.fan-pengfei.top/2022/10/31/DJI%E9%9D%A2%E7%BB%8F/#more\"\u003e面经\u003c/a\u003e；\u003c/p\u003e\n\u003cp\u003e当我拿到海康实习的Offer后，我就决定去了，毕竟大疆的最后面试结果还没有出来，自己还要为后面的秋招准备，想着是有个实习经验，后面找工作会好一些，所以就去了杭州实习，其实实习时候自己还是比较迷茫的，对于自己到底要找什么样的工作，需要做一些什么样的准备都不太清楚，刷题刷了一些，面试经验也看了不少，但是又怀疑自己的学历到底够不够，硕士那么多，自己一个末流985本科找工作能找到吗，天天也是很焦虑，害怕自己找不到什么好的工作，怀疑自己放弃读研去找工作是不是正确的选择；焦虑归焦虑，日子还是一天天的过，看看书，做一点任务，刷刷题目，天天吃的也不少，可能也是海康餐厅的伙食不错；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"f9385dba3d24e4bec7a5d67538cda49\" loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%932022/img-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"4865cacbde4839cbfa36be10b06bb00\" loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%932022/img-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e转折点就在七月份的一天，我接到了大疆HR小姐姐打来的Offer Call，也收到了邮件发来的录用意向书；\u003c/p\u003e\n\u003cp\u003e我当时就想：\u003cstrong\u003e好哦，我的秋招结束了；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e知道自己拿到大疆Offer的那一刻，自己明显不那么焦虑了，当然学东西也还在学，但是更偏向自己兴趣而不是那些八股文了，因为在我看来，大疆的确是我比较好的工作去向了，不论是我面试的感受还是平常了解到的，大疆确实是我很喜欢的一家公司；\u003c/p\u003e\n\u003cp\u003e工作找到了，在海康又待了一个月，实习也结束了，又回到了学校；虽然拿到了录用意向书，但是又听说很多公司会有毁意向书的情况存在，又开始焦虑了，然后自己大四上学期又基本没有什么课程任务，就自己开始一天天摆弄自己的小玩意还有等待着最后的正式Offer，然后就在十月底的一天等到了，这样秋招才是正式结束了，悬着的心放下了，自己的秋招之旅也是有了一个满意的结果；\u003c/p\u003e\n\u003cp\u003e我对工作这件事感觉很陌生，自己上了快20年的学，终于也要到了工作的时候了，有期待也有不安；\u003c/p\u003e\n\u003cp\u003e但是日子还是一天天的过下去，一眼望到头的生活我不喜欢；\u003c/p\u003e\n\u003cp\u003e我希望自己成长，能成为自己小时候梦想的工程师那样：无所不能。\u003c/p\u003e\n\u003ch2 id=\"关于未来\"\u003e关于未来\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e关键词：无所不能\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e我前几天看到了一段话，感觉挺有道理：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e生活不可能像你想象得那么好，但也不会像你想象得那么糟。我觉得人的脆弱和坚强都超乎自己的想象。有时，我可能脆弱得一句话就泪流满面，有时，也发现自己咬着牙走了很长的路。\n——莫泊桑《一生》\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e越往前走越能发现自己的路变得清晰；回想自己刚上大学时候那迷茫的样子，再看自己现在关于未来的想法，路变得越来越清晰了；\u003c/p\u003e\n\u003cp\u003e沿着这条路走下去，我也能成为自己期望的那种人；\u003c/p\u003e\n\u003cp\u003e新的一年，我希望我的家人平安幸福；\u003c/p\u003e\n\u003cp\u003e新的一年，我希望自己能在梦想的路上越走越远；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e我想，我会成为自己想成为的那个工程师，掌握很多技能，面对问题，自己总能想到办法；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e我相信我会成为这样的人：聪明、乐观、幽默、不言放弃、坚信问题终会被解决；\u003c/strong\u003e\u003c/p\u003e","title":"我的年终总结（2022）"},{"content":" 收罗一些常用的网站；\n1、立创开源硬件平台 当自己做一些硬件电路时在该网站上找一些参考设计；\n立创开源硬件平台\n2、立创商城 用于查询芯片的数据手册，有时也在该商城购买一些元器件，比较保真，开发票也很快；\n立创商城\n3、古月居ROS 用于查看一些ROS相关的教程，依稀记得自己入门ROS就是学习的古月居的教程；\n古月居ROS\n4、学长的博客 学长的博客网站，我记得自己AD库就是用的学长放在博客上面的；\n刘威学长的博客\n5、阿里云域名管理控制台 自己博客网站的域名就是在阿里云购买的，续费也是通过该平台进行；\n阿里云域名控制台\n6、ROBOMASTER赛事报名系统(专属通道投递简历) ROBOMASTER赛事报名系统\n7、牛客 找工作时候经常用的，用来看一些面经；\n牛客\n8、湖南大学WebVPN系统门户 不需要运行VPN客户端即可；\n湖南大学WebVPN系统门户\n9、威伯斯云登录页面 VPN登录页面；\n威伯斯云登录页面\n10、书伴 很多关于Kindle的使用技巧;\n书伴\n11、在线思维导图 用于在线绘制思维导图，可以导出为图片，很方便；\n在线思维导图\n12、阮一峰的博客 比较有意思；\n阮一峰的博客\n13、Typora官方中文站 有一些软件使用技巧；\nTypora官方中文站\n14、阿里云资源站 有很多的纪录片和电影小说可以下载；\n阿里云资源站\n15、磁力天堂 聚合类网站；\n磁力天堂\n16、哇酷开发者社区 之前搞荔枝派的时候经常使用这个网站；\nWhyCan Forum(哇酷开发者社区)\n17、阿里云矢量图标网站 之前搞LVGL用了很多这个网站上的小图标；\n阿里云矢量图标网站\n","permalink":"https://fan-pengfei.top/posts/%E5%B8%B8%E7%94%A8%E7%BD%91%E7%AB%99/","summary":"\u003cblockquote\u003e\n\u003cp\u003e收罗一些常用的网站；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1立创开源硬件平台\"\u003e1、立创开源硬件平台\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e当自己做一些硬件电路时在该网站上找一些参考设计；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://oshwhub.com/\"\u003e立创开源硬件平台\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"2立创商城\"\u003e2、立创商城\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用于查询芯片的数据手册，有时也在该商城购买一些元器件，比较保真，开发票也很快；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.szlcsc.com/\"\u003e立创商城\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"3古月居ros\"\u003e3、古月居ROS\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用于查看一些ROS相关的教程，依稀记得自己入门ROS就是学习的古月居的教程；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.guyuehome.com/\"\u003e古月居ROS\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"4学长的博客\"\u003e4、学长的博客\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e学长的博客网站，我记得自己AD库就是用的学长放在博客上面的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://seaskyl.gitee.io/blog/\"\u003e刘威学长的博客\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"5阿里云域名管理控制台\"\u003e5、阿里云域名管理控制台\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e自己博客网站的域名就是在阿里云购买的，续费也是通过该平台进行；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://dc.console.aliyun.com/next/index?spm=5176.12818093.ProductAndResource--ali--widget-product-recent.dre0.bf2616d0gkDRmp#/overview\"\u003e阿里云域名控制台\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"6robomaster赛事报名系统专属通道投递简历\"\u003e6、ROBOMASTER赛事报名系统(专属通道投递简历)\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://registration.robomaster.com/registration-center\"\u003eROBOMASTER赛事报名系统\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"7牛客\"\u003e7、牛客\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e找工作时候经常用的，用来看一些面经；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.nowcoder.com/\"\u003e牛客\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"8湖南大学webvpn系统门户\"\u003e8、湖南大学WebVPN系统门户\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e不需要运行VPN客户端即可；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://webvpn.hnu.edu.cn/\"\u003e湖南大学WebVPN系统门户\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"9威伯斯云登录页面\"\u003e9、威伯斯云登录页面\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eVPN登录页面；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.vps000.org/site/login\"\u003e威伯斯云登录页面\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"10书伴\"\u003e10、书伴\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e很多关于Kindle的使用技巧;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://bookfere.com/\"\u003e书伴\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"11在线思维导图\"\u003e11、在线思维导图\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用于在线绘制思维导图，可以导出为图片，很方便；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.processon.com/\"\u003e在线思维导图\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"12阮一峰的博客\"\u003e12、阮一峰的博客\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e比较有意思；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://ruanyifeng.com/blog/\"\u003e阮一峰的博客\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"13typora官方中文站\"\u003e13、Typora官方中文站\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e有一些软件使用技巧；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://typoraio.cn/\"\u003eTypora官方中文站\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"14阿里云资源站\"\u003e14、阿里云资源站\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e有很多的纪录片和电影小说可以下载；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.yunpanziyuan.com/\"\u003e阿里云资源站\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"15磁力天堂\"\u003e15、磁力天堂\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e聚合类网站；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://btlm.cc/#term-33\"\u003e磁力天堂\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"16哇酷开发者社区\"\u003e16、哇酷开发者社区\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e之前搞荔枝派的时候经常使用这个网站；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://whycan.com/index.html\"\u003eWhyCan Forum(哇酷开发者社区)\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"17阿里云矢量图标网站\"\u003e17、阿里云矢量图标网站\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e之前搞LVGL用了很多这个网站上的小图标；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.iconfont.cn/\"\u003e阿里云矢量图标网站\u003c/a\u003e\u003c/p\u003e","title":"常用网站"},{"content":" 最近Ubuntu2004虚拟机无法联网，而且这个问题很常见，就记录一下；\n参考：VMware Ubuntu20.04无法联网的解决方法\n一、安装环境及版本 宿主机 Windows 10 虚拟机软件版本 VMware Workstation 16 Pro ubuntu版本 Ubuntu 20.04\n二、解决方法 1、虚拟机-\u0026gt;设置-\u0026gt;网络适配器：进行如下设置：\n2、然后打开终端窗口，在终端窗口输入如下命令：\nsudo service network-manager stop sudo rm /var/lib/NetworkManager/NetworkManager.state sudo service network-manager start 3、再运行ifconfig便可以发现网络已经恢复正常；\n","permalink":"https://fan-pengfei.top/posts/ubuntu2004%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91%E9%97%AE%E9%A2%98/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近Ubuntu2004虚拟机无法联网，而且这个问题很常见，就记录一下；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e参考：\u003ca href=\"https://blog.csdn.net/weixin_45084986/article/details/119192544?spm=1001.2014.3001.5506\"\u003eVMware Ubuntu20.04无法联网的解决方法\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"一安装环境及版本\"\u003e一、安装环境及版本\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e宿主机\nWindows 10\n虚拟机软件版本\nVMware Workstation 16 Pro\nubuntu版本\nUbuntu 20.04\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"二解决方法\"\u003e二、解决方法\u003c/h2\u003e\n\u003cp\u003e1、虚拟机-\u0026gt;设置-\u0026gt;网络适配器：进行如下设置：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/ubuntu2004%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91%E9%97%AE%E9%A2%98/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e2、然后打开终端窗口，在终端窗口输入如下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo service network-manager stop\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo rm /var/lib/NetworkManager/NetworkManager.state\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo service network-manager start\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e3、再运行\u003ccode\u003eifconfig\u003c/code\u003e便可以发现网络已经恢复正常；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"88d3a0e09984f60b005c21210c88bd6\" loading=\"lazy\" src=\"/posts/ubuntu2004%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91%E9%97%AE%E9%A2%98/img-2.png\"\u003e\u003c/p\u003e","title":"ubuntu2004无法联网问题"},{"content":" 以基于STM32F103的FOC源码为例分析；\n保存电机参数 采用宏定义开关来决定是否要进行参数校准；\n//1 已校准 //0 未校准 #define IS_ALIGN 0 //参数数据结构体 struct ALIGN_DATA { long direction; int pole; float zero_angle; } DATA_ALIGN; //根据宏定义判断是否要更新FLASH中存储的参数 #if IS_ALIGN Internal_ReadFlash(ALIGN_ANGLE_ADDR, (uint32_t *)\u0026amp;DATA_ALIGN, 3);//读取内部FLASH中的参数 sensor_direction = DATA_ALIGN.direction; //方向 pole_pairs = DATA_ALIGN.pole; //极对数 zero_electric_angle = DATA_ALIGN.zero_angle;//零点角度 #else // //此处略去参数识别代码 // DATA_ALIGN.direction = sensor_direction; //方向 DATA_ALIGN.pole = pole_pairs; //极对数 DATA_ALIGN.zero_angle = zero_electric_angle;//零点角度 Internal_WriteFlash(ALIGN_ANGLE_ADDR, (uint32_t *)\u0026amp;DATA_ALIGN, 3);//将参数写入单片机内部FLASH中 通过UART通信控制电机 /** * @brief 简易串口命令接收，需在while循环里重复调用该函数 * @return 无 */ void commander_run(void) { if ((USART_RX_STA \u0026amp; 0x8000) != 0) { switch (USART_RX_BUF[0]) { case \u0026#39;H\u0026#39;: printf(\u0026#34;Hello World!\\r\\n\u0026#34;); break; case \u0026#39;T\u0026#39;: // T6.28 target = atof((const char *)(USART_RX_BUF + 1)); printf(\u0026#34;RX=%.4f\\r\\n\u0026#34;, target); break; case \u0026#39;D\u0026#39;: // D M1_Disable; printf(\u0026#34;OK!\\r\\n\u0026#34;); break; case \u0026#39;E\u0026#39;: // E M1_Enable; printf(\u0026#34;OK!\\r\\n\u0026#34;); break; } USART_RX_STA = 0; } } #define USART_REC_LEN 256 unsigned char USART_RX_BUF[USART_REC_LEN]; //接收缓冲，usart.h中定义长度 //接收状态 // bit15 接收完成标志 // bit14 接收到0x0D // bit13~0 接收的字节数 unsigned short USART_RX_STA = 0; //接收状态标志 //发送的命令必须以\u0026#34;\\r\\n\u0026#34;作为结尾，以标志命令的结束 void USART1_IRQHandler(void) //串口1中断程序 { unsigned char Res; if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // { Res = USART_ReceiveData(USART1); //读取接收到的字节 if ((USART_RX_STA \u0026amp; 0x8000) == 0) //接收未完成 { if (USART_RX_STA \u0026amp; 0x4000) //接收到了0x0d { if (Res != 0x0a) { USART_RX_STA = 0; //接收错误，重新开始 } else { USART_RX_STA |= 0x8000; //接收完成 USART_RX_BUF[USART_RX_STA \u0026amp; 0X3FFF] = \u0026#39;\\0\u0026#39;; //最后一个字节放\u0026#39;0’，方便判断 } } else //还没收到0x0D { if (Res == 0x0d) { USART_RX_STA |= 0x4000; } else { USART_RX_BUF[USART_RX_STA \u0026amp; 0X3FFF] = Res; USART_RX_STA++; if (USART_RX_STA \u0026gt; (USART_REC_LEN - 1)) { USART_RX_STA = 0; //接收错误，重新开始 } } } } } } ","permalink":"https://fan-pengfei.top/posts/foc%E5%9F%BA%E7%A1%80%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/","summary":"\u003cblockquote\u003e\n\u003cp\u003e以基于STM32F103的FOC源码为例分析；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"保存电机参数\"\u003e保存电机参数\u003c/h3\u003e\n\u003cp\u003e采用宏定义开关来决定是否要进行参数校准；\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//1 已校准\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//0 未校准\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define IS_ALIGN 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//参数数据结构体\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e ALIGN_DATA\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e direction;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e pole;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efloat\u003c/span\u003e zero_angle;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} DATA_ALIGN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//根据宏定义判断是否要更新FLASH中存储的参数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#if IS_ALIGN\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eInternal_ReadFlash\u003c/span\u003e(ALIGN_ANGLE_ADDR, (\u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eDATA_ALIGN, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e);\u003cspan style=\"color:#75715e\"\u003e//读取内部FLASH中的参数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sensor_direction \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DATA_ALIGN.direction;    \u003cspan style=\"color:#75715e\"\u003e//方向\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    pole_pairs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DATA_ALIGN.pole;               \u003cspan style=\"color:#75715e\"\u003e//极对数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    zero_electric_angle \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DATA_ALIGN.zero_angle;\u003cspan style=\"color:#75715e\"\u003e//零点角度\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#else\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//此处略去参数识别代码\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DATA_ALIGN.direction \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sensor_direction;    \u003cspan style=\"color:#75715e\"\u003e//方向\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DATA_ALIGN.pole \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e pole_pairs;               \u003cspan style=\"color:#75715e\"\u003e//极对数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DATA_ALIGN.zero_angle \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e zero_electric_angle;\u003cspan style=\"color:#75715e\"\u003e//零点角度\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eInternal_WriteFlash\u003c/span\u003e(ALIGN_ANGLE_ADDR, (\u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eDATA_ALIGN, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e);\u003cspan style=\"color:#75715e\"\u003e//将参数写入单片机内部FLASH中\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"通过uart通信控制电机\"\u003e通过UART通信控制电机\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @brief 简易串口命令接收，需在while循环里重复调用该函数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e * @return 无\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecommander_run\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ((USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x8000\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eswitch\u003c/span\u003e (USART_RX_BUF[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;H\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World!\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;T\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// T6.28\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            target \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eatof\u003c/span\u003e((\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)(USART_RX_BUF \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;RX=%.4f\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, target);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;D\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// D\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            M1_Disable;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;OK!\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;E\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// E\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            M1_Enable;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;OK!\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\r\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define USART_REC_LEN 256\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e USART_RX_BUF[USART_REC_LEN]; \u003cspan style=\"color:#75715e\"\u003e//接收缓冲，usart.h中定义长度\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//接收状态\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// bit15  接收完成标志\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// bit14  接收到0x0D\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// bit13~0  接收的字节数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eshort\u003c/span\u003e USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//接收状态标志\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//发送的命令必须以\u0026#34;\\r\\n\u0026#34;作为结尾，以标志命令的结束\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eUSART1_IRQHandler\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e//串口1中断程序\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e Res;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eUSART_GetITStatus\u003c/span\u003e(USART1, USART_IT_RXNE) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e RESET) \u003cspan style=\"color:#75715e\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        Res \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eUSART_ReceiveData\u003c/span\u003e(USART1); \u003cspan style=\"color:#75715e\"\u003e//读取接收到的字节\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ((USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x8000\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e//接收未完成\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x4000\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e//接收到了0x0d\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (Res \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x0a\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//接收错误，重新开始\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e|=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x8000\u003c/span\u003e;                     \u003cspan style=\"color:#75715e\"\u003e//接收完成\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_BUF[USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003eX3FFF] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\0\u0026#39;\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//最后一个字节放\u0026#39;0’，方便判断\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e//还没收到0x0D\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (Res \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x0d\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e|=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0x4000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_BUF[USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003eX3FFF] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Res;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    USART_RX_STA\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e (USART_REC_LEN \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        USART_RX_STA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//接收错误，重新开始\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"FOC基础源码分析"},{"content":" CSAPP学习中所记录的笔记；\n第七章 链接 链接是将各种代码和数据片段收集并组合成为一个单一文件的过程，这个文件可被加载（复制）到内存中并执行。\n链接可以执行于： 编译时：也就是在源代码被翻译成机器代码时； 加载时：也就是在程序被加载器加载到内存并执行时； 运行时：也就是由应用程序来执行； 在早期系统，链接是手动执行的； 在现代系统，链接是由叫做链接器的程序自动执行的；\n链接器使分离编译成为可能；\n学习链接的必要性：\n理解链接器将帮助你构造大型程序；\n理解链接器将帮助你避免一些危险的编程错误（例如，在默认情况下，错误的定义多个全局变量的程序将通过链接器，而不产生任何警告信息）；\n理解链接器将帮助你理解语言的作用域规则是如何实现的（全局变量和局部变量之间的区别？static属性的变量或者函数到底意味着什么）；\n理解链接将帮助你理解其他重要的系统概念（比如加载和运行程序、虚拟内存、分页、内存映射等）；\n理解链接将使你能够利用链接库；\n7.1 编译器驱动程序 贯穿本章的示例程序：\nmain.c main函数初始化一个整数数组，然后调用sum函数对数组进行求和；\nint sum(int *a, int n); int array[2] = {1, 2}; int main() { int val = sum(array, 2); return val; } sum.c\nint sum(int *a, int n) { int i, s=0; for(i=0;i 以上命令等同于以下命令： \u0026gt; 首先运行预处理器，将main.c翻译成ASCII码的中间文件main.i： \u0026gt; ```bash \u0026gt; cpp main.c /tmp/main.i \u0026gt; ``` \u0026gt; 接下来，驱动程序运行C编译器，将main.i翻译成一个ASCII汇编语言文件main.s： \u0026gt; ```plaintext \u0026gt; cc1 /temp/main.i -Og -o /tmp/main.s \u0026gt; ``` \u0026gt; 然后，驱动程序运行汇编器，它将main.s翻译成一个可重定位目标文件main.o： \u0026gt; ```bash \u0026gt; as -o /temp/main.o /tmp/main.s \u0026gt; ``` \u0026gt; 驱动程序经过相同的过程生成sum.o。 \u0026gt; 最后，运行链接器程序ld，将main.o和sum.o以及一些必要的目标文件组合起来，创建一个可执行目标文件prog： \u0026gt; ```bash \u0026gt; ld -o prog /tmp/main.o /tmp/sum.o \u0026gt; ``` 要运行可执行文件prog，使用以下命令： ```bash ./prog shell调用操作系统中一个叫做加载器的函数，它将可执行文件prog中的代码和数据复制到内存，然后将控制转移到这个程序的开头；\n7.2 静态链接 像Linux LD程序这样的静态链接器以一组可重定位目标文件和命令行参数作为输入，生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节组成，每一节都是一个连续的字节序列；\n指令在一节中，初始化了的全局变量在另一节中，而未初始化的变量又在另外一节中；\n链接器的两个主要任务：\n符号解析： 目标文件定义和引用符号，每个符号对应于一个函数、一个全局变量或一个静态变量。 符号解析的目的是将每个符号引用正好和一个符号定义关联起来；\n重定位： 链接器通过把每个符号定义与一个内存位置关联起来，从而重定位这些节，然后修改所有对这个符号的引用，使得他们指向这个内存位置；\n7.3 目标文件 目标文件有三种形式：\n可重定位目标文件： 包含二进制代码和数据，其形式可以在编译时与其他可重定位目标文件合并起来，创建一个可执行目标文件；\n可执行目标文件： 包含二进制代码和数据，其形式可以被直接复制到内存并执行；\n共享目标文件： 一种特殊类型的可重定位目标文件，可以在加载或者运行时被动态地加载到内存并链接；\n可重定位目标文件（包括共享目标文件）是由编译器和汇编器生成的； 可执行目标文件是由链接器生成的；\n关于各个系统的目标文件格式：\nWindows：可移植可执行格式（PE）；\nMac OS-X：Mach-O格式；\nLinux和Unix：可执行可链接格式（ELF）；\n7.4 可重定位目标文件 ","permalink":"https://fan-pengfei.top/posts/csapp%E7%AC%94%E8%AE%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003eCSAPP学习中所记录的笔记；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"第七章-链接\"\u003e第七章 链接\u003c/h2\u003e\n\u003cp\u003e链接是将各种代码和数据片段收集并组合成为一个单一文件的过程，这个文件可被加载（复制）到内存中并执行。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e链接可以执行于：\n编译时：也就是在源代码被翻译成机器代码时；\n加载时：也就是在程序被加载器加载到内存并执行时；\n运行时：也就是由应用程序来执行；\n在早期系统，链接是手动执行的；\n在现代系统，链接是由叫做链接器的程序自动执行的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e链接器使分离编译成为可能；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e学习链接的必要性：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e理解链接器将帮助你构造大型程序；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e理解链接器将帮助你避免一些危险的编程错误（例如，在默认情况下，错误的定义多个全局变量的程序将通过链接器，而不产生任何警告信息）；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e理解链接器将帮助你理解语言的作用域规则是如何实现的（全局变量和局部变量之间的区别？static属性的变量或者函数到底意味着什么）；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e理解链接将帮助你理解其他重要的系统概念（比如加载和运行程序、虚拟内存、分页、内存映射等）；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e理解链接将使你能够利用链接库；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"71-编译器驱动程序\"\u003e7.1 编译器驱动程序\u003c/h3\u003e\n\u003cp\u003e贯穿本章的示例程序：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003emain.c\nmain函数初始化一个整数数组，然后调用sum函数对数组进行求和；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esum\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ea, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e n);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e array[\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e val \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esum\u003c/span\u003e(array, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e val;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003esum.c\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esum\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ea, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e n)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i, s\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e(i\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;i \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e以上命令等同于以下命令：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e首先运行预处理器，将\u003c/span\u003emain.c翻译成ASCII码的中间文件main.i\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ebash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e cpp main.c \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etmp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.i\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e接下来，驱动程序运行\u003c/span\u003eC编译器\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，将\u003c/span\u003emain.i翻译成一个ASCII汇编语言文件main.s\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003eplaintext\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e cc1 \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etemp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.i \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eOg \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eo \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etmp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.s\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e然后，驱动程序运行汇编器，它将\u003c/span\u003emain.s翻译成一个可重定位目标文件main.o\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ebash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e as \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eo \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etemp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.o \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etmp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.s\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e驱动程序经过相同的过程生成\u003c/span\u003esum.o\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e最后，运行链接器程序\u003c/span\u003eld\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，将\u003c/span\u003emain.o和sum.o以及一些必要的目标文件组合起来\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，创建一个可执行目标文件\u003c/span\u003eprog\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ebash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e ld \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eo prog \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etmp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emain.o \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003etmp\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003esum.o\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e要运行可执行文件\u003c/span\u003eprog\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e，使用以下命令：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ebash\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eprog\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eshell调用操作系统中一个叫做加载器的函数，它将可执行文件prog中的代码和数据复制到内存，然后将控制转移到这个程序的开头；\u003c/p\u003e","title":"CSAPP笔记"},{"content":" DJI面试经验；\n流程时间表： 事件 日期\n投递简历 4月25日\n性格测评 4月27日\n第一次面试 5月3日\n第二次面试 5月13日\n第三次面试 5月20日\n电话OC 7月22日\n收到录用意向书 7月22日\n谈薪电话 10月27日\n收到正式OFFER 10月27日\n沟通三方信息 10月28日\n性格测评： 这个测评的题量还是比较大的（有大概一百多道题），不过时间也是完全足够的，有大概两个小时；\n题型全部都是选择题；主要内容为：一些简单的行测题、图形推理题、性格测试题、各种主观题，从难度来说都不是很难，不过听说测评会刷掉挺多的人，不过也没必要刻意去违心地回答这些问题，表现真实一些就可以，毕竟如果是跟自己价值观相差很大的公司，你进入后也不会很舒服；\n答题过程中会需要开摄像头，如果自己电脑不带前置摄像头的话，可能需要自备一个；\n答题过程中是可以使用草稿纸进行演算的，这个是没有任何问题的；\n其他的问题就没有了，可能需要提前了解一些大疆的各种产品信息等等；\nPS:因为我投递的是提前批，所以并没有笔试部分。\n第一次面试： 面试前我很紧张，毕竟是第一次正式面试，甚至比后面的海康暑假实习面试还要早很多；\n面试前我准备了很多很多，把自己做过的项目都总结了一下，从头到尾过了一遍，听说会问比较多RTOS相关的知识，所以又把RTOS相关的知识过了一遍，即使这样，准备的还算比较充分，毕竟自己实际也做过很多很多自己的项目，但还是非常紧张；\n面试平台用的是腾讯会议，会提前发会议链接；\n我很早就在面试间等着，第一场面试的面试官有三位，他们三个轮换着来提问，不过应该其中一位是面试的主导者；\n上来就是自我介绍，我提前准备了一个三分钟自我介绍，阿巴阿巴介绍完，面试官就开始问问题；例如你最满意的项目是什么，为什么会很满意，做项目遇到了什么问题，你是怎样解决的等等诸如此类的问题；\n出乎我意料的是，整个面试中并没有问到什么知识点，唯一问到的就是单片机是如何启动到main函数的，而这个我正好复习过，就顺利地答上来了，面试官问和我回答的这段时间有二十分钟左右，后续就是问我有什么问题要问吗，我就问了几个应届生培养方案之类的问题；\n总的来说，面试过程很愉快，也并没有自己相信的那样紧张；\n大概是第二天，就收到了面试通过的邮件，效率很高；\n第二次面试： 第一次面试之后过了大概一个星期就是第二次面试，这次面试只有一位面试官；\n面试的内容跟第一次面试的内容很相似，也都是自我介绍，问自己做过的项目，最满意的项目是哪个，我就说是曾经做过的小一，然后问为什么会觉得满意，就围绕着这个项目谈了很多（主要可能是我自发的做的这个项目，并没有什么功利性），还问了你有没有跟市面上类似的产品做过比较等等的，你用的这个框架LVGL你觉得有什么不足之处吗；\n后面还问了一些其他的问题，比如你FreeRTOS用的熟吗，队里边是只有你一个人用得熟，还是大家用的都很熟，平常在实验室都待到几点，在队伍里的感受怎么样，曾经有没有用过大疆的产品，这个产品你觉得有什么优点（我提到了大疆的一款无人机）；\n面试官说话让人感觉到很舒服，很愉快，也不会让自己太紧张，这次的面试时间很长，大概有五十多分钟；\n大概当晚我就收到了面试通过的消息；\n第三次面试： 大概第二次面试通过五六天之后就是第三次面试，这次面试的面试官也是一位；第三次面试跟前两次有些不同，更多的是问我性格相关的问题，对项目相关的问题会少一些；\n问的问题有：你用一个词语形容一下自己，这个我当时真的蒙了，就尝试说了几下自己的优势，然后面试官还追问说，那你用一个词语描述下呢？最后好像也没说出什么（还是怪我没有准备完全）；\n还有就是问我有没有跟别人争执过，因为什么而争执，怎么解决的；你觉得你的性格方面有没有什么缺陷等等；\n这场面试感觉答得不是很好，因为有些问题自己之前都没准备到，本以为凉凉了，没想到很快就通过了，然后就是等着综合评估结果；\n大概第二天，登录招聘网站，就发现三面通过了，不过听说还会有综合排序，会刷一部分的人；\n在七月下旬，终于接到OC（Offer Call）了，我的秋招正式结束；\n","permalink":"https://fan-pengfei.top/posts/dji%E9%9D%A2%E7%BB%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003eDJI面试经验；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"流程时间表\"\u003e流程时间表：\u003c/h3\u003e\n\u003cp\u003e事件\n日期\u003c/p\u003e\n\u003cp\u003e投递简历\n4月25日\u003c/p\u003e\n\u003cp\u003e性格测评\n4月27日\u003c/p\u003e\n\u003cp\u003e第一次面试\n5月3日\u003c/p\u003e\n\u003cp\u003e第二次面试\n5月13日\u003c/p\u003e\n\u003cp\u003e第三次面试\n5月20日\u003c/p\u003e\n\u003cp\u003e电话OC\n7月22日\u003c/p\u003e\n\u003cp\u003e收到录用意向书\n7月22日\u003c/p\u003e\n\u003cp\u003e谈薪电话\n10月27日\u003c/p\u003e\n\u003cp\u003e收到正式OFFER\n10月27日\u003c/p\u003e\n\u003cp\u003e沟通三方信息\n10月28日\u003c/p\u003e\n\u003ch3 id=\"性格测评\"\u003e性格测评：\u003c/h3\u003e\n\u003cp\u003e这个测评的题量还是比较大的（有大概一百多道题），不过时间也是完全足够的，有大概两个小时；\u003c/p\u003e\n\u003cp\u003e题型全部都是选择题；主要内容为：一些简单的行测题、图形推理题、性格测试题、各种主观题，从难度来说都不是很难，不过听说测评会刷掉挺多的人，不过也没必要刻意去违心地回答这些问题，表现真实一些就可以，毕竟如果是跟自己价值观相差很大的公司，你进入后也不会很舒服；\u003c/p\u003e\n\u003cp\u003e答题过程中会需要开摄像头，如果自己电脑不带前置摄像头的话，可能需要自备一个；\u003c/p\u003e\n\u003cp\u003e答题过程中是可以使用草稿纸进行演算的，这个是没有任何问题的；\u003c/p\u003e\n\u003cp\u003e其他的问题就没有了，可能需要提前了解一些大疆的各种产品信息等等；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePS:因为我投递的是提前批，所以并没有笔试部分。\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"第一次面试\"\u003e第一次面试：\u003c/h3\u003e\n\u003cp\u003e面试前我很紧张，毕竟是第一次正式面试，甚至比后面的海康暑假实习面试还要早很多；\u003c/p\u003e\n\u003cp\u003e面试前我准备了很多很多，把自己做过的项目都总结了一下，从头到尾过了一遍，听说会问比较多RTOS相关的知识，所以又把RTOS相关的知识过了一遍，即使这样，准备的还算比较充分，毕竟自己实际也做过很多很多自己的项目，但还是非常紧张；\u003c/p\u003e\n\u003cp\u003e面试平台用的是腾讯会议，会提前发会议链接；\u003c/p\u003e\n\u003cp\u003e我很早就在面试间等着，第一场面试的面试官有三位，他们三个轮换着来提问，不过应该其中一位是面试的主导者；\u003c/p\u003e\n\u003cp\u003e上来就是自我介绍，我提前准备了一个三分钟自我介绍，阿巴阿巴介绍完，面试官就开始问问题；例如你最满意的项目是什么，为什么会很满意，做项目遇到了什么问题，你是怎样解决的等等诸如此类的问题；\u003c/p\u003e\n\u003cp\u003e出乎我意料的是，整个面试中并没有问到什么知识点，唯一问到的就是单片机是如何启动到main函数的，而这个我正好复习过，就顺利地答上来了，面试官问和我回答的这段时间有二十分钟左右，后续就是问我有什么问题要问吗，我就问了几个应届生培养方案之类的问题；\u003c/p\u003e\n\u003cp\u003e总的来说，面试过程很愉快，也并没有自己相信的那样紧张；\u003c/p\u003e\n\u003cp\u003e大概是第二天，就收到了面试通过的邮件，效率很高；\u003c/p\u003e\n\u003ch3 id=\"第二次面试\"\u003e第二次面试：\u003c/h3\u003e\n\u003cp\u003e第一次面试之后过了大概一个星期就是第二次面试，这次面试只有一位面试官；\u003c/p\u003e\n\u003cp\u003e面试的内容跟第一次面试的内容很相似，也都是自我介绍，问自己做过的项目，最满意的项目是哪个，我就说是曾经做过的小一，然后问为什么会觉得满意，就围绕着这个项目谈了很多（主要可能是我自发的做的这个项目，并没有什么功利性），还问了你有没有跟市面上类似的产品做过比较等等的，你用的这个框架LVGL你觉得有什么不足之处吗；\u003c/p\u003e\n\u003cp\u003e后面还问了一些其他的问题，比如你FreeRTOS用的熟吗，队里边是只有你一个人用得熟，还是大家用的都很熟，平常在实验室都待到几点，在队伍里的感受怎么样，曾经有没有用过大疆的产品，这个产品你觉得有什么优点（我提到了大疆的一款无人机）；\u003c/p\u003e\n\u003cp\u003e面试官说话让人感觉到很舒服，很愉快，也不会让自己太紧张，这次的面试时间很长，大概有五十多分钟；\u003c/p\u003e\n\u003cp\u003e大概当晚我就收到了面试通过的消息；\u003c/p\u003e\n\u003ch3 id=\"第三次面试\"\u003e第三次面试：\u003c/h3\u003e\n\u003cp\u003e大概第二次面试通过五六天之后就是第三次面试，这次面试的面试官也是一位；第三次面试跟前两次有些不同，更多的是问我性格相关的问题，对项目相关的问题会少一些；\u003c/p\u003e\n\u003cp\u003e问的问题有：你用一个词语形容一下自己，这个我当时真的蒙了，就尝试说了几下自己的优势，然后面试官还追问说，那你用一个词语描述下呢？最后好像也没说出什么（还是怪我没有准备完全）；\u003c/p\u003e\n\u003cp\u003e还有就是问我有没有跟别人争执过，因为什么而争执，怎么解决的；你觉得你的性格方面有没有什么缺陷等等；\u003c/p\u003e\n\u003cp\u003e这场面试感觉答得不是很好，因为有些问题自己之前都没准备到，本以为凉凉了，没想到很快就通过了，然后就是等着综合评估结果；\u003c/p\u003e\n\u003cp\u003e大概第二天，登录招聘网站，就发现三面通过了，不过听说还会有综合排序，会刷一部分的人；\u003c/p\u003e\n\u003cp\u003e在七月下旬，终于接到OC（Offer Call）了，我的秋招正式结束；\u003c/p\u003e","title":"DJI面经"},{"content":" 因为上次做的板子是将FPGA和一块MCU芯片集成在一块的，他们之间就需要通信，当然最容易实现的就是UART串口通信； 下面的代码是一个Demo代码，实现的功能是，RX接收到什么，TX就发送相同的数据； FPGA芯片 AG1280Q48 时钟频率 48MHZ 波特率 921600 RX引脚 PIN_16 TX引脚 PIN_14 LED引脚 PIN_48 时钟输入引脚 PIN_15 复位引脚 PIN_17\n具体代码如下：\n顶层模块 module connect_mcu (input clk, input rst_n, input wire uart_rx, output wire uart_tx, output wire led); wire clk_pll_o; //PLL时钟 inpll pll_inst ( .clkin(clk),\t// PLL.clkin MUST connect to PIN_XX_GB .clkfb(clk_pll_o), .pllen(1\u0026#39;b1), .resetn(rst_n), .clkout0en(1\u0026#39;b1), .clkout1en(1\u0026#39;b0), .clkout2en(1\u0026#39;b0), .clkout3en(1\u0026#39;b0), .clkout0(clk_pll_o), .clkout1(), .clkout2(), .clkout3(), .lock() ); parameter SYSTERM_CLK = 26\u0026#39;d48_000_000; //系统时钟频率 parameter UART_BPS = 20\u0026#39;d921600; //串口波特率 wire flag; wire [7:0] data; // assign led = data[0]; uart_receive #( .SYSTERM_CLK (SYSTERM_CLK), .UART_BPS (UART_BPS) ) u_uart_receive( .clk (clk_pll_o), .rst_n (rst_n), .uart_rx (uart_rx), .receive_done (flag), .uart_data (data) ); uart_send #( .SYSTERM_CLK (SYSTERM_CLK), .UART_BPS (UART_BPS) ) u_uart_send( .clk (clk_pll_o), .rst_n (rst_n), .uart_in_data (data), .uart_in_flag (flag), .uart_tx (uart_tx) ); reg [1:0] led_counter; assign led = led_counter[0]; // parameter SEC_TIME = 32\u0026#39;d48_000_000;//48M reg\t[31:0] cnt; always @ (posedge clk_pll_o or negedge rst_n)begin if (rst_n == 0) cnt = 4\u0026#39;d1)) begin reg_data ![f1992c7fce5ccbde038bca329048345](img-1.png) ","permalink":"https://fan-pengfei.top/posts/ag1280%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e因为上次做的板子是将FPGA和一块MCU芯片集成在一块的，他们之间就需要通信，当然最容易实现的就是UART串口通信；\n下面的代码是一个Demo代码，实现的功能是，RX接收到什么，TX就发送相同的数据；\nFPGA芯片\nAG1280Q48\n时钟频率\n48MHZ\n波特率\n921600\nRX引脚\nPIN_16\nTX引脚\nPIN_14\nLED引脚\nPIN_48\n时钟输入引脚\nPIN_15\n复位引脚\nPIN_17\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e具体代码如下：\u003c/p\u003e\n\u003ch2 id=\"顶层模块\"\u003e顶层模块\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-verilog\" data-lang=\"verilog\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003emodule\u003c/span\u003e connect_mcu (\u003cspan style=\"color:#66d9ef\"\u003einput\u003c/span\u003e clk,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003einput\u003c/span\u003e rst_n,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003einput\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e uart_rx,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eoutput\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e uart_tx,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eoutput\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e led);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e clk_pll_o; \u003cspan style=\"color:#75715e\"\u003e//PLL时钟\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    inpll pll_inst (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkin(clk),\t\t\u003cspan style=\"color:#75715e\"\u003e// PLL.clkin MUST connect to PIN_XX_GB\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkfb(clk_pll_o),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .pllen(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;b1\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .resetn(rst_n),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout0en(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;b1\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout1en(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;b0\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout2en(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;b0\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout3en(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;b0\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout0(clk_pll_o),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout1(),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout2(),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clkout3(),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .lock()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eparameter\u003c/span\u003e  SYSTERM_CLK \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e26\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;d48\u003c/span\u003e_000_000;               \u003cspan style=\"color:#75715e\"\u003e//系统时钟频率\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eparameter\u003c/span\u003e  UART_BPS    \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;d921600\u003c/span\u003e;                   \u003cspan style=\"color:#75715e\"\u003e//串口波特率\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e       flag;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewire\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] data;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// assign led = data[0];\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    uart_receive\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    #(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .SYSTERM_CLK   (SYSTERM_CLK),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .UART_BPS      (UART_BPS)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    u_uart_receive(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clk          (clk_pll_o),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .rst_n        (rst_n),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .uart_rx      (uart_rx),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .receive_done (flag),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .uart_data    (data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    uart_send\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    #(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .SYSTERM_CLK   (SYSTERM_CLK),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .UART_BPS      (UART_BPS)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    u_uart_send(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .clk          (clk_pll_o),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .rst_n        (rst_n),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .uart_in_data (data),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .uart_in_flag (flag),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .uart_tx      (uart_tx)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereg\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] led_counter;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eassign\u003c/span\u003e led \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e led_counter[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// parameter SEC_TIME = 32\u0026#39;d48_000_000;//48M\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereg\u003c/span\u003e\t[\u003cspan style=\"color:#ae81ff\"\u003e31\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] cnt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ealways\u003c/span\u003e @ (\u003cspan style=\"color:#66d9ef\"\u003eposedge\u003c/span\u003e clk_pll_o \u003cspan style=\"color:#66d9ef\"\u003eor\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enegedge\u003c/span\u003e rst_n)\u003cspan style=\"color:#66d9ef\"\u003ebegin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (rst_n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            cnt \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u0026#39;d1\u003c/span\u003e)) \u003cspan style=\"color:#66d9ef\"\u003ebegin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            reg_data\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e[f1992c7fce5ccbde038bca329048345](img\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1.\u003c/span\u003epng)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"AG1280串口通信"},{"content":" 记录AT32开发中遇到的问题；\n硬件部分： 硬件部分我是参考官方开发板来做的；\n经过测试，硬件部分没有任何问题；\n原理图 PCB仿真图 实物图 显示效果 有几个值得关注的地方，我将在下面列出：\n使用编码器开关作为输入设备；\n使用CH340E芯片作为串口转USB；\n通过TYPE-C接口的正反插实现不同的功能（连接CH340E芯片和AT32的USB引脚）：\n屏幕接口采用的是FPC0.5mm接口，方便安装；\n硬件部分的测试很快就结束了，主要就是测试各个外设是否能正常使用；\n经测试，主控芯片、屏幕、spiflash、USB接口、CH340E、编码器开关、RTC时钟均能正常使用；\n软件开发： 参考资料： 主控芯片使用的是雅特力的AT32F403ACGU7，其特性如下所示：\n参考资料网站：https://www.arterytek.com/cn/product/AT32F403A.jsp\n下载固件库源码，经解压后，可以在目录:\n.\\AT32F403A_407_Firmware_Library_V2.1.2\\project\\at_start_f403a\\examples 找到各个外设的例子：\n$ ls acc/ crm/ gpio/ spi/ wwdt/ adc/ dac/ i2c/ sram/ xmc/ bpr/ debug/ i2s/ tmr/ can/ dma/ pwc/ usart/ cortex_m4/ exint/ rtc/ usb_device/ crc/ flash/ sdio/ wdt/ 首先当然是串口打印和点灯，这样能测试时钟配置是否正常，以及判断芯片好坏与否；\n具体开发： 关键代码如下：\n点灯： void at32_led_init(void) { gpio_init_type gpio_init_struct; /* enable the led clock */ crm_periph_clock_enable(USER_LED_GPIO_CRM_CLK, TRUE); /* set default parameter */ gpio_default_para_init(\u0026amp;gpio_init_struct); /* configure the led gpio */ gpio_init_struct.gpio_pins = USER_LED_PIN; gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(USER_LED_GPIO, \u0026amp;gpio_init_struct); } void at32_led_on(void) { USER_LED_GPIO-\u0026gt;clr = USER_LED_PIN; } void at32_led_off(void) { USER_LED_GPIO-\u0026gt;scr = USER_LED_PIN; } void at32_led_toggle(void) { USER_LED_GPIO-\u0026gt;odt ^= USER_LED_PIN; } 串口： void uart_print_init(uint32_t baudrate) { gpio_init_type gpio_init_struct; /* enable the uart and gpio clock */ crm_periph_clock_enable(PRINT_UART_CRM_CLK, TRUE); crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, TRUE); gpio_default_para_init(\u0026amp;gpio_init_struct); /* configure the uart tx pin */ gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_MUX; gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(PRINT_UART_TX_GPIO, \u0026amp;gpio_init_struct); /* configure uart param */ usart_init(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT); usart_transmitter_enable(PRINT_UART, TRUE); usart_enable(PRINT_UART, TRUE); } 串口重定向：\n#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) PUTCHAR_PROTOTYPE { while (usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET) ; usart_data_transmit(PRINT_UART, ch); return ch; } 然后在main函数中分别初始化后即可：\nat32_led_init(); uart_print_init(115200); 通过USB线将板子连接在电脑上，即可通过串口助手收到板子发送的信息，并且板子上的LED开始闪烁；\nLCD屏幕： 然后便是测试LCD屏幕，LCD的接口使用的是SPI串行通信接口，使用AT32的硬件SPI接口，初始方案使用的是中景园的例子，成功点亮，后续为了提高帧率，优化了下，并且使用DMA传输，刷屏速度大大加快；\n下表是引脚配置：\nLCD引脚 AT32引脚\nRST PA8\nDC PB10\nCS PB12\nBLK PA3\nMOSI PB15(SPI2)\nCLK PB13(SPI2)\n下边是屏幕驱动的关键代码：\n/* * @Author : fan-pengfei 2253770787@qq.com * @Date : 2022-10-16 18:55:05 * @LastEditors : fan-pengfei 2253770787@qq.com * @LastEditTime : 2022-10-19 13:09:13 * @FilePath : \\Core\\Src\\lcd.c * @Description : * Copyright (c) 2022 by fan-pengfei 2253770787@qq.com, All Rights Reserved. */ #include \u0026#34;lcd.h\u0026#34; #include \u0026#34;at32f403a_407_board.h\u0026#34; uint16_t BACK_COLOR; //背景色 /** * @brief 设置LCD亮度 * @param BLK_NUM 亮度百分比 * @return 无 */ void LCD_BLK_SET(uint16_t BLK_NUM) { if (BLK_NUM \u0026gt; 100) { BLK_NUM = 100; } tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_4, 10 * BLK_NUM); } static volatile uint8_t dma_trans_done_flag = 1; /** * @brief DMA1 5通道中断 * @return 无 */ void DMA1_Channel5_IRQHandler(void) { if (dma_flag_get(DMA1_FDT5_FLAG) != RESET) { dma_trans_done_flag = 1; dma_reset(DMA1_CHANNEL5); //复位DMA通道 } } /** * @brief LCD控制引脚初始化 * @return 无 */ static void lcd_bus_init() { /* GPIO外设 */ crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //使能GPIOA外设时钟 crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); //使能GPIOA外设时钟 gpio_init_type gpio_initstructure; gpio_default_para_init(\u0026amp;gpio_initstructure); gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_initstructure.gpio_mode = GPIO_MODE_MUX; gpio_initstructure.gpio_pins = GPIO_PINS_13 | GPIO_PINS_15; // PB13=SPI2_CLK PB15=SPI2_MOSI gpio_init(GPIOB, \u0026amp;gpio_initstructure); gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; gpio_initstructure.gpio_pins = GPIO_PINS_12; // PB12=SPI2_CS gpio_init(GPIOB, \u0026amp;gpio_initstructure); gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; gpio_initstructure.gpio_pins = GPIO_PINS_10; // PB10=LCD_DC gpio_init(GPIOB, \u0026amp;gpio_initstructure); gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT; gpio_initstructure.gpio_pins = GPIO_PINS_8; // PA8=LCD_RES gpio_init(GPIOA, \u0026amp;gpio_initstructure); /* SPI外设 */ crm_periph_clock_enable(CRM_SPI2_PERIPH_CLOCK, TRUE); //使能SPI2外设时钟 spi_init_type spi_init_struct; spi_default_para_init(\u0026amp;spi_init_struct); spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX; //仅发送 spi_init_struct.master_slave_mode = SPI_MODE_MASTER; spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2; // 120/2=60Mhz spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; spi_init_struct.frame_bit_num = SPI_FRAME_8BIT; spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH; spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE; spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE; spi_init(SPI2, \u0026amp;spi_init_struct); spi_i2s_dma_transmitter_enable(SPI2, TRUE); //使能SPI发送DMA请求 spi_enable(SPI2, TRUE); /* DMA外设 */ crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE); nvic_irq_enable(DMA1_Channel5_IRQn, 1, 0); //使能DMA中断请求 } /** * @brief LCD 发送命令 * @param cmd 具体命令 * @return 无 */ static void lcd_send_cmd(uint8_t cmd) { OLED_DC_Clr(); // D/C=0, 指令 OLED_CS_Clr(); // CS=0 spi_frame_bit_num_set(SPI2, SPI_FRAME_8BIT); spi_i2s_data_transmit(SPI2, cmd); while (spi_i2s_flag_get(SPI2, SPI_I2S_BF_FLAG) == SET) ; //等待传输完成 OLED_CS_Set(); // CS=1 OLED_DC_Set(); // D/C=1, 数据 } /** * @brief LCD发送数据 * @param *data 数据指针 * @param len 数据长度 * @return 无 */ static void lcd_send_data(const uint8_t *data, uint8_t len) { OLED_CS_Clr(); // CS=0 spi_frame_bit_num_set(SPI2, SPI_FRAME_8BIT); for (uint8_t i = 0; i dt); dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD; dma_init_struct.peripheral_inc_enable = FALSE; dma_init_struct.priority = DMA_PRIORITY_MEDIUM; dma_init_struct.loop_mode_enable = FALSE; dma_init(DMA1_CHANNEL5, \u0026amp;dma_init_struct); dma_interrupt_enable(DMA1_CHANNEL5, DMA_FDT_INT, TRUE); //开启DMA传输完成中断 OLED_CS_Clr(); // CS=0 dma_channel_enable(DMA1_CHANNEL5, TRUE); //启动传输 } /** * @brief LCD初始化 * @return 无 */ static void lcd_reg_init_st7789v() { OLED_RST_Clr(); delay_ms(20); OLED_RST_Set(); delay_ms(20); /* 退出睡眠模式 */ lcd_send_cmd(0x11); /* IPS屏幕需要设置屏幕反显才能显示正确的颜色 */ lcd_send_cmd(0x21); //开启反显 // lcd_send_cmd(0x20); //关闭反显 /* 显存访问控制 */ lcd_send_cmd(0x36); // lcd_send_data((uint8_t[]){0x60}, 1); //MX=MV=1, MY=ML=MH=0, RGB=0 横屏, 芯片在右 // lcd_send_data((uint8_t[]){0xA0}, 1); //MY=MV=1, MX=ML=MH=0, RGB=0 横屏，芯片在左 lcd_send_data((uint8_t[]){0x00}, 1); // MX=MV=MY=ML=MH=0, RGB=0 竖屏，芯片在下 /* 像素格式 */ lcd_send_cmd(0x3A); lcd_send_data((uint8_t[]){0x05}, 1); // MCU mode, 16bit/pixel /* Porch设置 */ lcd_send_cmd(0xB2); lcd_send_data((uint8_t[]){0x0C, 0x0C, 0x00, 0x33, 0x33}, 5); /* Gate设置 */ lcd_send_cmd(0xB7); lcd_send_data((uint8_t[]){0x35}, 1); // Vgh=13.26V, Vgl=-10.43V /* VCOM设置 */ lcd_send_cmd(0xBB); lcd_send_data((uint8_t[]){0x19}, 1); // VCOM=0.725V /* LCN设置 */ lcd_send_cmd(0xC0); lcd_send_data((uint8_t[]){0x2C}, 1); // XOR: BGR, MX, MH /* VDV与VRH写使能 */ lcd_send_cmd(0xC2); lcd_send_data((uint8_t[]){0x01, 0xFF}, 2); // CMDEN=1 /* VRH设置 */ lcd_send_cmd(0xC3); lcd_send_data((uint8_t[]){0x12}, 1); // Vrh=4.45+ /* VDV设置 */ lcd_send_cmd(0xC4); lcd_send_data((uint8_t[]){0x20}, 1); // Vdv=0 /* 刷新率设置 */ lcd_send_cmd(0xC6); lcd_send_data((uint8_t[]){0x0F}, 1); // 60Hz, no column inversion /* 电源控制1 */ lcd_send_cmd(0xD0); lcd_send_data((uint8_t[]){0xA4, 0xA1}, 2); // AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V /* 正电压伽马控制 */ lcd_send_cmd(0xE0); lcd_send_data((uint8_t[]){0xD0, 0x0D, 0x14, 0x0B, 0x0B, 0x07, 0x3A, 0x44, 0x50, 0x08, 0x13, 0x13, 0x2D, 0x32}, 14); /* 负电压伽马控制 */ lcd_send_cmd(0xE1); lcd_send_data((uint8_t[]){0xD0, 0x0D, 0x14, 0x0B, 0x0B, 0x07, 0x3A, 0x44, 0x50, 0x08, 0x13, 0x13, 0x2D, 0x32}, 14); } /** * @brief LCD发送某个区域的颜色数据 * @param x0 横轴起始坐标 * @param y0 纵轴起始坐标 * @param x1 横轴结束坐标 * @param y1 纵轴结束坐标 * @param *pix_data 发送的数据指针 * @return 无 */ void bsp_lcd_draw_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, const uint8_t *pix_data) { uint8_t tx_data[4]; /* 设置Column地址 */ tx_data[0] = x0 \u0026gt;\u0026gt; 8; //起始地址高8位 tx_data[1] = x0 \u0026amp; 0xFF; //起始地址高8位 tx_data[2] = x1 \u0026gt;\u0026gt; 8; //结束地址高8位 tx_data[3] = x1 \u0026amp; 0xFF; //结束地址高8位 lcd_send_cmd(0x2A); lcd_send_data(tx_data, 4); /* 设置Page地址 */ tx_data[0] = y0 \u0026gt;\u0026gt; 8; tx_data[1] = y0 \u0026amp; 0xFF; tx_data[2] = y1 \u0026gt;\u0026gt; 8; tx_data[3] = y1 \u0026amp; 0xFF; lcd_send_cmd(0x2B); lcd_send_data(tx_data, 4); /* 写入图像数据 */ lcd_send_cmd(0x2C); lcd_send_color(pix_data, (x1 - x0 + 1) * (y1 - y0 + 1)); } /** * @brief 等待数据传输完毕 * @return 无 */ void bsp_lcd_draw_rect_wait(void) { while (dma_trans_done_flag == 0) ; //等待传输完毕 dma_trans_done_flag = 0; while (spi_i2s_flag_get(SPI2, SPI_I2S_BF_FLAG) == SET) ; // DMA传输完成中断产生时，SPI的最后一个字节仍在传输中，不能提前释放CS OLED_CS_Set(); // CS=1 } /** * @brief 控制LCD开关 * @param status 1-\u0026gt;开 0-\u0026gt;关 * @return 无 */ void bsp_lcd_display_switch(uint8_t status) { if (status) { lcd_send_cmd(0x29); //开显示 } else { lcd_send_cmd(0x28); //关显示 } } /** * @brief LCD初始化 * @return 无 */ void bsp_lcd_init(void) { lcd_bus_init(); lcd_reg_init_st7789v(); } //下面是头文件 #ifndef __LCD_H #define __LCD_H #include \u0026#34;at32f403a_407_board.h\u0026#34; #include \u0026#34;at32f403a_407_clock.h\u0026#34; #include \u0026#34;stdlib.h\u0026#34; #define OLED_RST_Clr() gpio_bits_reset(GPIOA, GPIO_PINS_8) // RES #define OLED_RST_Set() gpio_bits_set(GPIOA, GPIO_PINS_8) #define OLED_DC_Clr() gpio_bits_reset(GPIOB, GPIO_PINS_10) // DC #define OLED_DC_Set() gpio_bits_set(GPIOB, GPIO_PINS_10) #define OLED_CS_Clr() gpio_bits_reset(GPIOB, GPIO_PINS_12) // CS #define OLED_CS_Set() gpio_bits_set(GPIOB, GPIO_PINS_12) #define OLED_BLK_Clr() gpio_bits_reset(GPIOA, GPIO_PINS_3) // BLK #define OLED_BLK_Set() gpio_bits_set(GPIOA, GPIO_PINS_3) /* 屏幕分辨率 */ #define BSP_LCD_X_PIXELS 240 #define BSP_LCD_Y_PIXELS 240 void bsp_lcd_init(void); void bsp_lcd_display_switch(uint8_t status); void bsp_lcd_draw_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, const uint8_t *dat); void bsp_lcd_draw_rect_wait(void); void LCD_BLK_SET(uint16_t BLK_NUM); #endif 编码器开关： 接下来是测试编码器开关，编码器型号为：SIQ-02FVS3，\n根据上述原理图可以发现这其实跟光电编码器很相似；\n因而我使用外部中断来检测编码器的转动方向和转动角度以及按键状态：\nstatic uint8_t flag_exit = 0; static uint8_t int_nu = 0; int32_t cnt_encode = 0; void EXINT0_IRQHandler(void) { int i = 0; if (exint_flag_get(EXINT_LINE_0) != RESET) { i = 2000; while (i--) ; if (int_nu == 0 \u0026amp;\u0026amp; gpio_input_data_bit_read(GPIOB, GPIO_PINS_0) == 0) //第一次中断，并且A相是下降沿 { flag_exit = 0; if (gpio_input_data_bit_read(GPIOB, GPIO_PINS_1)) flag_exit = 1; //根据B相，设定标志 int_nu = 1; //中断计数 } if (int_nu \u0026amp;\u0026amp; gpio_input_data_bit_read(GPIOB, GPIO_PINS_0) == 1) //第二次中断，并且A相是上升沿 { if (gpio_input_data_bit_read(GPIOB, GPIO_PINS_1) == 0 \u0026amp;\u0026amp; flag_exit == 1) { cnt_encode--; } if (gpio_input_data_bit_read(GPIOB, GPIO_PINS_1) \u0026amp;\u0026amp; flag_exit == 0) { cnt_encode++; } int_nu = 0; //中断计数复位，准备下一次 } exint_flag_clear(EXINT_LINE_0); } } cnt_encode中存放旋转次数（24°为1单位），至于按键的按动状态检测则放在了1ms的定时器中；\n经测试，该检测算法可以很好的消除拨码开关转动时的抖动，最后得到的结果是比较准确的；\n中间件移植： 外设都测试完毕，接下来就是各种中间件的移植：LVGL移植、FATFS移植、USB Device等；\nLVGL-8.3移植： 文件夹创建： 下载以后打开文件可以看见下面四个文件：\n第一步： 在stm32工程下面创建一个文件夹，名称为lvgl；\n将上图中的四个文件加入到lvgl文件夹中；\n把lv_conf_template.h文件重命名为lv_conf.h；\n第二步： 在lvgl/examples/porting文件夹中把三个.h文件开头的#if 0改为#if 1；\n打开你原本的stm32工程（就是那个正常驱动屏幕的工程），使能c99；\n第三步： 在左侧栏中添加两个文件夹\n添加两个port文件，位于lvgl/examples/porting，因为没有用到文件系统，故没有添加fs：\n添加lvgl源文件，将lvgl/src目录下的core draw font hal misc widgets文件夹下的所有文件全部添加进lvgl组\n注：文件夹中还有子文件夹要一层一层打开并加入；\n将lvgl/src/extra/目录下的文件添加进lvgl组。具体为：\nlayouts目录下所有子目录文件； themes目录下所有子目录文件； widgets目录下所有子目录文件； lv_extra.c；\n加入路径，将lvgl ，src，porting文件夹添加到源路径下。\n修改配置文件： 修改lv_port_disp_template.h：\n修改lv_port_disp_template.c： 将开头的#if 0改成#if 1\n修改lv_conf.h：\n这两句是修改你屏幕大小的，直接粘贴在那里就可以了。\n#define MY_DISP_HOR_RES 240 #define MY_DISP_VER_RES 240 修改显示的必要文件： 在lv_port_disp_template.c中包含着显示的重要文件，这里我们只需要修改两处：\n第一处： 把框选的地方注释掉，\n去除warning； 此时编译应该没有error，但是有warning这个warning无伤大雅可以直接屏蔽；\n--diag_suppress=188,546,68,111 当完成这一步的时候就已经说明你已经把显示的移植了99%了，可以编译看一下是否有报错；\n第二处： 这是刷新函数，用于图形填充的，这里介绍两种方法，第一种是不断画点，只需要把红色部分写成你原来工程中的画点的函数就可以了，如果你的画点函数传入的形参中颜色的定义并不是指针的方式的话，一定要写成图中color_p-\u0026gt;full的形式，不然会报错的。\n不要把#include“lcd.h”遗忘\n第二种方法是通过区域填充来实现的如果你是使用中景园的代码，你直接调用他的区域填充函数基本上会花屏，所以需要对他的函数进行一下改良。 可在你本来工程下面的lcd.c中添加一段代码：\nvoid lvgl_LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, lv_color_t *color) { uint32_t y=0; u16 height, width; width = ex - sx + 1; //得到填充的宽度 height = ey - sy + 1; //高度 LCD_SetWindows(sx,sy,ex,ey); for(y = 0; y full); color++; } } 也许他会报错说找不到lv_color_t这是以为我们没有在文件开头添加#include \u0026quot;lvgl.h\u0026quot;添加以后还需要在lcd.h文件中进行声明，不出意外还会报错，需要你在文件开头加入#include \u0026quot;lvgl.h\u0026quot;，我们就可以把第一种的描点换成画图：\n显示测试： 在main.c中``#include \u0026ldquo;lvgl.h\u0026rdquo; #include \u0026ldquo;lv_port_disp_template.h\u0026rdquo;` 随后在屏幕初始化后面加上代码：\nlv_init(); lv_port_disp_init(); 还需要一个在嘀嗒定时器里加一个跳动的“心脏”进行刷新；\nlv_tick_inc(1);//在中断里面进行刷新； 如果这个对你而言比较难实现你也可以把这个放到主函数的循环里面；\n并在主函数中创建一个测试函数：\nvoid lv_ex_label(void) { char* github_addr = \u0026#34;https://gitee.com/W23\u0026#34;; lv_obj_t * label = lv_label_create(lv_scr_act()); lv_label_set_recolor(label, true); lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/ lv_obj_set_width(label, 120); lv_label_set_text_fmt(label, \u0026#34;#ff0000 Gitee: %s#\u0026#34;, github_addr); lv_obj_align(label, LV_ALIGN_CENTER, 0, 10); lv_obj_t * label2 = lv_label_create(lv_scr_act()); lv_label_set_recolor(label2, true); lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/ lv_obj_set_width(label2, 120); lv_label_set_text_fmt(label2, \u0026#34;#ff0000 Hello# #0000ff world !123456789#\u0026#34;); lv_obj_align(label2, LV_ALIGN_CENTER, 0, -10); } 将测试函数写在lv_port_disp_init();的后面\nLCD_Init(); lv_init(); lv_port_disp_init(); lv_ex_label(); 在主函数中写：\nlv_task_handler(); delay_ms(10); lv_port_disp.c示例代码： #include \u0026#34;lv_port_disp.h\u0026#34; #include #include \u0026#34;lcd.h\u0026#34; #define MY_DISP_HOR_RES 240 #define MY_DISP_VER_RES 240 #define LVGL_BUFF_SIZE (BSP_LCD_X_PIXELS * BSP_LCD_Y_PIXELS / 4) // 1/6屏幕分辨率 static lv_color_t lvgl_draw_buff1[LVGL_BUFF_SIZE]; static lv_color_t lvgl_draw_buff2[LVGL_BUFF_SIZE]; static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); void lv_port_disp_init(void) { /* 向lvgl注册缓冲区 */ static lv_disp_draw_buf_t draw_buf_dsc; //需要全程生命周期，设置为静态变量 lv_disp_draw_buf_init(\u0026amp;draw_buf_dsc, lvgl_draw_buff1, lvgl_draw_buff2, sizeof(lvgl_draw_buff1) / sizeof(lv_color_t)); /* 创建并初始化用于在lvgl中注册显示设备的结构 */ static lv_disp_drv_t disp_drv; lv_disp_drv_init(\u0026amp;disp_drv); //使用默认值初始化该结构 /* 设置屏幕分辨率 */ disp_drv.hor_res = BSP_LCD_X_PIXELS; disp_drv.ver_res = BSP_LCD_Y_PIXELS; /* 初始化LCD总线 */ bsp_lcd_init(); /* 设置显示矩形函数，用于将矩形缓冲区刷新到屏幕上 */ disp_drv.flush_cb = disp_flush; /* 设置缓冲区 */ disp_drv.draw_buf = \u0026amp;draw_buf_dsc; /* 注册显示设备 */ lv_disp_drv_register(\u0026amp;disp_drv); /* 开启显示 */ bsp_lcd_display_switch(1); } static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { /* 等待上次传输完成 */ bsp_lcd_draw_rect_wait(); /* 通知lvgl传输已完成 */ lv_disp_flush_ready(disp_drv); /* 启动新的传输 */ bsp_lcd_draw_rect(area-\u0026gt;x1, area-\u0026gt;y1, area-\u0026gt;x2, area-\u0026gt;y2, (const uint8_t *)color_p); } 这里的数据传输部分的代码直接使用的是DMA传输，在该分辨率下，lvgl的benchmark加权平均帧率甚至能达到200FPS；\n","permalink":"https://fan-pengfei.top/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/","summary":"\u003cblockquote\u003e\n\u003cp\u003e记录AT32开发中遇到的问题；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"硬件部分\"\u003e硬件部分：\u003c/h2\u003e\n\u003cp\u003e硬件部分我是参考官方开发板来做的；\u003c/p\u003e\n\u003cp\u003e经过测试，硬件部分没有任何问题；\u003c/p\u003e\n\u003cp\u003e原理图\n\u003cimg alt=\"2e8cc9324c8e969b2ba048221939699\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003ePCB仿真图\n\u003cimg alt=\"f12b1584db83aef4fbd8cc925b76d40\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e实物图\n\u003cimg alt=\"bbea8d18667d74360d3ef9c638453d5\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e显示效果\n\u003cimg alt=\"dcf5793c87931e4e32513eca0f71157\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e有几个值得关注的地方，我将在下面列出：\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e使用编码器开关作为输入设备；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e使用CH340E芯片作为串口转USB；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e通过TYPE-C接口的正反插实现不同的功能（连接CH340E芯片和AT32的USB引脚）：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e屏幕接口采用的是FPC0.5mm接口，方便安装；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e硬件部分的测试很快就结束了，主要就是测试各个外设是否能正常使用；\u003c/p\u003e\n\u003cp\u003e经测试，主控芯片、屏幕、spiflash、USB接口、CH340E、编码器开关、RTC时钟均能正常使用；\u003c/p\u003e\n\u003ch2 id=\"软件开发\"\u003e软件开发：\u003c/h2\u003e\n\u003ch3 id=\"参考资料\"\u003e参考资料：\u003c/h3\u003e\n\u003cp\u003e主控芯片使用的是雅特力的AT32F403ACGU7，其特性如下所示：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"f9ed3e62565a45863771434081c095d\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e参考资料网站：\u003ca href=\"https://www.arterytek.com/cn/product/AT32F403A.jsp\"\u003ehttps://www.arterytek.com/cn/product/AT32F403A.jsp\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"ae1d18fa022ab02fffa6a006428d441\" loading=\"lazy\" src=\"/posts/at32f403a%E7%B3%BB%E5%88%97%E5%8D%95%E7%89%87%E6%9C%BA%E5%BC%80%E5%8F%91/img-6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e下载固件库源码，经解压后，可以在目录:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#ae81ff\"\u003e\\A\u003c/span\u003eT32F403A_407_Firmware_Library_V2.1.2\u003cspan style=\"color:#ae81ff\"\u003e\\p\u003c/span\u003eroject\u003cspan style=\"color:#ae81ff\"\u003e\\a\u003c/span\u003et_start_f403a\u003cspan style=\"color:#ae81ff\"\u003e\\e\u003c/span\u003examples\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e找到各个外设的例子：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ ls\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eacc/        crm/    gpio/  spi/         wwdt/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadc/        dac/    i2c/   sram/        xmc/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebpr/        debug/  i2s/   tmr/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecan/        dma/    pwc/   usart/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecortex_m4/  exint/  rtc/   usb_device/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecrc/        flash/  sdio/  wdt/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e首先当然是串口打印和点灯，这样能测试时钟配置是否正常，以及判断芯片好坏与否；\u003c/p\u003e\n\u003ch3 id=\"具体开发\"\u003e具体开发：\u003c/h3\u003e\n\u003cp\u003e关键代码如下：\u003c/p\u003e\n\u003ch4 id=\"点灯\"\u003e点灯：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eat32_led_init\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_type gpio_init_struct;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* enable the led clock */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecrm_periph_clock_enable\u003c/span\u003e(USER_LED_GPIO_CRM_CLK, TRUE);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* set default parameter */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003egpio_default_para_init\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003egpio_init_struct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* configure the led gpio */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_pins \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e USER_LED_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_mode \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_MODE_OUTPUT;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_out_type \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_OUTPUT_PUSH_PULL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_pull \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_PULL_NONE;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_drive_strength \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_DRIVE_STRENGTH_STRONGER;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003egpio_init\u003c/span\u003e(USER_LED_GPIO, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003egpio_init_struct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eat32_led_on\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    USER_LED_GPIO\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003eclr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e USER_LED_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eat32_led_off\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    USER_LED_GPIO\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003escr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e USER_LED_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eat32_led_toggle\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    USER_LED_GPIO\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003eodt \u003cspan style=\"color:#f92672\"\u003e^=\u003c/span\u003e USER_LED_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"串口\"\u003e串口：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003euart_print_init\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e baudrate)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_type gpio_init_struct;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* enable the uart and gpio clock */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecrm_periph_clock_enable\u003c/span\u003e(PRINT_UART_CRM_CLK, TRUE);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecrm_periph_clock_enable\u003c/span\u003e(PRINT_UART_TX_GPIO_CRM_CLK, TRUE);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003egpio_default_para_init\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003egpio_init_struct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* configure the uart tx pin */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_drive_strength \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_DRIVE_STRENGTH_STRONGER;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_out_type \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_OUTPUT_PUSH_PULL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_mode \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_MODE_MUX;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_pins \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e PRINT_UART_TX_PIN;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gpio_init_struct.gpio_pull \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e GPIO_PULL_NONE;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003egpio_init\u003c/span\u003e(PRINT_UART_TX_GPIO, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003egpio_init_struct);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/* configure uart param */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eusart_init\u003c/span\u003e(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eusart_transmitter_enable\u003c/span\u003e(PRINT_UART, TRUE);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eusart_enable\u003c/span\u003e(PRINT_UART, TRUE);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e串口重定向\u003c/strong\u003e：\u003c/p\u003e","title":"AT32F403A系列单片机开发"},{"content":" 打算做一个适配1.33寸屏幕和2.0寸屏幕的底板，屏幕接口留出来两种，FPC排线连接，八位并行接口和SPI接口；\n这个可能要搁置一段时间了，因为目前只做了集成MCU和屏幕的板子；\n","permalink":"https://fan-pengfei.top/posts/lcd%E5%BA%95%E6%9D%BF%E8%AE%BE%E8%AE%A1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e打算做一个适配1.33寸屏幕和2.0寸屏幕的底板，屏幕接口留出来两种，FPC排线连接，八位并行接口和SPI接口；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e这个可能要搁置一段时间了，因为目前只做了集成MCU和屏幕的板子；\u003c/p\u003e","title":"LCD底板设计"},{"content":" 如何修改 Typora 「高亮」的颜色；\nTypora 有一个「高亮」的格式（示例：==例子==），类似于荧光笔，但是感觉默认的颜色偏亮，看久了不舒服，所以利用修改主题文件的方式来自定义颜色。\n操作很简单，先找到主题文件：「文件」 ==\u0026gt; 「偏好设置」（或者直接 Ctrl + 逗号），在右边「外观」栏中找到「打开主题文件」打开：\n打开主题对应的 .css 文件，在最后面加上下面的文字：\nmark { background: #a9d18e; border-bottom: 0px solid #ffffff; padding: 0.0px; margin: 0 0px; } 如果只是想要单纯改变颜色，也可以只写 background的这一行：\nmark { background: #a9d18e; } 其中 backgraoud后面的十六进制数为所需要的颜色。border-bottom 是下划线的大小和颜色。padding就是上下左右的边框大小。margin就是所标记文字离左右文字的距离。\n最后，Typora 中「高亮」没有快捷键，但是可以自定义。Ctrl+逗号 打开「偏好设置」，在「通用」里最下面打开高级设置，找到下图位置，添加自己需要的快捷键：\n// Custom key binding, which will override the default ones. \u0026#34;keyBinding\u0026#34;: { // for example: // \u0026#34;Always on Top\u0026#34;: \u0026#34;Ctrl+Shift+P\u0026#34; }, 修改为：\n// Custom key binding, which will override the default ones. \u0026#34;keyBinding\u0026#34;: { // for example: // \u0026#34;Always on Top\u0026#34;: \u0026#34;Ctrl+Shift+P\u0026#34; \u0026#34;Highlight\u0026#34;:\u0026#34;Ctrl+Shift+H\u0026#34; }, 最后的效果如下：\n","permalink":"https://fan-pengfei.top/posts/typora%E4%BF%AE%E6%94%B9%E9%AB%98%E4%BA%AE%E9%A2%9C%E8%89%B2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e如何修改 Typora 「高亮」的颜色；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eTypora 有一个「高亮」的格式（示例：\u003ccode\u003e==例子==\u003c/code\u003e），类似于荧光笔，但是感觉默认的颜色偏亮，看久了不舒服，所以利用修改主题文件的方式来自定义颜色。\u003c/p\u003e\n\u003cp\u003e操作很简单，先找到主题文件：「文件」 ==\u0026gt; 「偏好设置」（或者直接 Ctrl + 逗号），在右边「外观」栏中找到「打开主题文件」打开：\u003c/p\u003e\n\u003cp\u003e打开主题对应的 \u003ccode\u003e.css\u003c/code\u003e 文件，在最后面加上下面的文字：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003emark\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#a9d18e\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eborder-bottom\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003esolid\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e#ffffff\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003epadding\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如果只是想要单纯改变颜色，也可以只写 \u003ccode\u003ebackground\u003c/code\u003e的这一行：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003emark\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebackground\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#a9d18e\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中 \u003ccode\u003ebackgraoud\u003c/code\u003e后面的十六进制数为所需要的颜色。\u003ccode\u003eborder-bottom\u003c/code\u003e 是下划线的大小和颜色。\u003ccode\u003epadding\u003c/code\u003e就是上下左右的边框大小。\u003ccode\u003emargin\u003c/code\u003e就是所标记文字离左右文字的距离。\u003c/p\u003e\n\u003cp\u003e最后，Typora 中「高亮」没有快捷键，但是可以自定义。Ctrl+逗号 打开「偏好设置」，在「通用」里最下面打开高级设置，找到下图位置，添加自己需要的快捷键：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Custom key binding, which will override the default ones.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyBinding\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// for example:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// \u0026#34;Always on Top\u0026#34;: \u0026#34;Ctrl+Shift+P\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e修改为：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Custom key binding, which will override the default ones.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyBinding\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// for example:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// \u0026#34;Always on Top\u0026#34;: \u0026#34;Ctrl+Shift+P\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;Highlight\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Ctrl+Shift+H\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e最后的效果如下：\u003c/p\u003e","title":"Typora修改高亮颜色"},{"content":" AG1280是一款国产的CPLD芯片，我准备将这个芯片和MCU配合起来，来做一些定制化的接口和功能，例如扩展UART接口，外接一些高速的AD/DA芯片；\n芯片资源： AG1280资源：\nLUTs 1280\nDistributed RAM (Kbits) 10\nEBR SRAM (Kbits) 68\nMaximum User I/O pins 40\nNumber of PLLs 1\nPackage 48-Pin QFN\n价格很便宜，每颗单价大概是7元；\n我画了一个评估板，除了这颗CPLD芯片外，还加了一颗STM32F103C6T6芯片，他们之间有六个IO口互相连接来进行通信或者时钟输入；\nSTM32F103C6T6资源：\n封装 / 箱体 LQFP-48\n核心 ARM Cortex M3\n程序存储器大小 32 kB\n数据总线宽度 32 bit\nADC分辨率 12 bit\n最大时钟频率 72 MHz\n输入/输出端数量 48 I/O\n数据 RAM 大小 10 kB\n评估板设计： 板子原理图如下：\nPCB视图如下： 硬件设计注意： AG1280硬件设计中有几个需要特别注意的点：\n1、IO_GLOBE_S1(位于第9脚)、IO_GLOBE_S2(位于第13脚)、IO_GLOBE_S3(位于第15脚)、IO_GLOBE_S4(位于第19脚)、IO_GLOBE_N1(位于第41脚) 、IO_GLOBE_N2(位于第44脚) 、IO_GLOBE_N3(位于第46脚)可以作为全局时钟输入管脚，可用于输入全局时钟。但若要使用PLL，则只能从13、15和19管脚输入。\n2、电路板载一个24MHz有源晶振，另外还可以通过PMOD接口从STM32的MCO时钟输出管脚获得时钟，它们被连接到具有PLL输入功能的管脚13、15上。\n3、AG1280的GPIO分为North和South两组，可以使用不同IO电平，以实现不同电平逻辑的转换。另外AG1280还需要3.3V电源作为片上Flash电源，且该电源域North组的IO电源共用，因此North组也只能使用3.3V的IO电源电压。South组却可以任选电源电压。\n4、AG1280还需要1.2V内核电源电压，且该电源应略迟于Flash电源上电，以方便Flash加载程序。我的图2电路通过PMOD接口从STM32开发板获得3.3V电源，再用LDO芯片XC6206P122MR从3.3V向下稳压到1.2V内核电源，LDO后带有100uF电容,1.2V上电时间自然要落后于3.3V上电。\n板子已经发出去打样了，估计今天就能到，我到时候焊接测试下；\n软件： 环境配置： AG1280的开发EDA软件Supera还不具备分析和综合电路的能力，但能实现其特有的PLL和片上RAM的IP核打包、综合后的布局布线、下载文件打包及下载等功能。\n到百度网盘http://pan.baidu.com/s/1eQxc6XG 提取密码:q59e下载AGM公司EDA开发软件Supra（网盘上有多个版本的Supra，选择需要的一种即可）。Supra无需安装，下载后将其放置在不含中文的路径下，直接运行Bin目录下的Supra.exe即可。\n目前版本的Supra还无法进行硬件描述语言及原理框图的开发和电路综合，用户只能在Supra下创建工程并完成AGM公司特有IP（包括PLL和RAM）的配置，再通过Supra创建Quartus-II工程文件，在转到Quartus-II下完成硬件描述语言和原理框图开发和电路综合，最后再回到Supra中完成器件内部的布局布线、下载文件的打包和器件烧写（具体流程在后续会详细介绍）。\n综上，进行AG1280的开发一定需要安装一个顺手的Quartus-II。这里特别提醒网友注意，**Supra只支持Quartus-II 13.0以上，且不支持Web与Lite版本，必须安装Full或Standard版本（本人掉到过坑里，因此特别提醒大家注意）。**至于Quartus-II的安装方法，网上资料较多，这里不再赘述。\nAG1280可以使用Intel的USB-Blaster进行下载和软件调试，但淘宝网上USB-Blaster版本较多，价格差异较大。据网传，有的版本USB-Blaster不支持AG1280的Flash下载，大家可自行注意避坑。\n开发流程： 具体操作参考：https://www.cnblogs.com/helesheng/p/16692320.html\n在 Supra 中新建工程 运行 Supra，选择 File - Project - New Project：\n新建一个叫做 blinky1280 的工程，按图填写好工程目录和工程名称，点击 Save。\n在 Supra 中使用 PLL 由于我们需要使用内部的 PLL，所以需要先创建 IP。 在 Supra 中创建 IP 的步骤如下，选择 Tools - Create IP - Create PLL :\n分别配置模块名称、输入频率、PLL类型、反馈模式、输出时钟路数、输出频率后，单击Generate按钮产生IP和顶层封装HDL文件（模块名称.v和模块名称.ip）。\n其中值得注意的是：AG1280有两种时钟源模式：片内RC振荡器模式和片外有源振荡器模式。可以在反馈模式（Feedback mode）选项中选择EXT_FEEDBACK，以选择外部有源振荡器模式；选择NO_REFERENCE，以选择片上RC振荡器模式。片上RC振荡器振荡频率不会太准确，供不需要精确定时的系统使用。若选择片上振荡器则应在输入频率（Input frequency）处输入8MHz，否则真实输出频率将与你输入的频率成比例变化。 另外，Supra中AG1280的PLL配置中的PLL类型（PLL type），只能选择PLLX。而输出路数（PLL output count），相位移动（Phase shift）等配置参照字面意思理解即可。\n下面会打印出实际的频率：\n然后在工程目录下会生成 inpll.ip 和 inpll.v 两个文件，前者需要在编译时在 Supra 中引入，后者则是点灯代码中需要例化的 PLL 模块原型。\n建立 Quartus II 工程并完成逻辑综合 点击 Tools - Migrate，按照图中填写工程细节：\n点击 Next 后，运行 Quartus II，打开工程（目录下的 blinky1280.qpf），该工程中自动包含了一个 blinky1280.v。 打开该文件，并编写点灯代码：\nmodule blinky1280 ( input wire clk, input wire rst_n, output wire led, output [1:0] gpio ); wire clk_pll_o; inpll pll_inst ( .clkin(clk),\t// PLL.clkin MUST connect to PIN_XX_GB .clkfb(clk_pll_o), .pllen(1\u0026#39;b1), .resetn(rst_n), .clkout0en(1\u0026#39;b1), .clkout1en(1\u0026#39;b0), .clkout2en(1\u0026#39;b0), .clkout3en(1\u0026#39;b0), .clkout0(clk_pll_o), .clkout1(), .clkout2(), .clkout3(), .lock() ); reg [1:0] led_counter; reg [1:0] gpio_counter; assign led = led_counter[0]; assign gpio = gpio_counter[0]; // parameter SEC_TIME = 32\u0026#39;d24_000_000;//24M reg\t[31:0] cnt; always @ (posedge clk or negedge rst_n)begin if(rst_n==0) cnt ![image-20221025192914526](img-9.png) 选择 Quartus II 中的 Tools - Tcl Scripts，选择目录下的 af_quartus.tcl 并运行： ![image-20221025192948154](img-10.png) ![image-20221025193004741](img-11.png) ##### Place \u0026amp; Route \u0026gt; 以后修改原设计， Quartus 里只需执行正常的编译（ Start Compilation），不用再运行af_quartus.tcl 文件。然后在 Supra 中运行 Compile，完成编译即可； 当 Quartus II 完成逻辑综合后，回到 Supra，点击 Next： ![930878e08678fe2c2c9d31c7260fca4](img-12.png) 在这里先不要点 Finish，先打开工程目录下的 blinky1280.asf 分配引脚： ```verilog set_location_assignment PIN_15 -to clk set_location_assignment PIN_48 -to led set_location_assignment PIN_17 -to rst_n set_location_assignment PIN_1 -to gpio[0] set_global_assignment -name RESERVE_ALL_UNUSED_PINS \u0026#34;AS INPUT TRI-STATED\u0026#34; 保存，其他参数默认就行，点击 Finish，Supra 就会开始 Place \u0026amp; Route。 一切顺利会输出这样：\n如果不顺利，那就自己多试试，解决不了建议换回 Altera。\n烧写 准备一个 USB Blaster，我试过淘宝十几块的和正点原子的，都可以用，区别是便宜的慢。 先将 USB Blaster 按照引脚定义对应连接到板子上，然后板子上电，将 USB Blaster 接入电脑。 在 Supra 中打开 Tool - Program，点击 Query Device ID； AG1280Q48 的 Device ID 是 0x00120010，如果正确连接会读到这个数值。 选择刚刚生成的烧录文件，其中：\nblinky1280_sram.prg 是烧写到内部 SRAM 的，掉电后丢失\n需要掉电保存的话选择 blinky1280_hybird.prg\n这里以 SRAM 为例。烧写成功如下图，这时板子上的灯应该就会闪了。\n用示波器测量PIN_1引脚，能看到方波输出：\n自己的想法： 如果是小规模FPGA（几个K的LUT），一般是用来做胶合逻辑（glue logic，将不同数字电路拼接起来，涉及到协议转换或简单计算）或是简单的数字信号处理（例如语音唤醒等传感器相关处理）会多一些；\n我感觉还是这种MCU+FPGA/CPLD的方案是很有用的，比如有时候MCU的UART接口不够用，就需要外扩，一般是购买专用的芯片，但是如果有了FPGA，就可以自己写一个逻辑用来外扩UART接口，而且通用性高，成本也很低；\n等板子测试过了再看具体要做什么应用吧；\n","permalink":"https://fan-pengfei.top/posts/ag1280%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003eAG1280是一款国产的CPLD芯片，我准备将这个芯片和MCU配合起来，来做一些定制化的接口和功能，例如扩展UART接口，外接一些高速的AD/DA芯片；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"芯片资源\"\u003e芯片资源：\u003c/h3\u003e\n\u003cp\u003eAG1280资源：\u003c/p\u003e\n\u003cp\u003eLUTs\n1280\u003c/p\u003e\n\u003cp\u003eDistributed RAM (Kbits)\n10\u003c/p\u003e\n\u003cp\u003eEBR SRAM (Kbits)\n68\u003c/p\u003e\n\u003cp\u003eMaximum User I/O pins\n40\u003c/p\u003e\n\u003cp\u003eNumber of PLLs\n1\u003c/p\u003e\n\u003cp\u003ePackage\n48-Pin QFN\u003c/p\u003e\n\u003cp\u003e价格很便宜，每颗单价大概是7元；\u003c/p\u003e\n\u003cp\u003e我画了一个评估板，除了这颗CPLD芯片外，还加了一颗STM32F103C6T6芯片，他们之间有六个IO口互相连接来进行通信或者时钟输入；\u003c/p\u003e\n\u003cp\u003eSTM32F103C6T6资源：\u003c/p\u003e\n\u003cp\u003e封装 / 箱体\nLQFP-48\u003c/p\u003e\n\u003cp\u003e核心\nARM Cortex M3\u003c/p\u003e\n\u003cp\u003e程序存储器大小\n32 kB\u003c/p\u003e\n\u003cp\u003e数据总线宽度\n32 bit\u003c/p\u003e\n\u003cp\u003eADC分辨率\n12 bit\u003c/p\u003e\n\u003cp\u003e最大时钟频率\n72 MHz\u003c/p\u003e\n\u003cp\u003e输入/输出端数量\n48 I/O\u003c/p\u003e\n\u003cp\u003e数据 RAM 大小\n10 kB\u003c/p\u003e\n\u003ch3 id=\"评估板设计\"\u003e评估板设计：\u003c/h3\u003e\n\u003cp\u003e板子原理图如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"b1d8d200e99d2ad024b82831d823bf7\" loading=\"lazy\" src=\"/posts/ag1280%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003ePCB视图如下：\n\u003cimg alt=\"d85be4f1a50fb9dc67385b0af4d328f\" loading=\"lazy\" src=\"/posts/ag1280%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"硬件设计注意\"\u003e硬件设计注意：\u003c/h3\u003e\n\u003cp\u003eAG1280硬件设计中有几个需要特别注意的点：\u003c/p\u003e\n\u003cp\u003e1、IO_GLOBE_S1(位于第9脚)、IO_GLOBE_S2(位于第13脚)、IO_GLOBE_S3(位于第15脚)、IO_GLOBE_S4(位于第19脚)、IO_GLOBE_N1(位于第41脚) 、IO_GLOBE_N2(位于第44脚) 、IO_GLOBE_N3(位于第46脚)可以作为全局时钟输入管脚，可用于输入全局时钟。\u003cstrong\u003e但若要使用PLL，则只能从13、15和19管脚输入。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e2、电路板载一个24MHz有源晶振，另外还可以通过PMOD接口从STM32的MCO时钟输出管脚获得时钟，它们被连接到具有PLL输入功能的管脚13、15上。\u003c/p\u003e\n\u003cp\u003e3、AG1280的GPIO分为North和South两组，可以使用不同IO电平，以实现不同电平逻辑的转换。另外AG1280还需要3.3V电源作为片上Flash电源，且该电源域North组的IO电源共用，因此\u003cstrong\u003eNorth组也只能使用3.3V的IO电源电压。South组却可以任选电源电压\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e4、\u003cstrong\u003eAG1280还需要1.2V内核电源电压，且该电源应略迟于Flash电源上电，以方便Flash加载程序\u003c/strong\u003e。我的图2电路通过PMOD接口从STM32开发板获得3.3V电源，再用LDO芯片XC6206P122MR从3.3V向下稳压到1.2V内核电源，LDO后带有100uF电容,1.2V上电时间自然要落后于3.3V上电。\u003c/p\u003e\n\u003cp\u003e板子已经发出去打样了，估计今天就能到，我到时候焊接测试下；\u003c/p\u003e\n\u003ch3 id=\"软件\"\u003e软件：\u003c/h3\u003e\n\u003ch4 id=\"环境配置\"\u003e环境配置：\u003c/h4\u003e\n\u003cp\u003eAG1280的开发EDA软件Supera还不具备分析和综合电路的能力，但能实现其特有的PLL和片上RAM的IP核打包、综合后的布局布线、下载文件打包及下载等功能。\u003c/p\u003e","title":"AG1280开发记录"},{"content":" Uboot和系统跑起来了，接下来就是进行驱动开发了；\nHeartbeat(心跳灯) 修改设备树： 在设备树文件arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts中；\n在 / { } 所包裹的根节点目录下添加：\nleds { compatible = \u0026#34;gpio-leds\u0026#34;; user_led { label = \u0026#34;led:usr\u0026#34;; gpios = ; /* PE12 */ }; }; 其中\ngpios = ; /* PE12 */ 代表引脚 4 * 32 + 12也就是PE12（ A~G： 0~6） 其名字为：led:usr；\n系统启动后与LED控制有关的文件；\n系统启动后，将看到这样的文件：\n$: ls /sys/class/leds/ led:usr 这里三个文件夹分别对应设备树中定义的三个LED；\n编译设备树： 可以只编译设备树，不编译其他文件；\nmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs -j12 控制LED灯亮灭： 点亮LED：\n$: echo 1 \u0026gt; /sys/class/leds/led\\:usr/brightness 熄灭LED：\n$: echo 0 \u0026gt; /sys/class/leds/led\\:usr/brightness 控制LED闪烁：\n$: ls /sys/class/leds/led\\:usr/ brightness invert power trigger device max_brightness subsystem uevent $: cat /sys/class/leds/led\\:usr/trigger none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 [heartbeat] default-on #这里可以看到当前的值为none，表示没有trigger，将其值改成heartbeat就可以看到闪烁了 $: echo heartbeat \u0026gt; /sys/class/leds/led\\:usr/trigger 开机自动打开心跳灯： 作为系统服务自动启动，在这个目录添加脚本文件 /etc/init.d/； 作为登录用户的自动启动程序，在 /etc/profile.d/ 添加脚本文件；\n新建脚本文件heartbeat_led.sh：\necho heartbeat \u0026gt;/sys/class/leds/led\\:usr/trigger 将该脚本放置于 /etc/profile.d/中即可开机自动调用该脚本：\n$: ls /etc/profile.d/ heartbeat_led.sh umask.sh F1C100S GPIO操作： #使用sysfs操作GPIO的例子： echo 192 \u0026gt; /sys/class/gpio/export #导出 PG0, GREEN ls /sys/class/gpio/ export gpio192 gpiochip0 unexport ls /sys/class/gpio/gpio192/ active_low direction subsystem/ value device/ power/ uevent echo \u0026#34;out\u0026#34; \u0026gt; /s ys/class/gpio/gpio192/direction #设置为输出 echo 0 \u0026gt; /sys/class/gpio/gpio192/value #亮灯 echo 1 \u0026gt; /sys/class/gpio/gpio192/value #灭灯 echo \u0026#34;in\u0026#34; \u0026gt; /sys/class/gpio/gpio192/direction #设置为输入 cat /sys/class/gpio/gpio192/value #读取电平 0 引脚计算规则： 在Linux中，GPIO 使用0～MAX_INT之间的整数标识； 对于32位CPU，每组GPIO 32个，引脚号就是按顺序排列； 从PA0开始gpio是0，那么PE3对应是32*4+3=131，经试验已验证；\n这个板子是PE12引脚为LED引脚，故：\nPIN_{num}=32\\times4+12=140\n故点灯的脚本如下： echo \u0026#34;out\u0026#34; \u0026gt; /sys/class/gpio/gpio140/direction #设置为输出 #死循环 while true do echo 0 \u0026gt; /sys/class/gpio/gpio140/value echo \u0026#34;点亮\u0026#34; sleep 0.1 echo 1 \u0026gt; /sys/class/gpio/gpio140/value sleep 0.1 echo \u0026#34;熄灭\u0026#34; done 文件系统打包和解压缩（rootfs.tar）： 压缩： sudo tar -cvf rootfs.tar ./ 解压： sudo tar -xf ./rootfs.tar -C /mnt/ 解压到SD卡中： #如果分区已挂载到别的地方先进行卸载 sudo umount /dev/sdb2 #将分区挂载到 /mnt sudo mount /dev/sdb2 /mnt #解压并拷贝rootfs cd ~/f1c100s-sdk/buildroot-2022.02/ sudo tar -xf output/images/rootfs.tar -C /mnt/ #保存退出 sync sudo umount /dev/sdb2 复制.so文件到系统中： From： #虚拟机 /usr/arm-linux-gnueabi/lib To： #嵌入式Linux系统 /usr/lib 有名称相同的文件，跳过即可，不能覆盖；\n关机操作： 需要注意的是，在开发板运行过程中，如果想要重启，请先执行：\npoweroff 命令正常关闭系统后，在按重启按钮，否则有很大概率回造成文件系统损坏；\n","permalink":"https://fan-pengfei.top/posts/f1c100s%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/","summary":"\u003cblockquote\u003e\n\u003cp\u003eUboot和系统跑起来了，接下来就是进行驱动开发了；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"heartbeat心跳灯\"\u003eHeartbeat(心跳灯)\u003c/h2\u003e\n\u003ch3 id=\"修改设备树\"\u003e修改设备树：\u003c/h3\u003e\n\u003cp\u003e在设备树文件\u003ccode\u003earch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts\u003c/code\u003e中；\u003c/p\u003e\n\u003cp\u003e在 / { } 所包裹的根节点目录下添加：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eleds {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    compatible = \u0026#34;gpio-leds\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    user_led {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    label = \u0026#34;led:usr\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        gpios = ; /* PE12 */\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    };\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpios = ; /* PE12 */\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e代表引脚 \u003ccode\u003e4 * 32 + 12\u003c/code\u003e也就是\u003ccode\u003ePE12（ A~G： 0~6）\u003c/code\u003e\n其名字为：\u003ccode\u003eled:usr\u003c/code\u003e；\u003c/p\u003e\n\u003cp\u003e系统启动后与LED控制有关的文件；\u003c/p\u003e\n\u003cp\u003e系统启动后，将看到这样的文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$: ls /sys/class/leds/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eled:usr\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这里三个文件夹分别对应设备树中定义的三个LED；\u003c/p\u003e\n\u003ch3 id=\"编译设备树\"\u003e编译设备树：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e可以只编译设备树，不编译其他文件；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabi- dtbs -j12\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"控制led灯亮灭\"\u003e控制LED灯亮灭：\u003c/h3\u003e\n\u003cp\u003e点亮LED：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$: echo \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u0026gt; /sys/class/leds/led\u003cspan style=\"color:#ae81ff\"\u003e\\:\u003c/span\u003eusr/brightness\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e熄灭LED：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$: echo \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026gt; /sys/class/leds/led\u003cspan style=\"color:#ae81ff\"\u003e\\:\u003c/span\u003eusr/brightness\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e控制LED闪烁：\u003c/p\u003e","title":"F1C100S驱动开发"},{"content":" 最近画了一个Linux板子，主控是全志的F1C100S，自带了32M RAM，不是很大，但是可以学习下；\n板子图片： 板子正面 板子反面 UBoot启动\nFLASH下载工具使用 1、通过命令 sudo sunxi-fel ver 来确认有无成功进入fel模式；\nsudo sunxi-fel ver 2、短接FLASH1和4脚，可以进入fel模式，其实就是CS引脚拉低；\n3、单次运行（下载到RAM中）：\nsunxi-fel uboot /your/path/to/u-boot-sunxi-with-spl.bin 4、烧进 spi-flash （开机自启）\nsunxi-fel -p spiflash-write 0 /your/path/to/u-boot-sunxi-with-spl.bin 其中0是烧录偏移地址；\nUboot编译 sudo apt-get install git git clone https://gitee.com/LicheePiNano/u-boot.git cd u-boot # 查看分支 git branch -a # 切换到 Nano 分支 git checkout nano-lcd800480 git clone -b nano-lcd800480 --depth=1 https://github.com/Lichee-Pi/u-boot.git 此处告知make采用arm-linux-gnueabi下的所有交叉编译工具，目标架构为Arm，设定各项默认配置为 nano 的spiflash支持版 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- f1c100s_nano_uboot_defconfig # 若不带spi-flash的板子，请换成 licheepi_nano_defconfig # 进行可视化配置 make ARCH=arm menuconfig # 修改默认bootcmd gedit include/configs/suniv.h # 需要修改为 #define CONFIG_BOOTCOMMAND \u0026#34;run distro_bootcmd\u0026#34; # 开始编译 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j8 只需要u-boot-sunxi-with-spl.bin即可；\nboot.scr 根据上面对bootcmd的修改，u-boot启动时会从第一分区读取 boot.scr 文件，并执行其中的脚本。我们可以通过这个来设置要传递给linux内核的参数、来加载内核和设备树、来启动内核。\n在uboot目录下新建boot.cmd文件，向其中写入u-boot要执行的脚本：\ncd ~/f1c100s-sdk/u-boot/ touch boot.cmd gedit boot.cmd 写入以下内容：\n#设置传递给内核的bootargs参数 #读取内核镜像和设备树到内存中指定位置 #启动内核程序 setenv bootargs console=tty0 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 rw load mmc 0:1 0x80008000 zImage load mmc 0:1 0x80C00000 suniv-f1c100s-licheepi-nano.dtb bootz 0x80008000 - 0x80C00000 使用u-boot编译后tools目录下的 mkimage 工具可以将boot.cmd文件生成为 boot.scr 文件，通过下面命令：\n#arm架构；不压缩；script文件；输入boot.cmd文件；输出boot.scr文件 tools/mkimage -A arm -C none -T script -d boot.cmd boot.scr 生成的 boot.scr 文件就在当前目录下；\nLinux编译 1、Linux下载\ngit clone https://gitee.com/LicheePiNano/Linux.git cd ./Linux 2、Linux编译\nmake ARCH=arm f1c100s_nano_linux_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j8 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=out modules make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=out modules_install 编译成功后，生成文件所在位置：\n内核img文件：./arch/arm/boot/zImage\n设备树dtb文件:./arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dtb\nmodules文件夹：./out/lib/modules\nbuildrooft构建根文件系统 buildroot可用于构建小型的linux根文件系统。 大小最小可低至2M，与内核一起可以放入最小8M的spi flash中。 buildroot中可以方便地加入第三方软件包（其实已经内置了很多），省去了手工交叉编译的烦恼。\n下载安装 首先安装一些依赖，比如linux头文件：\napt-get install linux-headers-$(uname -r) 然后下载安装：\nwget https://buildroot.org/downloads/buildroot-2021.02.4.tar.gz tar xvf buildroot-2021.02.4.tar.gz cd buildroot-2021.02.4/ make menuconfig 配置 make menuconfig 以下选项为基础配置： - Target options - Target Architecture (ARM (little endian)) - Target Variant arm926t - Toolchain - C library (musl) # 使用musl减小最终体积 - System configuration - Use syslinks to /usr .... # 启用/bin, /sbin, /lib的链接 - Enable root login # 启用root登录 - Run a getty after boot # 启用登录密码输入窗口 - (licheepi) Root password #　默认账户为root 密码为licheepi 另可自行添加或删除指定的软件包 一些配置的简单说明 Target options ---\u0026gt; Target Architecture Variant (arm926t) ---\u0026gt; // arm926ejs架构 [ ] Enable VFP extension support // Nano 没有 VFP单元，勾选会导致某些应用无法运行 Target ABI (EABI) ---\u0026gt; Floating point strategy (Soft float) ---\u0026gt; // 软浮点 System configuration ---\u0026gt; (Lichee Pi) System hostname // hostname (licheepi) Root password // 默认账户为root 密码为licheepi [*] remount root filesystem read-write during boot // 启动时重新挂在文件系统使其可读写 编译 make 编译的过程如果带上下载软件包的时间比较漫长，很适合喝杯茶睡个午觉；(buildroot不能进行多线程编译)\n编译完成的镜像包，是buildroot-2021.02.4/output/images/rootfs.tar；\n测试程序 嵌入式linux开发最终是需要在系统上运行应用程序来实现特定的功能需求，这里编写个基础的应用程序用于测试：\n建立程序文件夹并进入\nmkdir helloworld cd helloworld/ 建立程序文件并编写程序\ntouch helloworld.c gedit helloworld.c 写入以下内容：\n#include int main(void) { printf(\u0026#34;Hello, world!\\n\u0026#34;); } 编译生成可执行文件：\narm-linux-gnueabi-gcc -static helloworld.c -o helloworld 生成的 helloworld 就是我们需要的可执行文件了。\n需要静态编译；\n系统烧录(SD卡) 在SD卡中放了以下文件： ├── boot.cmd ├── boot.scr*（这个可以在Uboot中输入对应命令达到相同的结果） ├── helloworld* ├── rootfs.tar* ├── suniv-f1c100s-licheepi-nano.dtb* ├── u-boot-sunxi-with-spl.bin* └── zImage* 带星号的是加载进SD卡中的； 分别是U-boot、rootfs、Linux、boot.scr、测试程序； 关于boot.scr： 修改默认bootcmd：\ngedit include/configs/suniv.h 需要修改为：\n#define CONFIG_BOOTCOMMAND \u0026#34;run distro_bootcmd\u0026#34; 然后就可以编译了： 根据上面对bootcmd的修改，u-boot启动时会从第一分区读取 boot.scr 文件，并执行其中的脚本。我们可以通过这个来设置要传递给linux内核的参数、来加载内核和设备树、来启动内核。\n也可以使用Uboot中的boot命令：\nboot 命令也是用来启动 Linux 系统的，只是 boot会读取环境变量 bootcmd 来启动 Linux 系 统， bootcmd 是一个很重要的环境变量！其名字分为“boot”和“cmd”，也就是“引导”和“命 令”，说明这个环境变量保存着引导命令，其实就是启动的命令集合，具体的引导命令内容是可 以修改的。比如我们要想使用 tftp 命令从网络启动 Linux 那么就可以设置 bootcmd为“tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 -83000000”，然后使用 saveenv将 bootcmd保存起来。然后直接输入 boot 命令即可从网络启动Linux 系统，命令如下：\n$:setenv bootcmd \u0026#39;tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000\u0026#39; $:saveenv $:boot boot命令使用如下所示：\n$:setenv bootcmd \u0026#39;setenv bootargs console=tty0 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 rw; load mmc 0:1 0x80008000 zImage; load mmc 0:1 0x80C00000 suniv-f1c100s-licheepi-nano.dtb; bootz 0x80008000 - 0x80C00000\u0026#39; $:saveenv $:boot 最后输入boot，启动linux系统，并且重启后仍会自动跳转进入linux系统；\n得到了需要的所有文件，接下来是烧录到SD卡中；\n文件烧录： 前面编译生成的内容可以分块分别烧录进SD卡进行测试，也可以将 u-boot \u0026amp; linux \u0026amp; rootfs 整块打包烧录进SD卡进行测试，其实本质上是一样的，这里先进行分块测试的介绍，打包烧录介绍将在后面的章节说明。\n先将SD卡插入Ubuntu中；\n使用 lsblk 查看SD卡设备号sdX；\n我这里显示为sdb，下面均以此进行说明；\n分区设置： 准备SD卡并按要求分区，空间划分参考下表：\nstart sector size usage\n0KB 0 8KB Unused, available for an MBR or (limited) GPT partition table\n8KB 16 32KB Initial SPL loader\n40KB 80 Max 984KB U-Boot\n1MB 2048 bootfs and rootfs\n下面是在Ubuntu终端中进行分区划分示例：\n如果已经分过区了那么Ubuntu可能会自动挂载；\n逐条使用 sudo umount /dev/sdbn 进行卸载；\n对SD（TF）卡进行分区：\nsudo fdisk /dev/sdb #如果有分区的话可以输入 d 回车依次删除 #输入 n 新建分区，分区大小根据需要设置即可 #下面是我新建的两个分区的输入情况 #n回车 回车(p) 回车(1) 回车(2048) +32M回车 (如果有额外提示则Y回车) #n回车 回车(p) 回车(2) 回车(67584) +200M回车 (如果有额外提示则Y回车) #输入 w 回车保存退出，输入使用 lsblk 查看分区情况 格式化分区建立文件系统：\nsudo mkfs.vfat /dev/sdb1 sudo mkfs.ext4 /dev/sdb2 分块烧录: u-boot： u-boot-sunxi-with-spl.bin 文件需要放置在SD卡8k开始的位置上：\ncd ~/f1c100s-sdk/u-boot/ sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8 linux \u0026amp; dtb \u0026amp; boot.scr 这三个放在刚才新建的第一个分区里（sdb1）：\n#如果分区已挂载到别的地方先进行卸载 sudo umount /dev/sdb1 #将分区挂载到 /mnt sudo mount /dev/sdb1 /mnt #拷贝linux和dtb cd ~/f1c100s-sdk/linux/ sudo cp arch/arm/boot/zImage /mnt/ sudo cp arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dtb /mnt/ #拷贝boot.scr cd ~/f1c100s-sdk/u-boot/ sudo cp boot.scr /mnt/ #保存退出 sync sudo umount /dev/sdb1 rootfs： 这个放在刚才新建的第二个分区里（sdb2）：\n#如果分区已挂载到别的地方先进行卸载 sudo umount /dev/sdb2 #将分区挂载到 /mnt sudo mount /dev/sdb2 /mnt #解压并拷贝rootfs cd ~/f1c100s-sdk/buildroot-2022.02/ sudo tar -xf output/images/rootfs.tar -C /mnt/ #保存退出 sync sudo umount /dev/sdb2 测试程序： #如果分区已挂载到别的地方先进行卸载 sudo umount /dev/sdb2 #将分区挂载到 /mnt sudo mount /dev/sdb2 /mnt #拷贝helloworld cd ~/f1c100s-sdk/helloworld/ sudo cp helloworld /mnt/root/ #保存退出 sync sudo umount /dev/sdb2 系统烧录(SPI FLASH 16M) Flash分区： 分区序号 分区大小 分区作用 地址空间及分区名\nmtd0 1MB spl+uboot 0x0000000-0x0100000 : “uboot”\nmtd2 64KB dtb文件 0x0100000-0x0110000: “dtb”\nmtd2 4MB linux内核 0x0110000-0x0510000 : “kernel”\nmtd3 剩余 根文件系统 0x0510000-0x0c00000 : “rootfs”\nmtd4 剩余 用户区 0x0c00000-0x1000000 : “user”\n烧录前要进入fel模式，然后进行程序下载；\n烧录文件准备： 只有根文件系统烧录文件与TF卡的不同，其他文件均相同；\n准备根文件系统，生成rootfs.img镜像文件：\n1、新建一个目录make_rootfs（这个目录随便找一个路径放就可以了），拷贝rootfs.tar到make_rootfs目录下。\n2、使用命令解压：\ntar -xf rootfs.tar 然后删除压缩包：\nrm -rf rootfs.tar 3、回到上级目录make_rootfs：\ncd ../ 4、然后使用命令生成rootfs.img：\nmkfs.jffs2 -s 0x100 -e 0x10000 -p 0x6F0000 -d rootfs/ -o rootfs.img 说明：（0x10000：块擦除大小）,（0x6F0000：分区的大小）；\n5、mtd-utils安装： 此步骤是上一步制作根文件系统的命令没有的前提下进行的；\n安装mkfs.jffs2工具：\nsudo apt-get install mtd-utils 烧录命令： 清空flash，进入FEL模式：\nsf probe 0 sf erase 0 0x100000 #清空flash reset\t#即可重新进入fel模式 烧录u-boot sudo sunxi-fel -p spiflash-write 0 ./u-boot-sunxi-with-spl.bin 烧录kernel sudo sunxi-fel -p spiflash-write 0x0110000 ./zImage 烧录dtb sudo sunxi-fel -p spiflash-write 0x0100000 ./suniv-f1c100s-licheepi-nano.dtb 烧录rootfs sudo sunxi-fel -p spiflash-write 0x0510000 ./rootfs.img 烧录userfs 说明：这个是个人创建的文件系统，该分区如果不需要可以不烧了；\nsudo sunxi-fel -p spiflash-write 0x0c00000 userfs.img U-boot中启动系统： sf probe 0 50000000 sf read 0x80C00000 0x100000 0x10000 #加载dtb设备树 sf read 0x80008000 0x110000 0x400000 #加载内核 bootz 0x80008000 - 0x80C00000 setenv bootcmd \u0026#39;sf probe 0 50000000; sf read 0x80C00000 0x100000 0x10000; sf read 0x80008000 0x110000 0x400000; bootz 0x80008000 - 0x80C00000\u0026#39; saveenv U-boot中sf命令用法： sf read用来读取flash数据到内存；\nsf write写内存数据到flash；\nsf erase 擦除指定位置,指定长度的flash内容, 在进行写flash的时候一定要先进行擦除，否则会失败，因为flash只能从1变为0；\n具体用法： sf - SPI flash sub-system Usage:0 sf probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus and chip select sf read addr offset len - read `len\u0026#39; bytes starting at `offset\u0026#39; to memory at `addr\u0026#39; sf write addr offset len - write `len\u0026#39; bytes from memory at `addr\u0026#39; to flash at `offset\u0026#39; sf erase offset [+]len - erase `len\u0026#39; bytes from `offset\u0026#39; `+len\u0026#39; round up `len\u0026#39; to block size sf update addr offset len - erase and write `len\u0026#39; bytes from memory at `addr\u0026#39; to flash at `offset\u0026#39; 示例： 连接flash： sf probe 0 在使用sf的其他命令之前必须先进行此操作进行连接flash；\n写flash： sf write 0x82000000 0x8000 0x20000 把内存0x8200 0000处的数据, 写入flash的偏移0x80000, 写入数据长度为0x20000(128KB), 操作偏移和长度最小单位是Byte；\n读flash： sf read 0x82000000 0x10000 0x20000 把flash偏移0x10000(64KB)处, 长度为0x20000(128KB)的数据, 写入到内存0x82000000, 操作偏移和长度最小单位是Byte；\n擦除flash： sf erase 0x0 0x10000 擦除偏移0x0处, 到0x10000之间的擦除块, 擦除操作是以erase block为单位的, 要求offset和len参数必须是erase block对齐的；\nsunxi-fel工具的使用： 安装： git clone -b f1c100s-spiflash https://github.com/Icenowy/sunxi-tools.git cd sunxi-tools make \u0026amp;\u0026amp; sudo make install sudo apt install pkg-config sudo apt install pkgconf sudo apt-get install zlib1g-dev sudo apt-get install libusb-1.0-0-dev 用法： Usage: ./sunxi-fel [options] command arguments... [command...] -h, --help Print this usage summary and exit -v, --verbose Verbose logging -p, --progress \u0026#34;write\u0026#34; transfers show a progress bar -l, --list Enumerate all (USB) FEL devices and exit -d, --dev bus:devnum Use specific USB bus and device number --sid SID Select device by SID key (exact match) spl file Load and execute U-Boot SPL If file additionally contains a main U-Boot binary (u-boot-sunxi-with-spl.bin), this command also transfers that to memory (default address from image), but won\u0026#39;t execute it. uboot file-with-spl like \u0026#34;spl\u0026#34;, but actually starts U-Boot U-Boot execution will take place when the fel utility exits. This allows combining \u0026#34;uboot\u0026#34; with further \u0026#34;write\u0026#34; commands (to transfer other files needed for the boot). hex[dump] address length Dumps memory region in hex dump address length Binary memory dump exe[cute] address Call function address reset64 address RMR request for AArch64 warm boot memmove dest source size Copy bytes within device memory readl address Read 32-bit value from device memory writel address value Write 32-bit value to device memory read address length file Write memory contents into file write address file Store file contents into memory write-with-progress addr file \u0026#34;write\u0026#34; with progress bar write-with-gauge addr file Output progress for \u0026#34;dialog --gauge\u0026#34; write-with-xgauge addr file Extended gauge output (updates prompt) multi[write] # addr file ... \u0026#34;write-with-progress\u0026#34; multiple files, sharing a common progress status multi[write]-with-gauge ... like their \u0026#34;write-with-*\u0026#34; counterpart, multi[write]-with-xgauge ... but following the \u0026#39;multi\u0026#39; syntax: addr file [addr file [...]] echo-gauge \u0026#34;some text\u0026#34; Update prompt/caption for gauge output ver[sion] Show BROM version sid Retrieve and output 128-bit SID key clear address length Clear memory fill address length value Fill memory spiflash-info Retrieves basic information spiflash-read addr length file Write SPI flash contents into file spiflash-write addr file Store file contents into SPI flash 示例： 烧录文件到FLASH：\nsudo sunxi-fel -p spiflash-write 0 Your-Flash-BIN 烧录到RAM中：\nsudo sunxi-fel uboot u-boot-sunxi-with-spl.bin 检测usb设备：\n$: sudo sunxi-fel -l USB device 002:005 Allwinner F1C100s 列出设备信息：\n$: AWUSBFEX soc=00001663(F1C100s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000 F1C100S GPIO操作： #使用sysfs操作GPIO的例子： echo 192 \u0026gt; /sys/class/gpio/export #导出 PG0, GREEN ls /sys/class/gpio/ export gpio192 gpiochip0 unexport ls /sys/class/gpio/gpio192/ active_low direction subsystem/ value device/ power/ uevent echo \u0026#34;out\u0026#34; \u0026gt; /sys/class/gpio/gpio192/direction #设置为输出 echo 0 \u0026gt; /sys/class/gpio/gpio192/value #亮灯 echo 1 \u0026gt; /sys/class/gpio/gpio192/value #灭灯 echo \u0026#34;in\u0026#34; \u0026gt; /sys/class/gpio/gpio192/direction #设置为输入 cat /sys/class/gpio/gpio192/value #读取电平 0 引脚计算规则：\n在Linux中，GPIO 使用0～MAX_INT之间的整数标识。 对于32位CPU，每组GPIO 32个，引脚号就是按顺序排列。 从PA0开始gpio是0，那么PE3对应是32*4+3=131，经试验已验证；\n这个板子是PE12引脚为LED引脚，故：\nPIN_{num}=32\\times4+12=140 故点灯的脚本如下：\necho \u0026#34;out\u0026#34; \u0026gt; /sys/class/gpio/gpio140/direction #设置为输出 #死循环 while true do echo 0 \u0026gt; /sys/class/gpio/gpio140/value echo \u0026#34;点亮\u0026#34; sleep 0.1 echo 1 \u0026gt; /sys/class/gpio/gpio140/value sleep 0.1 echo \u0026#34;熄灭\u0026#34; done Linux取消开机登录输入账户密码： 找到 /etc/inittab 文件的：\nconsole::respawn:/sbin/getty -L console 0 vt100 # GENERIC_SERIAL 修改为：\nconsole::respawn:-/bin/sh 重启后就没有恼人的 login 提示了；\n","permalink":"https://fan-pengfei.top/posts/f1c100s%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近画了一个Linux板子，主控是全志的F1C100S，自带了32M RAM，不是很大，但是可以学习下；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"板子图片\"\u003e板子图片：\u003c/h2\u003e\n\u003cp\u003e板子正面\n板子反面\nUBoot启动\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"f1c100s-1\" loading=\"lazy\" src=\"/posts/f1c100s%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-1.jpg\"\u003e\n\u003cimg alt=\"f1c100s-2\" loading=\"lazy\" src=\"/posts/f1c100s%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-2.jpg\"\u003e\n\u003cimg alt=\"f1c100s-3\" loading=\"lazy\" src=\"/posts/f1c100s%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/img-3.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"flash下载工具使用\"\u003eFLASH下载工具使用\u003c/h2\u003e\n\u003cp\u003e1、通过命令 \u003ccode\u003esudo sunxi-fel ver\u003c/code\u003e 来确认有无成功进入fel模式；\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo sunxi-fel ver\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e2、短接FLASH\u003ccode\u003e1\u003c/code\u003e和\u003ccode\u003e4\u003c/code\u003e脚，可以进入fel模式，其实就是CS引脚拉低；\u003c/p\u003e\n\u003cp\u003e3、单次运行（下载到RAM中）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esunxi-fel uboot /your/path/to/u-boot-sunxi-with-spl.bin\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e4、烧进 spi-flash （开机自启）\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esunxi-fel -p spiflash-write 0 /your/path/to/u-boot-sunxi-with-spl.bin\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中0是烧录偏移地址；\u003c/p\u003e\n\u003ch2 id=\"uboot编译\"\u003eUboot编译\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt-get install git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone https://gitee.com/LicheePiNano/u-boot.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd u-boot\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 查看分支\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit branch -a\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 切换到 Nano 分支\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit checkout nano-lcd800480\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone -b nano-lcd800480 --depth\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e https://github.com/Lichee-Pi/u-boot.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e此处告知make采用arm-linux-gnueabi下的所有交叉编译工具，目标架构为Arm，设定各项默认配置为 nano 的spiflash支持版\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabi- f1c100s_nano_uboot_defconfig\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 若不带spi-flash的板子，请换成 licheepi_nano_defconfig\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 进行可视化配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm menuconfig\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 修改默认bootcmd\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egedit include/configs/suniv.h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 需要修改为\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define CONFIG_BOOTCOMMAND \u0026#34;run distro_bootcmd\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 开始编译\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake ARCH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm CROSS_COMPILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003earm-linux-gnueabi- -j8\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e只需要\u003ccode\u003eu-boot-sunxi-with-spl.bin\u003c/code\u003e即可；\u003c/p\u003e","title":"F1C100S开发记录"},{"content":" 大四应该没多少事情了，想继续做一下FOC相关的；\n开发过程(持续更新)： 2022年9月29日： 中间好久没有更新，是这几天比较忙，没有时间写；\n算下来，FOC的板子做了三个大的版本，五个比较小的版本，可以说是一次次的迭代更新；\n主控 供电方案 驱动模块 是否有电流采样 通信方式\nFOC-V1 STM32F103C6T6 AMS1117-3.3V DRV8313 否 UART\nFOC-V2 STM32F103C6T6 AMS1117-3.3V DRV8313 否 UART\nFOC-V3 STM32F103C6T6 AMS1117-3.3V MP6540 是 UART\nFOC-V4 AT32F403ACGU7 AMS1117-5V \u0026amp; AMS117-3.3V DRV8313 否 UART\nFOC-V5 STM32F405RGT6 L7805 \u0026amp; AMS1117-3.3V BTN7960B 是 UART/CAN\n代码自己也加入了很多功能，电流环还没有做出来，后边再继续努力做，加油！\n板子PCB： 版本 图片\nFOC-V1 FOC-V2 FOC-V3 FOC-V4 FOC-V5 2022年9月12日： 中间好久不更新，这些天对FOC项目做了更多的测试，还有做了一些应用，例如倒立摆，平衡车等等；\n想要加上电流环，刚开始自然的想法是，用合金采样电阻加电流放大器加stm32的AD，这个外围电路比较麻烦，成本也很高；\n昨天查找资料发现了一个芯片：MP6540，三相电机驱动芯片，且内部集成了电流检测，只需要用32的AD去读就可以了，今上午画了一个测试板，已经发去打样了，希望能正常工作；\n等板子回来了，我测试一下，在说要不要改用这个芯片；\n2022年9月3日： 修改的板子今天已经到了，焊接好了，也测试了，很完美，发热没那么严重了；\n这次使用的LDO是AMS1117(SOT-89封装)，它的输入最高电压是18V，完全够用了，发热虽然还是很大，但封装也更大了，所以还是可以接受的；\nAMS1117数据手册说明的的输入电压限制\n在测试过程中，长时间运转未出现任何问题，闭环控制，开环控制都可以很好运转；\n后续就是开始做平衡车，调参数；\n2022年8月22日： 今天又查看了一下RT9013-33GB的数据手册，发现电压最大为5.5V，看来是要换供电部分的电路了；\n输入电压限制 输出电流限制\n下午用飞线将降压芯片换成了AMS1117-3.3V，效果还好，不过还是会出现温度很高的情况；\n更换降压芯片后的板子\n测试了闭环角度控制以及闭环速度控制，效果都还挺好的；\n后续调参数，修改板子；\n2022年8月21日： 板子已经收到，并且已经焊好各种元件以及安装在配套的无刷云台电机上；\n图片如下所示：\n板子实物图 凌乱的桌面\n板子出现了一个比较大的问题，给单片机芯片以及磁传感器芯片供电的3.3V电压是通过一个线性稳压芯片做的，型号是RT9013-33GB，线性稳压芯片有一个很大的问题就是，当压降比较大时，效率会很低，其计算公式为：\n\\eta=\\frac{I_{OUT}}{(I_{OUT}+I_{GND})}*\\frac{V_{OUT}}{V_{IN}}*100% 该式子前者接近于1，故效率主要取决于后者，输入输出压差越大，效率就越低，耗散的功率绝大部分都转化为热量，所以芯片温度会很高，很容易烧毁芯片造成短路，甚至会对主控芯片和驱动芯片造成损伤；\n而驱动芯片使用的是DRV8313，最低的驱动电压是8V，经过线性稳压芯片输出3.3V，压降比较大，发热很大，下午调试的时候，用的可调电源，从5V往上升，升到10V的时候还是正常，除了发热比较大；再高一点，突然就芯片烧了，不仅是稳压芯片，连带着主控芯片也烧了，万用表一测，正负极直接滴滴响，换了芯片后，调到高一点的电压，还是会烧芯片；\n另一个问题是，当输入电压为8V时，虽然不会直接毁芯片，但是运行一会，温度比较高，影响输出电压，导致磁编码器芯片不能正常工作，这个问题也很头疼，想到的解决办法是使用DC-DC降压电路先降到5V，再用线性稳压芯片降到3.3V，这就等下一版板子进行修改测试；\n除了这些问题之外，其他倒没有什么问题，测试了开环驱动，闭环有一点问题，后续修改一下代码应该就可，角度值也可以正常读取；\n后续：修改板子，先用现有这一版测试闭环驱动，然后调参，然后上平衡车；\n2022年8月16日： 已经发去打板，原理图和PCB图如下所示：\n等打样的板子收到后看一下功能是不是都正常；\n基本思路： 先把一个电机和单片机，驱动电路集成的出来，测试驱动能力，还有控制算法；\n电流环先不加；\n然后是做一个平衡车；\n基本描述： 主控芯片：STM32F103C6T6A\n驱动芯片：DRV8313\n通讯方式：串口控制\n参考方案：https://oshwhub.com/iMcHineSe/mini_simplefoc\n参考程序下载地址：https://oshwhub.com/attachments/2022/6/Yc5g3HpSO4iSV1dMhxs98B2FosqwhmaWe8Zns9UF.bin?operation=download\n原理图：\n一些关键点，要集成接口，编码所在的板子上留出电机UVW三相的焊接接口；\n","permalink":"https://fan-pengfei.top/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e大四应该没多少事情了，想继续做一下FOC相关的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"开发过程持续更新\"\u003e开发过程(持续更新)：\u003c/h1\u003e\n\u003ch2 id=\"2022年9月29日\"\u003e2022年9月29日：\u003c/h2\u003e\n\u003cp\u003e中间好久没有更新，是这几天比较忙，没有时间写；\u003c/p\u003e\n\u003cp\u003e算下来，FOC的板子做了三个大的版本，五个比较小的版本，可以说是一次次的迭代更新；\u003c/p\u003e\n\u003cp\u003e主控\n供电方案\n驱动模块\n是否有电流采样\n通信方式\u003c/p\u003e\n\u003cp\u003eFOC-V1\nSTM32F103C6T6\nAMS1117-3.3V\nDRV8313\n否\nUART\u003c/p\u003e\n\u003cp\u003eFOC-V2\nSTM32F103C6T6\nAMS1117-3.3V\nDRV8313\n否\nUART\u003c/p\u003e\n\u003cp\u003eFOC-V3\nSTM32F103C6T6\nAMS1117-3.3V\nMP6540\n是\nUART\u003c/p\u003e\n\u003cp\u003eFOC-V4\nAT32F403ACGU7\nAMS1117-5V \u0026amp; AMS117-3.3V\nDRV8313\n否\nUART\u003c/p\u003e\n\u003cp\u003eFOC-V5\nSTM32F405RGT6\nL7805 \u0026amp; AMS1117-3.3V\nBTN7960B\n是\nUART/CAN\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e代码自己也加入了很多功能，电流环还没有做出来，后边再继续努力做，加油！\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"板子pcb\"\u003e板子PCB：\u003c/h4\u003e\n\u003cp\u003e版本\n图片\u003c/p\u003e\n\u003cp\u003eFOC-V1\n\u003cimg alt=\"5a51969d25e04571c84bbed4619b3d1\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003eFOC-V2\n\u003cimg alt=\"810f1cf95eb622c35b9f178e4779c89\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003eFOC-V3\n\u003cimg alt=\"9e2eb6c5c2c84dea0f5ffeafe90cf76\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003eFOC-V4\n\u003cimg alt=\"98b04d9d4090dbfbf42a2c17963e346\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003eFOC-V5\n\u003cimg alt=\"de81656ff3a84382a63eb2933b4aada\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-5.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2022年9月12日\"\u003e2022年9月12日：\u003c/h2\u003e\n\u003cp\u003e中间好久不更新，这些天对FOC项目做了更多的测试，还有做了一些应用，例如倒立摆，平衡车等等；\u003c/p\u003e\n\u003cp\u003e想要加上电流环，刚开始自然的想法是，用合金采样电阻加电流放大器加stm32的AD，这个外围电路比较麻烦，成本也很高；\u003c/p\u003e\n\u003cp\u003e昨天查找资料发现了一个芯片：\u003cstrong\u003eMP6540\u003c/strong\u003e，三相电机驱动芯片，且内部集成了电流检测，只需要用32的AD去读就可以了，今上午画了一个测试板，已经发去打样了，希望能正常工作；\u003c/p\u003e\n\u003cp\u003e等板子回来了，我测试一下，在说要不要改用这个芯片；\u003c/p\u003e\n\u003ch2 id=\"2022年9月3日\"\u003e2022年9月3日：\u003c/h2\u003e\n\u003cp\u003e修改的板子今天已经到了，焊接好了，也测试了，很完美，发热没那么严重了；\u003c/p\u003e\n\u003cp\u003e这次使用的LDO是AMS1117(SOT-89封装)，它的输入最高电压是18V，完全够用了，发热虽然还是很大，但封装也更大了，所以还是可以接受的；\u003c/p\u003e\n\u003cp\u003eAMS1117数据手册说明的的输入电压限制\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"fcf213d49a6cab89ced651af4a63af8\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e在测试过程中，长时间运转未出现任何问题，闭环控制，开环控制都可以很好运转；\u003c/p\u003e\n\u003cp\u003e后续就是开始做平衡车，调参数；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"FOC_V1\" loading=\"lazy\" src=\"/posts/foc%E9%A1%B9%E7%9B%AE%E8%AE%B0%E5%BD%95/img-7.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2022年8月22日\"\u003e2022年8月22日：\u003c/h2\u003e\n\u003cp\u003e今天又查看了一下RT9013-33GB的数据手册，发现电压最大为5.5V，看来是要换供电部分的电路了；\u003c/p\u003e\n\u003cp\u003e输入电压限制\n输出电流限制\u003c/p\u003e","title":"FOC项目记录"},{"content":" 最近在做FOC项目的时候，用到了GIT中的分支功能，因而再复习一下GIT的用法，在此记录一下GIT常用命令；\n为表简洁，我就直接给出具体的命令以及简短的注释和自己对此的理解；\n1、git init 用于初始化git仓库；\n2、git add +文件名 将指定工作区文件提交到git暂存区； -A，是添加文件夹内的所有文件到暂存区； 每次提交，都要git add加入到暂存区，然后再commit提交； 第一次修改 -\u0026gt; git add -\u0026gt; 第二次修改 -\u0026gt; git add -\u0026gt; git commit； commit提交的是git add 加入的修改的内容，而不是文件当前的内容；\n3、git commit -m +“本次提交的说明” 将暂存区文件提交到仓库； -m，后边是关于本次提交的说明； 该命令执行成功后会显示提交的内容： 例如： 1 file changed：1个文件被改动； 2 insertions：插入了两行内容；\n4、git status 显示当前仓库的状态（被修改的文件是否被提交）； 例如某个文件被修改却未被提交； 包括工作区的文件修改情况和暂存区的文件修改情况；\n5、git diff +文件名 查看文件修改内容； 常看尚未被提交的文件做了什么修改； 是工作区和暂存区文件之间的差别；\n6、git log 显示从近到远的提交日志； 7、git reset —hard HEAD^ 回退到上一版本； HEAD 当前版本 HEAD^ 上一版本 HEAD^^ 上上一版本\n8、git reset —hard 1094a 回退或者前进到指定版本； —hard，后边的是版本号前几位；\n9、git reflog 显示历史命令； 10、git diff HEAD — readme.txt 查看readme.txt文件工作区和版本库中最新版本的区别；\n11、git checkout — 文件名 撤销工作区的修改； 让这个文件回到最近一次git commit或git add时的状态； 注意是 --； git checkout其实是用版本库里的版本替换工作区的版本，无论工作区是修改还是删除，都可以“一键还原”；\n12、git reset HEAD 文件名 撤销暂存区的修改，重新放回工作区； 已经提交到暂存区，如何从暂存区和工作区撤销： （git reset HEAD 文件名）撤销暂存区的修改，回到工作区-\u0026gt;（git checkout -- 文件名）撤销工作区的修改；\n13、git rm 文件名 从版本库删除某文件； git rm -\u0026gt; commit提交该删除；\n14、git branch \u0026lt;分支名\u0026gt; 创建分支； 不加参数为列出所有分支，当前分支前有一个*号；\n15、git branch -d \u0026lt;分支名\u0026gt; 删除某一分支； -d，delete；\n15、git checkout \u0026lt;分支名\u0026gt; 切换到某一分支； git checkout -b ，创建并切换到新的分支；\n16、git switch \u0026lt;分支名\u0026gt; 切换到某一分支； git switch -c ，创建并切换到新的分支；\n17、git merge 用于合并指定分支到当前分支；\n18、分支合并 分支1修改-\u0026gt;提交该修改-\u0026gt;分支2修改-\u0026gt;提交该修改-\u0026gt;合并分支-\u0026gt;出现冲突-\u0026gt;解决冲突-\u0026gt;合并分支-\u0026gt;删除某一分支；\n19、git log —graph 查看分支合并图；\n20、git merge —no-ff -m “merge with no-ff” dev 合并dev分支，并有合并信息； 合并分支时，加上--no-ff参数就可以用普通模式合并，合并后的历史有分支，能看出来曾经做过合并，而fast forward合并就看不出来曾经做过合并；\n21、分支管理策略 首先，master分支应该是非常稳定的，也就是仅用来发布新版本，平时不能在上面干活； 那在哪干活呢？干活都在dev分支上，也就是说，dev分支是不稳定的，到某个时候，比如1.0版本发布时，再把dev分支合并到master上，在master分支发布1.0版本； 你和你的小伙伴们每个人都在dev分支上干活，每个人都有自己的分支，时不时地往dev分支上合并就可以了； 所以，团队合作的分支看起来就像这样； 22、git stash 暂存当前工作区和暂存区的内容，可以等恢复现场后再工作； git stash list，列出当前暂存的内容； git stash apply，恢复当前暂存的内容到工作区； git stash drop，删除暂存的内容； git stash pop，恢复当前暂存的内容到工作区的同时删除暂存的内容； 可以多次stash，先用git stash list查看，然后恢复指定的stash，用命令：git stash apply stash@{0}；\n23、修复bug 在Git中，由于分支是如此的强大，所以，每个bug都可以通过一个新的临时分支来修复，修复后，合并分支，然后将临时分支删除； 修复bug时，我们会通过创建新的bug分支进行修复，然后合并，最后删除； 当手头工作没有完成时，先把工作现场git stash一下，然后去修复bug，修复后，再git stash pop，回到工作现场； 在master分支上修复的bug，想要合并到当前dev分支，可以用git cherry-pick 命令，把bug提交的修改“复制”到当前分支，避免重复劳动。\n24、修复不同分支上相同的bug Git专门提供了一个cherry-pick命令，让我们能复制一个特定的提交到当前分支； git cherry-pick 4c805e2；\n25、开发新的feature 开发一个新feature，最好新建一个分支； 如果要丢弃一个没有被合并过的分支，可以通过git branch -D 强行删除。\n26、标签管理 发布一个版本时，我们通常先在版本库中打一个标签（tag），这样，就唯一确定了打标签时刻的版本。将来无论什么时候，取某个标签的版本，就是把那个打标签的时刻的历史版本取出来。所以，标签也是版本库的一个快照。 Git的标签虽然是版本库的快照，但其实它就是指向某个commit的指针（跟分支很像对不对？但是分支可以移动，标签不能移动），所以，创建和删除标签都是瞬间完成的。 在Git中打标签非常简单，首先，切换到需要打标签的分支上-\u0026gt; 然后，敲命令git tag 就可以打一个新标签-\u0026gt; 可以用命令git tag查看所有标签； 默认标签是打在最新提交的commit上的。有时候，如果忘了打标签，比如，现在已经是周五了，但应该在周一打的标签没有打，怎么办？ 方法是找到历史提交的commit id，然后打上就可以了：\n\u0026gt;$ git log --pretty=oneline --abbrev-commit \u0026gt;12a631b (HEAD -\u0026gt; master, tag: v1.0, origin/master) merged bug fix 101 \u0026gt;4c805e2 fix bug 101 \u0026gt;e1e9c68 merge with no-ff \u0026gt;f52c633 add merge \u0026gt;cf810e4 conflict fixed \u0026gt;5dc6824 \u0026amp; simple \u0026gt;14096d0 AND simple \u0026gt;b17d20e branch test \u0026gt;d46f35e remove test.txt \u0026gt;b84166e add test.txt \u0026gt;519219b git tracks changes \u0026gt;e43a48b understand how stage works \u0026gt;1094adb append GPL \u0026gt;e475afc add distributed \u0026gt;eaadf4e wrote a readme file 比方说要对add merge这次提交打标签，它对应的commit id是f52c633，敲入命令：\n\u0026gt;$ git tag v0.9 f52c633 再用命令git tag查看标签：\n\u0026gt;$ git tag \u0026gt;v0.9 \u0026gt;v1.0 注意，标签不是按时间顺序列出，而是按字母排序的。可以用git show 查看标签信息：\n\u0026gt;$ git show v0.9 \u0026gt;commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9) \u0026gt;Author: Michael Liao \u0026gt;Date: Fri May 18 21:56:54 2018 +0800 add merge \u0026gt;diff --git a/readme.txt b/readme.txt \u0026gt;... 可以看到，v0.9确实打在add merge这次提交上。 还可以创建带有说明的标签，用-a指定标签名，-m指定说明文字：\n\u0026gt;$ git tag -a v0.1 -m \u0026#34;version 0.1 released\u0026#34; 1094adb 用命令git show 可以看到说明文字：\n\u0026gt;$ git show v0.1 \u0026gt;tag v0.1 \u0026gt;Tagger: Michael Liao \u0026gt;Date: Fri May 18 22:48:43 2018 +0800 \u0026gt;version 0.1 released \u0026gt;commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1) \u0026gt;Author: Michael Liao \u0026gt;Date: Fri May 18 21:06:15 2018 +0800 append GPL \u0026gt;diff --git a/readme.txt b/readme.txt \u0026gt;... 命令git tag 用于新建一个标签，默认为HEAD，也可以指定一个commit id； 命令git tag -a -m \u0026quot;blablabla...\u0026quot;可以指定标签信息； 命令git tag可以查看所有标签； 如果标签打错了，也可以删除：\n\u0026gt;$ git tag -d v0.1 \u0026gt;Deleted tag \u0026#39;v0.1\u0026#39; (was f15b0dd) 因为创建的标签都只存储在本地，不会自动推送到远程。所以，打错的标签可以在本地安全删除。\n27、忽略特殊文件 在Git工作区的根目录下创建一个特殊的.gitignore文件，然后把要忽略的文件名填进去，Git就会自动忽略这些文件； 例如：\n\u0026gt;# Windows: \u0026gt;Thumbs.db \u0026gt;ehthumbs.db \u0026gt;Desktop.ini \u0026gt;# Python: \u0026gt;*.py[cod] \u0026gt;*.so \u0026gt;*.egg \u0026gt;*.egg-info \u0026gt;dist \u0026gt;build \u0026gt;# My configurations: \u0026gt;db.ini \u0026gt;deploy_key_rsa 忽略某些文件时，需要编写.gitignore； .gitignore文件本身要放到版本库里，并且可以对.gitignore做版本管理！ 有些时候，你想添加一个文件到Git，但发现添加不了，原因是这个文件被.gitignore忽略了：\n\u0026gt;$ git add App.class \u0026gt;The following paths are ignored by one of your .gitignore files: \u0026gt;App.class \u0026gt;Use -f if you really want to add them. 如果你确实想添加该文件，可以用-f强制添加到Git：\n\u0026gt;$ git add -f App.class 或者你发现，可能是.gitignore写得有问题，需要找出来到底哪个规则写错了，可以用git check-ignore命令检查：\n\u0026gt;$ git check-ignore -v App.class \u0026gt;.gitignore:3:*.class\tApp.class Git会告诉我们，.gitignore的第3行规则忽略了该文件，于是我们就可以知道应该修订哪个规则。 还有些时候，当我们编写了规则排除了部分文件时：\n\u0026gt;# 排除所有.开头的隐藏文件: \u0026gt;.* \u0026gt;# 排除所有.class文件: \u0026gt;*.class 但是我们发现.*这个规则把.gitignore也排除了，并且App.class需要被添加到版本库，但是被*.class规则排除了。 虽然可以用git add -f强制添加进去，但有强迫症的童鞋还是希望不要破坏.gitignore规则，这个时候，可以添加两条例外规则：\n\u0026gt;# 排除所有.开头的隐藏文件: \u0026gt;.* \u0026gt;# 排除所有.class文件: \u0026gt;*.class \u0026gt;# 不排除.gitignore和App.class: \u0026gt;!.gitignore \u0026gt;!App.class 把指定文件排除在.gitignore规则外的写法就是!+文件名，所以，只需把例外文件添加进去即可。\n28、打包发布文件 使用git压缩文档命令： 1、打包所有文档\n打包master分枝的所有文档\ngit archive --format=zip --output master.zip master 其中，输出格式为zip，输出文档为master.zip；git支持zip和tar两种输出格式；\n2、打包当前分枝当前HEAD的所有文档\ngit archive --format=zip --output head.zip GEAD 3、打包v1.2标签的所有文档\ngit archive --format=zip --output v1.2.zip v1.2 4、打包更改的文档\n原理： 用git diff找出文档列表，再用打包命令打包； 也就是说：只要能用找出文档列表，就可以git打包出来；\n5、打包最后修改的文档\n先通过git diff找到最新版本修改过的文档，再压缩打包这些文档；\ngit archive --format=zip -o update.zip HEAD $(git diff --name--only HEAD^) 6、打包最后两个版本修改的文档\n总共也是2个版本；\ngit achive --format=zip -o update.zip HEAD $(git diff --name-only HEAD~2) 7、打包两个分枝之间差别的文档\ngit archive --formate=zip -o update.zip HEAD $(git diff --name only master fix-error) 29、简单明了显示log git log --graph --oneline --decorate --all 30、更新与合并 要更新你的本地仓库至最新改动，执行：\ngit pull 以在你的工作目录中 获取（fetch） 并 合并（merge） 远端的改动； 要合并其他分支到你的当前分支（例如 master），执行：\ngit merge 在这两种情况下，git 都会尝试去自动合并改动。遗憾的是，这可能并非每次都成功，并可能出现冲突（conflicts）。 这时候就需要你修改这些文档来手动合并这些冲突（conflicts）。改完之后，你需要执行如下命令以将它们标记为合并成功：\ngit add 在合并改动之前，你可以使用如下命令预览差异：\ngit diff 31、标签 为软件发布创建标签是推荐的。这个概念早已存在，在 SVN 中也有。你可以执行如下命令创建一个叫做 1.0.0 的标签：\ngit tag 1.0.0 1b2e1d63ff 1b2e1d63ff 是你想要标记的提交 ID 的前 10 位字符。可以使用下列命令获取提交 ID：\ngit log 你也可以使用少一点的提交 ID 前几位，只要它的指向具有唯一性；\n32、替换本地改动 假如你操作失误（当然，这最好永远不要发生），你可以使用如下命令替换掉本地改动：\ngit checkout -- 此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文档；已添加到暂存区的改动以及新文档都不会受到影响；\n假如你想丢弃你在本地的所有改动与提交，可以到服务器上获取最新的版本历史，并将你本地主分支指向它：\ngit fetch origin git reset --hard origin/master 33、推送改动 你的改动现在已经在本地仓库的 HEAD 中了；执行如下命令以将这些改动提交到远端仓库：\ngit push origin master 可以把 master 换成你想要推送的任何分支；\n如果你还没有克隆现有仓库，并欲将你的仓库连接到某个远程服务器，你可以使用如下命令添加：\ngit remote add origin 如此你就能够将你的改动推送到所添加的服务器上去了；\n34、修改提交记录 修改最近的一次提交记录；\ngit commit --amend 参考：廖永峰的git教程；\n","permalink":"https://fan-pengfei.top/posts/git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近在做FOC项目的时候，用到了GIT中的分支功能，因而再复习一下GIT的用法，在此记录一下GIT常用命令；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e为表简洁，我就直接给出具体的命令以及简短的注释和自己对此的理解；\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"1git-init\"\u003e1、git init\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用于初始化git仓库；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"2git-add-文件名\"\u003e2、git add +文件名\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e将指定工作区文件提交到git暂存区；\n-A，是添加文件夹内的所有文件到暂存区；\n每次提交，都要\u003ccode\u003egit add\u003c/code\u003e加入到暂存区，然后再commit提交；\n第一次修改 -\u0026gt; \u003ccode\u003egit add\u003c/code\u003e -\u0026gt; 第二次修改 -\u0026gt; \u003ccode\u003egit add\u003c/code\u003e -\u0026gt; \u003ccode\u003egit commit\u003c/code\u003e；\ncommit提交的是git add 加入的修改的内容，而不是文件当前的内容；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"3git-commit--m-本次提交的说明\"\u003e3、git commit -m +“本次提交的说明”\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e将暂存区文件提交到仓库；\n-m，后边是关于本次提交的说明；\n该命令执行成功后会显示提交的内容：\n例如：\n\u003ccode\u003e1 file changed\u003c/code\u003e：1个文件被改动；\n\u003ccode\u003e2 insertions\u003c/code\u003e：插入了两行内容；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"4git-status\"\u003e4、git status\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e显示当前仓库的状态（被修改的文件是否被提交）；\n例如某个文件被修改却未被提交；\n包括工作区的文件修改情况和暂存区的文件修改情况；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"5git-diff-文件名\"\u003e5、git diff +文件名\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e查看文件修改内容；\n常看尚未被提交的文件做了什么修改；\n\u003cstrong\u003e是工作区和暂存区文件之间的差别；\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"6git-log\"\u003e6、git log\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e显示从近到远的提交日志；\n\u003cimg alt=\"efb8ab5c515470ebd88aece20684127\" loading=\"lazy\" src=\"/posts/git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/img-1.png\"\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"7git-reset-hard-head\"\u003e7、git reset —hard HEAD^\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e回退到上一版本；\nHEAD\n当前版本\nHEAD^\n上一版本\nHEAD^^\n上上一版本\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"8git-reset-hard-1094a\"\u003e8、git reset —hard 1094a\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003e回退或者前进到指定版本；\n—hard，后边的是版本号前几位；\u003c/p\u003e","title":"git常用命令"},{"content":" 昨天花了一个USB转TTL小板子，其中芯片引脚有RT和CTS，之前从没用过，还是查找资料了解一下吧。\nRTS （Require ToSend，发送请求）为输出信号，用于指示本设备准备好可接收数据，低电平有效，低电平说明本设备可以接收数据。\nCTS （Clear ToSend，发送允许）为输入信号，用于判断是否可以向对方发送数据，低电平有效，低电平说明本设备可以向对方发送数据。\n","permalink":"https://fan-pengfei.top/posts/uart%E4%B8%AD%E7%9A%84%E7%A1%AC%E4%BB%B6%E6%B5%81%E6%8E%A7cts%E5%92%8Crts/","summary":"\u003cblockquote\u003e\n\u003cp\u003e昨天花了一个USB转TTL小板子，其中芯片引脚有RT和CTS，之前从没用过，还是查找资料了解一下吧。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eRTS （Require ToSend，发送请求）为输出信号，用于指示本设备准备好可接收数据，低电平有效，低电平说明本设备可以接收数据。\u003c/p\u003e\n\u003cp\u003eCTS （Clear ToSend，发送允许）为输入信号，用于判断是否可以向对方发送数据，低电平有效，低电平说明本设备可以向对方发送数据。\u003c/p\u003e","title":"UART中的硬件流控CTS和RTS"},{"content":" 用Markdown编辑数学公式看起来会很舒服，学一下；\n希腊字母： 常见希腊字母： $\\alpha$、$\\beta$、$\\chi$、$\\Delta$、$\\Gamma$、$\\Theta$ $$ \\alpha\\beta\\chi $$ 显示效果为：\n\\alpha\\beta\\chi\\Delta\\Gamma\\Theta\n注：当希腊字母的LaTex语法首字母大写时，即输出大写的希腊字母；首字母小写时，输出小写的希腊字母。\n数学结构： 分数： $\\frac{abc}{xyz}$ 显示效果为：\n\\frac{abc}{xyz}\n根号： $$ \\frac{\\sqrt{1+abc}}{\\sqrt{1-abc}} $$ 显示效果为：\n\\frac{\\sqrt{1+abc}}{\\sqrt{1-abc}}\n向量符号： $\\overrightarrow{F}$ 显示效果为：\n\\overrightarrow{F}\n定界符： $|$、$\\|$、$\\Uparrow$ 显示效果为：\n|、|、\\Uparrow\n注：将上述定界符与\\left和right组合使用可以使得定界符匹配其内容的高度。\n比如要构建一个如下的矩阵的行列式。\n$$ \\left|\\begin{matrix} 1 \u0026amp; 2 \u0026amp; 3 \\\\ 4 \u0026amp; 5 \u0026amp; 6 \\\\ 7 \u0026amp; 8 \u0026amp; 9 \\end{matrix} \\right| $$ 显示效果如下：\n\\left|\\begin{matrix} 1 \u0026amp; 2 \u0026amp; 3 \\ 4 \u0026amp; 5 \u0026amp; 6 \\ 7 \u0026amp; 8 \u0026amp; 9 \\end{matrix} \\right|\n可变大小的符号 $\\sum$、$\\int$、$\\oint$、$\\iint$ $$ \\bigcap\\bigcup\\bigoplus\\bigotimes $$ 显示效果如下：\n\\sum\\int\\oint\\iint\\bigcap\\bigcup\\bigoplus\\bigotimes\n函数名称： $\\sin$、$\\cos$、$\\tan$、$\\log$ $$ \\tan(at-n\\pi) $$ 显示效果如下：\n\\tan(at-n\\pi)\n二进制运算符和关系运算符 $\\times$、$\\ast$、$\\div$、$\\pm$、$\\mp$、$\\leq$、$\\geq$、$\\lessgtr$ 显示效果如下：\n\\times\\ast\\div\\pm\\mp\\leq\\geq\\lessgtr\n上下标 在符号的后面打下划线_，那么下划线后面的符号就自动放在了下标的位置；\n上标就是次方符号^；\n如果要同时打出上下标，直接连续输入即可。并且上、下标的输入顺序是无所谓的；\n如果上下标的内容由多个字符组成，那么就必须要加上花括号。这是因为上下标符号后面只默认第一个字符为上下标内容；\n左边的上下标，直接把上下标的内容左边即可；\n举例：\nT^t T_t T_t^r T_{tt} T^t T_t T_t^r T_{tt}\n其他例子 由以下公式：\n^{i-1}iT=Rot(x,\\alpha{i-1})*Trans(\\alpha_{i-1},0,0)*Rot(z,\\theta_i)*Trans(0,0,d_i) ^{i-1}_iT= \\left [\\begin{array}{c} cos\\theta_i \u0026amp;-sin\\theta_icos\\alpha_i \u0026amp;sin\\theta_isin\\alpha_i \u0026amp;\\alpha_icos\\theta_i \\ sin\\theta_i \u0026amp;cos\\theta_isin\\alpha_i \u0026amp;-cos\\theta_isin\\alpha_i \u0026amp;\\alpha_isin\\theta_i \\ 0 \u0026amp;sin\\alpha_i \u0026amp;cos\\alpha_i \u0026amp;d_i \\ 0 \u0026amp;0 \u0026amp;0 \u0026amp;1 \\end{array}\\right] 可以得到^0_1T、^1_2T、^2_3T、^3_4T、^4_5T、^5_6T的计算公式；\n然后由公式：\n^0_6T=^0_1T*^1_2T*^2_3T*^3_4T*^4_5T*^5_6T= \\left [\\begin{array}{c} n_x \u0026amp;o_x \u0026amp;a_x \u0026amp;p_x \\ n_y \u0026amp;o_y \u0026amp;a_y \u0026amp;p_y\\ n_z \u0026amp;o_z \u0026amp;a_z \u0026amp;p_z\\ 0 \u0026amp;0 \u0026amp;0 \u0026amp;1 \\end{array}\\right] 可以计算得出^0_6T的值即可；\n使用MATLAB计算得到的结果为：\n^0_6T= \\left [\\begin{array}{r} 0.000 \u0026amp;-1.000 \u0026amp;0.000 \u0026amp;0.1639 \\ 1.000 \u0026amp;-1.000 \u0026amp;0.000 \u0026amp;-0.7277\\ 0.000 \u0026amp;1.000 \u0026amp;0.000 \u0026amp;-0.3529\\ 0.000 \u0026amp;0.000 \u0026amp;0.000 \u0026amp;1.0000 \\end{array}\\right] 从末端姿态矩阵^0_6T可以知道机器人的末端位置分量：\n(p_x,p_y,p_z)=(0.1639,-0.7277,-0.3529) 按照关节角度[90°, 0°, 90°, 180°, 90°, 90°]来控制仿真程序中机器人得到的仿真结果如下图所示：\nHexo公式渲染问题： hexo LaTeX渲染问题\n","permalink":"https://fan-pengfei.top/posts/markdown%E7%BC%96%E8%BE%91%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003e用Markdown编辑数学公式看起来会很舒服，学一下；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"希腊字母\"\u003e希腊字母：\u003c/h2\u003e\n\u003ch3 id=\"常见希腊字母\"\u003e常见希腊字母：\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/markdown%E7%BC%96%E8%BE%91%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F/img-1.jpg\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$\\alpha$、$\\beta$、$\\chi$、$\\Delta$、$\\Gamma$、$\\Theta$\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\\alpha\\beta\\chi\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果为：\u003c/p\u003e\n\u003cp\u003e\\alpha\\beta\\chi\\Delta\\Gamma\\Theta\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注：当希腊字母的LaTex语法首字母大写时，即输出大写的希腊字母；首字母小写时，输出小写的希腊字母。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"数学结构\"\u003e数学结构：\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/markdown%E7%BC%96%E8%BE%91%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F/img-2.jpg\"\u003e\u003c/p\u003e\n\u003ch3 id=\"分数\"\u003e分数：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$\\frac{abc}{xyz}$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果为：\u003c/p\u003e\n\u003cp\u003e\\frac{abc}{xyz}\u003c/p\u003e\n\u003ch3 id=\"根号\"\u003e根号：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\\frac{\\sqrt{1+abc}}{\\sqrt{1-abc}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果为：\u003c/p\u003e\n\u003cp\u003e\\frac{\\sqrt{1+abc}}{\\sqrt{1-abc}}\u003c/p\u003e\n\u003ch3 id=\"向量符号\"\u003e向量符号：\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/markdown%E7%BC%96%E8%BE%91%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F/img-3.png\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$\\overrightarrow{F}$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果为：\u003c/p\u003e\n\u003cp\u003e\\overrightarrow{F}\u003c/p\u003e\n\u003ch2 id=\"定界符\"\u003e定界符：\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/markdown%E7%BC%96%E8%BE%91%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F/img-4.png\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$|$、$\\|$、$\\Uparrow$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果为：\u003c/p\u003e\n\u003cp\u003e|、|、\\Uparrow\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注：将上述定界符与\u003ccode\u003e\\left\u003c/code\u003e和\u003ccode\u003eright\u003c/code\u003e组合使用可以使得定界符匹配其内容的高度。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e比如要构建一个如下的矩阵的行列式。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\\left|\\begin{matrix}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   1 \u0026amp; 2 \u0026amp; 3 \\\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   4 \u0026amp; 5 \u0026amp; 6 \\\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   7 \u0026amp; 8 \u0026amp; 9\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \\end{matrix} \\right|\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$$\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e显示效果如下：\u003c/p\u003e\n\u003cp\u003e\\left|\\begin{matrix}\n1 \u0026amp; 2 \u0026amp; 3 \\\n4 \u0026amp; 5 \u0026amp; 6 \\\n7 \u0026amp; 8 \u0026amp; 9\n\\end{matrix} \\right|\u003c/p\u003e","title":"markdown编辑数学公式"},{"content":" 记录一下刷题中遇到的知识点；\n1、(断言)assert 在程序设计中，断言（assertion）是一种放在程序中的一阶逻辑（如一个结果为真或是假的逻辑判断式），目的是为了标示与验证程序开发者预期的结果－当程序执行到断言的位置时，对应的断言应该为真。若断言不为真时，程序会中止执行，并给出错误消息。\n","permalink":"https://fan-pengfei.top/posts/%E5%88%B7%E9%A2%98%E4%B8%AD%E7%A2%B0%E5%88%B0%E7%9A%84%E7%9F%A5%E8%AF%86%E7%82%B9/","summary":"\u003cblockquote\u003e\n\u003cp\u003e记录一下刷题中遇到的知识点；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1断言assert\"\u003e1、(断言)assert\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e在程序设计中，断言（assertion）是一种放在程序中的一阶逻辑（如一个结果为真或是假的逻辑判断式），目的是为了标示与验证程序开发者预期的结果－当程序执行到断言的位置时，对应的断言应该为真。若断言不为真时，程序会中止执行，并给出错误消息。\u003c/strong\u003e\u003c/p\u003e","title":"刷题中碰到的知识点"},{"content":" 异或的常见用法；\nXOR用法 一、运算真值表： 0 ^ 0 = 0 0 ^ 1 = 1 1 ^ 0 = 1 1 ^ 1 = 0 二、运算定理： 一个值与自身的运算，总是为 false。 x ^ x = 0 一个值与 0 的运算，总是等于其本身。 x ^ 0 = x 可交换性 x ^ y = y ^ x 结合性 x ^ (y ^ z) = (x ^ y) ^ z 三、应用： 1、简化运算； 多个值的异或运算，可以根据运算定律进行简化。 a ^ b ^ c ^ a ^ b = a ^ a ^ b ^ b ^ c = 0 ^ 0 ^ c = c 2、交换值； 两个变量连续进行三次异或运算，可以互相交换值。 假设两个变量是x和y，各自的值是a和b。下面就是x和y进行三次异或运算，注释部分是每次运算后两个变量的值。 x = x ^ y // (a ^ b, b) y = x ^ y // (a ^ b, a ^ b ^ b) =\u0026gt; (a ^ b, a) x = x ^ y // (a ^ b ^ a, a) =\u0026gt; (b, a) 这是两个变量交换值的最快方法，不需要任何额外的空间。\n3、加密； 异或运算可以用于加密。 第一步，明文（text）与密钥（key）进行异或运算，可以得到密文（cipherText）。 text ^ key = cipherText 第二步，密文与密钥再次进行异或运算，就可以还原成明文。\ncipherText ^ key = text 原理很简单，如果明文是 x，密钥是 y，那么 x 连续与 y 进行两次异或运算，得到自身。\n(x ^ y) ^ y = x ^ (y ^ y) = x ^ 0 = x 4、数据备份； 异或运算可以用于数据备份。 文件 x 和文件 y 进行异或运算，产生一个备份文件 z。 x ^ y = z 以后，无论是文件 x 或文件 y 损坏，只要不是两个原始文件同时损坏，就能根据另一个文件和备份文件，进行还原。\nx ^ z = x ^ (x ^ y) = (x ^ x) ^ y = 0 ^ y = y 上面的例子是 y 损坏，x 和 z 进行异或运算，就能得到 y。\n四、一道面试题目： 一些面试的算法题，也能使用异或运算快速求解。\n请看下面这道题。\n一个数组包含 n-1 个成员，这些成员是 1 到 n 之间的整数，且没有重复，请找出缺少的那个数字。\n最快的解答方法，就是把所有数组成员（A[0] 一直到 A[n-2]）与 1 到 n 的整数全部放在一起，进行异或运算。\nA[0] ^ A[1] ^ ... ^ A[n-2] ^ 1 ^ 2 ^ ... ^ n 上面这个式子中，每个数组成员都会出现两次，相同的值进行异或运算就会得到 0。只有缺少的那个数字出现一次，所以最后得到的就是这个值。\n你可能想到了，加法也可以解这道题。\n1 + 2 + ... + n - A[0] - A[1] - ... - A[n-2] 但是，加法的速度没有异或运算快，而且需要额外的空间。如果数字比较大，还有溢出的可能。\n","permalink":"https://fan-pengfei.top/posts/xor%E7%94%A8%E6%B3%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e异或的常见用法；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"xor用法\"\u003eXOR用法\u003c/h1\u003e\n\u003ch2 id=\"一运算真值表\"\u003e一、运算真值表：\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003ch2 id=\"二运算定理\"\u003e二、运算定理：\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e一个值与自身的运算，总是为 false。\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e一个值与 0 的运算，总是等于其本身。\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e可交换性\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e结合性\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ez\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ez\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003ch2 id=\"三应用\"\u003e三、应用：\u003c/h2\u003e\n\u003ch3 id=\"1简化运算\"\u003e1、简化运算；\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e多个值的异或运算，可以根据运算定律进行简化。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eb\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eb\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eb\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eb\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003ch3 id=\"2交换值\"\u003e2、交换值；\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e两个变量连续进行三次异或运算，可以互相交换值。\n假设两个变量是\u003ccode\u003ex\u003c/code\u003e和\u003ccode\u003ey\u003c/code\u003e，各自的值是\u003ccode\u003ea\u003c/code\u003e和\u003ccode\u003eb\u003c/code\u003e。下面就是\u003ccode\u003ex\u003c/code\u003e和\u003ccode\u003ey\u003c/code\u003e进行三次异或运算，注释部分是每次运算后两个变量的值。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// (a ^ b, b)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// (a ^ b, a ^ b ^ b) =\u0026gt; (a ^ b, a)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// (a ^ b ^ a, a) =\u0026gt; (b, a)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cp\u003e这是两个变量交换值的最快方法，不需要任何额外的空间。\u003c/p\u003e\n\u003ch3 id=\"3加密\"\u003e3、加密；\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e异或运算可以用于加密。\n第一步，明文（text）与密钥（key）进行异或运算，可以得到密文（cipherText）。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003etext\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e^\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecipherText\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cp\u003e第二步，密文与密钥再次进行异或运算，就可以还原成明文。\u003c/p\u003e","title":"XOR用法"},{"content":" 转自知乎，如何做到有用的学习；\n问题描述：为什么我的成绩那么好，最终还是成了一个没用的人？\n作者：东坡夜奔 链接：https://www.zhihu.com/question/30375123/answer/48170636 来源：知乎 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。\n基本问题： 说两个问题。一是知识空白的问题。二是知识技能化的问题。\n1、知识空白 我们初高中（甚至大学）学到的知识，是教育部根据我国整体教育情况，考虑成本和国家工业化的需求，制定的一整套学习计划。\n说白了，目的很简单：就是用尽可能低的成本，培养出对于推进我国工业化和现代化足够多的人才。\n对于每个人个人的具体情况，对于一个人的人格情感培养，对于毕业后在社会上的就业，对于一个人的自我实现，基本上都没有考虑。\n它是一个整体考量，不是个体考量。\n所以，你会有很多知识空白。\n很多对于你自己人生成长成才所必须的知识，是空白的，尤其对于出身小地方或者农村的孩子们，学校教育几乎是唯一接受教育的途径，这种情况就更加严重。\n情感教育，心理教育，人格教育，美学，人际交往知识，良好的生活习惯……\n都没有，都是空白。\n再加上城市与农村的差异带来的冲击，学校和社会的差异带来的冲击，面临这样的心理困境，是正常的，不必过于焦虑。——这些东西，我们的教育中也是空白。\n这些重要的东西，经济发达物质基础好，父母见识广的地方，可能可以通过家庭教育进行弥补（他们的问题本来就少），但鉴于目前的经济和教育水平，在我国绝大多数地区，都做不到。\n我大学之后陷入困境，某种程度上，就是因为这些缺失的知识。\n比如心理学，比如思维科学，最重要的两门课，如何保持心理康健，如何正确地进行思考，我们的学校教育中都是空白。\n随着经济的不断发展，随着整个社会对于个体的逐渐重视，这个情况会有所好转，但是，我们毕竟来不及了。\n所以延伸出来的，就是自我教育的问题。\n好在你还年轻，会读书，会工作，会上网，会来知乎学习，会自我教育，就不算晚。\n2013年11月3日，我在某笔记里写下一篇名为《五年多的成长》的日记，里面写到毕业后才意识到的，自己所面临的困惑。\n六种困惑，其实都是知识的空白而已。\n1、男女之惑——对于情感的知识\n2、心理之惑——心理学\n3、性格之惑——我是什么性格以及为什么是这个性格\n4、方法之惑——为什么一腔真心、无数努力却做不成事\n5、自我之惑——我是不是没有自我，为什么没有自我，如何建立自我\n6、方向之惑——未来要怎么走\n其实这些问题，通过心理学和思维科学的学习，在毕业之后的五年里，我基本都有了较为清晰的阶段性认识。\n问题是，在之前的22年里，我不知道这些知识。\n自我教育，毕业之后的自我教育，才是人生成长的关键。\n这是对知识空白问题的解决。\n2、知识技能化 我们学校中学习的，都是知识，但对于个人来说，知识不转化为技能，就无用。\n知识当然是伟大的，那是人类百万年进化的结晶，但是，我们必须意识到，你的知识只有转化为技能，才拥有了价值。\n你的价值，附着于你的技能之上。\n你是律师，你要会打官司，能打赢官司，才能赚到钱，体现个人价值。\n你是医生，你要会治病，能治好病，才能赚到工资，体现个人价值。\n你是警察，你要会维持治安，打击犯罪，才能赚到工资，体现个人价值。\n……\n你说我有知识，但是我什么都不干，那就只好饿死，因为你没有通过技能体现出你的价值来。\n当年明月研究了多年明史，如果他不写出《明朝那些事儿》，就无法证明自己的价值。——他把明史的知识技能化了——转变成了一本书。\n郎朗学习了多年钢琴，如果他不去参加比赛获奖不去表演，就无法证明自己的价值。——他把钢琴弹奏技能化了——转变为一首首曲子。\n你要把你学习到的知识转化为对这个社会有价值的技能，这才有用。\n还是自己的例子，经过三年的锻炼，我基本上掌握了公文写作的技能，所以现在很崩溃，写稿子写讲话稿写的脑子想炸了，但是我的知识（文字知识，政治知识，写作知识，专业知识，部门知识……）转化为了技能（公文写作）。\n所以，你就可以靠技能吃饭了，有了这个技能，虽然并不是多大的笔杆子，但毕竟有了自己的利用价值，所以领导对自己还不错，提拔的时候也会考虑，不至于让你做凉板凳坐很久。\n家有万贯不如一技傍身，就是这个意思。\n你学了那么多知识，可否能够转化为某种对这个社会、对别人有价值的技能？\n技能远大于知识。\n技能能吃饭，知识不行。\n技能能发家，知识不行。\n切记切记，要将知识转化为技能。\n比如：你看了那么多电影，一肚子电影的知识，可有没有想过写剧本？\n写剧本，就是技能。\n另：会考试也是一个技能啊，而且是很厉害的技能，一次没考上怕什么，多考几次就是了。\n延伸问题： 技能的门槛。 你说我有技能啊，为什么还是没价值？\n可能是你自己的技能没有建立高门槛，随便就被别人比下去了。\n这是个高度竞争的社会。\n二八法则。\n所谓20%的人赚走80%的利润，就是因为人家建立了高门槛。\n1、高门槛不一定需要知识，人家资本雄厚，同样是高门槛，人家脸皮够厚，同样是高门槛，知识只是一个参考因素。\n2、在知识—技能这个维度，建立高门槛需要精确定位市场、研究规律、不断实践、积累经验、建立细节体系——建立你自己的高门槛，就能享受相对高的回报，因为你的价值也是相对高的。——这需要时间，需要思考，需要不断改进、需要耐心。\n3、耐心本身就是一个高门槛，这个世界上，聪明人很多，耐心的人，却真的不多。\n4、知识很重要，要充分利用（别小看考试能力，你知道多少领导家孩子想要进公务员系统却考不进来吗？），尤其是当你只拥有知识的时候，但问题是，不要过于夸大知识的作用，而且，你所拥有的知识，并不一定有价值。\n你要掌握真正有用的知识，将其技能化，耐心地建立高门槛，这才是重要的。\n再说一遍吧。\n你要认识自己，分析自己的成败，弥补自己的知识空白，掌握真正对自己成长、成才有用的知识，将其技能化，耐心地建立高门槛，这才是重要的。\n","permalink":"https://fan-pengfei.top/posts/%E5%A6%82%E4%BD%95%E5%81%9A%E5%88%B0%E6%9C%89%E7%94%A8%E7%9A%84%E5%AD%A6%E4%B9%A0/","summary":"\u003cblockquote\u003e\n\u003cp\u003e转自知乎，如何做到有用的学习；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e问题描述：为什么我的成绩那么好，最终还是成了一个没用的人？\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e作者：东坡夜奔  链接：\u003ca href=\"https://www.zhihu.com/question/30375123/answer/48170636\"\u003ehttps://www.zhihu.com/question/30375123/answer/48170636\u003c/a\u003e  来源：知乎  著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"基本问题\"\u003e基本问题：\u003c/h2\u003e\n\u003cp\u003e说两个问题。一是\u003cstrong\u003e知识空白\u003c/strong\u003e的问题。二是\u003cstrong\u003e知识技能化\u003c/strong\u003e的问题。\u003c/p\u003e\n\u003ch3 id=\"1知识空白\"\u003e1、知识空白\u003c/h3\u003e\n\u003cp\u003e我们初高中（甚至大学）学到的知识，是教育部根据我国整体教育情况，考虑\u003cstrong\u003e成本\u003c/strong\u003e和\u003cstrong\u003e国家工业化的需求\u003c/strong\u003e，制定的一整套学习计划。\u003c/p\u003e\n\u003cp\u003e说白了，目的很简单：就是用尽可能低的成本，培养出对于推进我国工业化和现代化足够多的\u003cstrong\u003e人才。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e对于每个人个人的具体情况，对于一个人的人格情感培养，对于毕业后在社会上的就业，对于一个人的自我实现，\u003cstrong\u003e基本上都没有考虑。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e它是一个整体考量，不是个体考量。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e所以，你会有很多\u003cstrong\u003e知识空白。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e很多对于你自己人生成长成才\u003cstrong\u003e所必须的知识\u003c/strong\u003e，是\u003cstrong\u003e空白的\u003c/strong\u003e，尤其对于出身小地方或者农村的孩子们，学校教育几乎是唯一接受教育的途径，这种情况就更加严重。\u003c/p\u003e\n\u003cp\u003e情感教育，心理教育，人格教育，美学，人际交往知识，良好的生活习惯……\u003c/p\u003e\n\u003cp\u003e都没有，都是空白。\u003c/p\u003e\n\u003cp\u003e再加上城市与农村的差异带来的冲击，学校和社会的差异带来的冲击，面临这样的心理困境，是正常的，不必过于焦虑。——这些东西，我们的教育中也是空白。\u003c/p\u003e\n\u003cp\u003e这些重要的东西，经济发达物质基础好，父母见识广的地方，可能可以通过家庭教育进行弥补（他们的问题本来就少），但鉴于目前的经济和教育水平，在我国绝大多数地区，都做不到。\u003c/p\u003e\n\u003cp\u003e我大学之后陷入困境，某种程度上，就是因为这些缺失的知识。\u003c/p\u003e\n\u003cp\u003e比如\u003cstrong\u003e心理学\u003c/strong\u003e，比如\u003cstrong\u003e思维科学\u003c/strong\u003e，最重要的两门课，如何保持心理康健，如何正确地进行思考，我们的学校教育中都是空白。\u003c/p\u003e\n\u003cp\u003e随着经济的不断发展，随着整个社会对于个体的逐渐重视，这个情况会有所好转，但是，我们毕竟来不及了。\u003c/p\u003e\n\u003cp\u003e所以延伸出来的，就是\u003cstrong\u003e自我教育\u003c/strong\u003e的问题。\u003c/p\u003e\n\u003cp\u003e好在你还年轻，会读书，会工作，会上网，会来知乎学习，\u003cstrong\u003e会自我教育，就不算晚。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e2013年11月3日，我在某笔记里写下一篇名为《五年多的成长》的日记，里面写到毕业后才意识到的，自己所面临的困惑。\u003c/p\u003e\n\u003cp\u003e六种困惑，其实都是知识的空白而已。\u003c/p\u003e\n\u003cp\u003e1、男女之惑——对于情感的知识\u003c/p\u003e\n\u003cp\u003e2、心理之惑——心理学\u003c/p\u003e\n\u003cp\u003e3、性格之惑——我是什么性格以及为什么是这个性格\u003c/p\u003e\n\u003cp\u003e4、方法之惑——为什么一腔真心、无数努力却做不成事\u003c/p\u003e\n\u003cp\u003e5、自我之惑——我是不是没有自我，为什么没有自我，如何建立自我\u003c/p\u003e\n\u003cp\u003e6、方向之惑——未来要怎么走\u003c/p\u003e\n\u003cp\u003e其实这些问题，通过心理学和思维科学的学习，在毕业之后的五年里，我基本都有了较为清晰的\u003cstrong\u003e阶段性认识。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e问题是，在之前的22年里，我不知道这些知识。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e自我教育，毕业之后的自我教育，才是人生成长的关键。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e这是对知识空白问题的解决。\u003c/p\u003e\n\u003ch3 id=\"2知识技能化\"\u003e2、知识技能化\u003c/h3\u003e\n\u003cp\u003e我们学校中学习的，都是知识，但对于个人来说，\u003cstrong\u003e知识不转化为技能，就无用。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e知识当然是伟大的，那是人类百万年进化的结晶，但是，我们必须意识到，你的知识只有转化为技能，才拥有了价值。\u003c/p\u003e\n\u003cp\u003e你的价值，附着于你的技能之上。\u003c/p\u003e\n\u003cp\u003e你是律师，你要会打官司，能打赢官司，才能赚到钱，体现个人价值。\u003c/p\u003e\n\u003cp\u003e你是医生，你要会治病，能治好病，才能赚到工资，体现个人价值。\u003c/p\u003e\n\u003cp\u003e你是警察，你要会维持治安，打击犯罪，才能赚到工资，体现个人价值。\u003c/p\u003e\n\u003cp\u003e……\u003c/p\u003e\n\u003cp\u003e你说我有知识，但是我什么都不干，那就只好饿死，因为你没有\u003cstrong\u003e通过技能体现出你的价值\u003c/strong\u003e来。\u003c/p\u003e\n\u003cp\u003e当年明月研究了多年明史，如果他不写出《明朝那些事儿》，就无法证明自己的价值。——他把明史的知识\u003cstrong\u003e技能化\u003c/strong\u003e了——转变成了一本书。\u003c/p\u003e\n\u003cp\u003e郎朗学习了多年钢琴，如果他不去参加比赛获奖不去表演，就无法证明自己的价值。——他把钢琴弹奏\u003cstrong\u003e技能化\u003c/strong\u003e了——转变为一首首曲子。\u003c/p\u003e\n\u003cp\u003e你要\u003cstrong\u003e把你学习到的知识转化为对这个社会有价值的技能\u003c/strong\u003e，这才有用。\u003c/p\u003e\n\u003cp\u003e还是自己的例子，经过三年的锻炼，我基本上掌握了\u003cstrong\u003e公文写作的技能\u003c/strong\u003e，所以现在很崩溃，写稿子写讲话稿写的脑子想炸了，但是我的知识（文字知识，政治知识，写作知识，专业知识，部门知识……）转化为了技能（公文写作）。\u003c/p\u003e\n\u003cp\u003e所以，你就可以靠技能吃饭了，有了这个技能，虽然并不是多大的笔杆子，但毕竟有了自己的利用价值，所以领导对自己还不错，提拔的时候也会考虑，不至于让你做凉板凳坐很久。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e家有万贯不如一技傍身\u003c/strong\u003e，就是这个意思。\u003c/p\u003e\n\u003cp\u003e你学了那么多知识，可否能够转化为某种对这个社会、对别人有价值的技能？\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e技能远大于知识。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e技能能吃饭，知识不行。\u003c/p\u003e\n\u003cp\u003e技能能发家，知识不行。\u003c/p\u003e\n\u003cp\u003e切记切记，要将知识转化为技能。\u003c/p\u003e\n\u003cp\u003e比如：你看了那么多电影，一肚子电影的知识，可有没有想过写剧本？\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e写剧本，就是技能。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e另：会考试也是一个技能啊，而且是很厉害的技能，一次没考上怕什么，多考几次就是了。\u003c/p\u003e\n\u003ch2 id=\"延伸问题\"\u003e延伸问题：\u003c/h2\u003e\n\u003ch3 id=\"技能的门槛\"\u003e技能的门槛。\u003c/h3\u003e\n\u003cp\u003e你说我有技能啊，为什么还是没价值？\u003c/p\u003e\n\u003cp\u003e可能是你自己的技能\u003cstrong\u003e没有建立高门槛\u003c/strong\u003e，随便就被别人比下去了。\u003c/p\u003e\n\u003cp\u003e这是个\u003cstrong\u003e高度竞争\u003c/strong\u003e的社会。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e二八法则。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e所谓20%的人赚走80%的利润，就是因为人家建立了高门槛。\u003c/p\u003e\n\u003cp\u003e1、\u003cstrong\u003e高门槛不一定需要知识\u003c/strong\u003e，人家资本雄厚，同样是高门槛，人家脸皮够厚，同样是高门槛，知识只是一个参考因素。\u003c/p\u003e\n\u003cp\u003e2、在知识—技能这个维度，建立高门槛需要\u003cstrong\u003e精确定位市场、研究规律、不断实践、积累经验、建立细节体系\u003c/strong\u003e——建立你自己的高门槛，就能享受相对高的回报，因为你的价值也是相对高的。——这需要时间，需要思考，需要不断改进、需要耐心。\u003c/p\u003e\n\u003cp\u003e3、\u003cstrong\u003e耐心本身就是一个高门槛\u003c/strong\u003e，这个世界上，聪明人很多，耐心的人，却真的不多。\u003c/p\u003e\n\u003cp\u003e4、知识很重要，要充分利用（别小看考试能力，你知道多少领导家孩子想要进公务员系统却考不进来吗？），尤其是当你只拥有知识的时候，但问题是，\u003cstrong\u003e不要过于夸大知识的作用\u003c/strong\u003e，而且，你所拥有的知识，并不一定有价值。\u003c/p\u003e","title":"如何做到有用的学习"},{"content":" 到今天为止（2022\\08\\07），我的暑假实习就要接近尾声了，我想，记录一下我这一个多月的实习经历吧，毕竟这是自己的第一份正式工作；\n","permalink":"https://fan-pengfei.top/posts/%E5%AE%9E%E4%B9%A0%E8%AE%B0%E5%BD%95%E4%B8%8E%E6%80%BB%E7%BB%93/","summary":"\u003cblockquote\u003e\n\u003cp\u003e到今天为止（2022\\08\\07），我的暑假实习就要接近尾声了，我想，记录一下我这一个多月的实习经历吧，毕竟这是自己的第一份正式工作；\u003c/p\u003e\n\u003c/blockquote\u003e","title":"实习记录与总结"},{"content":" Google的网站上有一个网页，叫做“我们的哲学”（Our Philosophy），上面列出了十句话。\n1. 用户第一，然后其他事情就会发生。\n（Focus on the user and all else will follow.）\n2. 最好的方法就是把一件事情做到非常、非常好。\n（It’s best to do one thing really, really well.）\n不要在乎自己现在的状况，如果你不断地正确地做一件事情，总有一天会有不一样的结果。\n3. 快比慢好。\n（Fast is better than slow.） “我们也许是世界上唯一一家希望用户尽快离开自己网站的公司：最快地给出用户想要的结果，一个多余的字节也没有，并且还在致力于变得更快。”\n4. 民主在网上很管用。\n（Democracy on the web works.） 相信民主的人在互联网上能获得成功，相信管制和独裁的人在互联网上必将遭到失败。\n5. 要寻找答案，不一定需要办公桌。\n（You don’t need to be at your desk to need an answer.）\n6. 不干坏事，也能挣到钱。\n（You can make money without doing evil.） 当考验来临时，要将信念置于金钱之上。坚信人生中有比金钱和物质更重要的东西，更加坚信这样做的人最终会赢得信赖和回报。\n7. 未知的信息总是存在的。\n（There’s always more information out there.）\n8. 对信息的需求无所不在。\n（The need for information crosses all borders.）\n9. 不穿西装，不代表你不专业。\n（You can be serious without a suit.） 相信他人，尊重他人，并且为那些愿意做出贡献的人们提供适当的环境和工具，他们一定不会辜负期望。\n10. 只是优秀还不够。\n（Great just isn’t good enough.） 不要满足于成为Number One，要力争成为Only One。 参考网站：参考；\n","permalink":"https://fan-pengfei.top/posts/%E8%B0%B7%E6%AD%8C%E5%8F%91%E7%8E%B0%E7%9A%84%E5%8D%81%E6%9D%A1%E7%9C%9F%E7%90%86/","summary":"\u003cblockquote\u003e\n\u003cp\u003eGoogle的网站上有一个网页，叫做\u003ca href=\"https://www.google.com/corporate/tenthings.html\"\u003e“我们的哲学”\u003c/a\u003e（Our Philosophy），上面列出了十句话。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e1. 用户第一，然后其他事情就会发生。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（Focus on the user and all else will follow.）\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e2. 最好的方法就是把一件事情做到非常、非常好。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（It’s best to do one thing really, really well.）\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e不要在乎自己现在的状况，如果你不断地正确地做一件事情，总有一天会有不一样的结果。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. 快比慢好。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（Fast is better than slow.）\n“我们也许是世界上唯一一家希望用户尽快离开自己网站的公司：最快地给出用户想要的结果，一个多余的字节也没有，并且还在致力于变得更快。”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e4. 民主在网上很管用。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（Democracy on the web works.）\n相信民主的人在互联网上能获得成功，相信管制和独裁的人在互联网上必将遭到失败。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e5. 要寻找答案，不一定需要办公桌。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（You don’t need to be at your desk to need an answer.）\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e6. 不干坏事，也能挣到钱。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（You can make money without doing evil.）\n当考验来临时，要将信念置于金钱之上。坚信人生中有比金钱和物质更重要的东西，更加坚信这样做的人最终会赢得信赖和回报。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e7. 未知的信息总是存在的。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e（There’s always more information out there.）\u003c/p\u003e","title":"谷歌发现的十条真理"},{"content":" 自己虽然是写程序的，但是专业是自动化，毕竟不是计算机科班出身，还是要补一补计算机基础的；\n计算机基础：\n计算机组成原理； 《深入理解计算机原理》\n操作系统； 《操作系统导论》\n计算机网络； 《计算机网络：自顶向下方法》\n数据结构与算法；\n编译原理；\n学习过程记录： 先学习计算机网络，正好视频跟课本是配套的：\n参考B站视频，争取一个月内补全计算机网络相关的知识；\n链接：https://www.bilibili.com/video/BV1JV411t7ow?spm_id_from=333.337.search-card.all.click\u0026amp;vd_source=d3e0d33e56175cb1e5a020e1a154d370 到今天为止，把计算机网络的内容过了一遍，满打满算花了一个月吧，后续肯定还需要更多的时间去消化这些知识内容；\n后续看一下计算机系统整体相关的知识吧，大四这一年应该是难得的知识摄入的时间。\n","permalink":"https://fan-pengfei.top/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80%E8%A1%A5%E5%85%A8%E8%AE%A1%E5%88%92/","summary":"\u003cblockquote\u003e\n\u003cp\u003e自己虽然是写程序的，但是专业是自动化，毕竟不是计算机科班出身，还是要补一补计算机基础的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e计算机基础：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e计算机组成原理；\u003c/strong\u003e\n《深入理解计算机原理》\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e操作系统；\u003c/strong\u003e\n《操作系统导论》\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e计算机网络；\u003c/strong\u003e\n《计算机网络：自顶向下方法》\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e数据结构与算法；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e编译原理；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"学习过程记录\"\u003e学习过程记录：\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e先学习计算机网络，正好视频跟课本是配套的：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e参考B站视频，争取一个月内补全计算机网络相关的知识；\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e链接：\u003ca href=\"https://www.bilibili.com/video/BV1JV411t7ow?spm_id_from=333.337.search-card.all.click\u0026amp;vd_source=d3e0d33e56175cb1e5a020e1a154d370\"\u003ehttps://www.bilibili.com/video/BV1JV411t7ow?spm_id_from=333.337.search-card.all.click\u0026amp;vd_source=d3e0d33e56175cb1e5a020e1a154d370\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e到今天为止，把计算机网络的内容过了一遍，满打满算花了一个月吧，后续肯定还需要更多的时间去消化这些知识内容；\u003c/p\u003e\n\u003cp\u003e后续看一下计算机系统整体相关的知识吧，大四这一年应该是难得的知识摄入的时间。\u003c/p\u003e","title":"计算机基础补全计划"},{"content":" 昨天收到了大疆的录用意向offer，很开心，从四月投递简历，到终面结束，再到昨天收到意向offer，自己花了很多的时间和精力去修改简历和准备面试，终于还是等到了；\n关于DJI面试 收到意向offer的时候，我正在海康的三楼餐厅吃饭，看到是深圳的电话，我就猜到是大疆的HR打来的；\n我很早就听说过大疆，那时候只是知道大疆是一家做无人机的公司，更多的就没有什么了解了；\n大二开学，我报名参加了我们学校的RoboMaster战队，在队内总共呆了一年，主要是负责哨兵机器人的嵌入式开发；说来惭愧，我并没有为我们队伍做出过什么大的贡献，自己也没有做什么创新性的东西，充其量我的表现是中规中矩，大多数时候都是在补充自己的知识储备，不过正是通过这个比赛我才更近距离地接触到大疆，也认识了挺多学长学姐，这对我的影响很大；\n在备赛过程中，我用过大疆的无刷电机，很强，功能很强，控制尤其方便，还勾起了我对无刷电机控制的兴趣，学了一点FOC，然后用开源的Simple FOC，做了一个简单的无刷电机驱动器，很有意思，在大疆的面试中，面试官问我：“你能不能说一个你用过的大疆的产品的一个缺点？”，我就提到了这件事，并说了一个我认为大疆的无刷电机所存在的缺点。\n我只去过一次比赛现场，感受过那种赛场的氛围，很难忘的经历，在面试中，面试官问了我一个问题：\n“你对大疆印象最深的是什么？” “我印象最深的就是是大疆的社会责任感，因为我去过比赛现场，所以我很感激大疆提供的这种难得的机会，对我这样的人来说，这是很难忘也很难得的机会；”\n不知道面试官对我的回答是否满意，但这的确是我心里的想法；\n我想，今天这个日子，几年或者更久的时间后，再回头来看，可能会说今天是一个十分重要的日子；\n4月25日，我投出了我的简历；然后按部就班，面试面试还是面试，一共三轮面试，最后就是等offer；\n在准备面试中，花了很多精力，学了好多东西，实时操作系统的知识还有C语言和ARM内核相关的知识，也把自己做过的项目，参加过的比赛都捋了好多遍，生怕面试官问到这方面的内容，自己却答不上来；\n这是我第一次投简历和面试，之前听说找工作时永远不要将自己最想去的公司放在第一个，因为第一次总是没有经验，总会紧张。所以如果第一次就要投最想去的公司，那就要认真准备了；\n准备面试的那几个星期我记了好多笔记，梳理了知识点，理通了自己做过的所有的项目还考虑了面试官可能问到的各种问题，记了几万字的笔记；\n我还记得那个下午，我在自习室坐了一天，把FreeRTOS的相关知识从头到尾梳理了一遍；\n准备的越多，自己就越有底气；\n虽然面试中并没有问到很多自己准备的问题，但是准备的过程还是很大程度上减弱了自己的紧张；\n虽然付出并不总是有收获的；\n但是想到自己经常熬夜去解决一个又一个Bug，熬夜画一个又一个板子，自己乐在其中，享受做东西的快乐；\n关于我的愿望 我QQ签名是：\n立志成为一名嵌入式开发攻城狮！\n是的，成为一名工程师是我一直以来的愿望；\n前些天我又看了一遍《三傻大闹宝莱坞》，每次看的感觉都不一样；\n第一次看这部电影是在我六年级的时候，那个时候觉得做一名工程师好酷，好像什么都能做出来，好像能解决遇到的一切问题，还有着不错的薪资；\n现在想来，梦想成为一名工程师的想法就是从那个时候开始的吧；\n兰彻，聪明，幽默，还有很多奇葩的想法，这部电影，让我觉得，做一名工程师会是多么有意思的事情呀；\n能用自己的知识解决遇到的各种问题，而且乐在其中；\n路一步步走，越走就离自己最初的想法越近；\n最后我想说 我想，我会成为自己想成为的那个工程师，掌握很多技能，面对问题，自己总能想到办法；\n我相信我会成为这样的人：聪明、乐观、幽默、不言放弃、坚信问题终会被解决；\n正式OFFER（2022年10月31号更新） 上周五中午接到电话，谈了薪水，晚上收到了正式OFFER，然后沟通了三方，尘埃落定；\n说真的，大疆开的是真的高，福利也不错，我是非常满意了，所以就决定去了。\n下面整理一下整个流程，也许可以帮助到后面的人：\n事件 日期\n投递简历 4月25日\n性格测评 4月27日\n第一次面试 5月3日\n第二次面试 5月13日\n第三次面试 5月20日\n电话OC 7月22日\n收到录用意向书 7月22日\n谈薪电话 10月27日\n收到正式OFFER 10月27日\n沟通三方信息 10月28日\n","permalink":"https://fan-pengfei.top/posts/%E6%88%91%E4%BC%9A%E6%88%90%E4%B8%BA%E8%87%AA%E5%B7%B1%E6%89%80%E6%9C%9F%E6%9C%9B%E7%9A%84%E9%82%A3%E4%B8%AA%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%90%97/","summary":"\u003cblockquote\u003e\n\u003cp\u003e昨天收到了大疆的录用意向offer，很开心，从四月投递简历，到终面结束，再到昨天收到意向offer，自己花了很多的时间和精力去修改简历和准备面试，终于还是等到了；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"关于dji面试\"\u003e关于DJI面试\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e收到意向offer的时候，我正在海康的三楼餐厅吃饭，看到是深圳的电话，我就猜到是大疆的HR打来的；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"image-20220729154202060\" loading=\"lazy\" src=\"/posts/%E6%88%91%E4%BC%9A%E6%88%90%E4%B8%BA%E8%87%AA%E5%B7%B1%E6%89%80%E6%9C%9F%E6%9C%9B%E7%9A%84%E9%82%A3%E4%B8%AA%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%90%97/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e我很早就听说过大疆，那时候只是知道大疆是一家做无人机的公司，更多的就没有什么了解了；\u003c/p\u003e\n\u003cp\u003e大二开学，我报名参加了我们学校的RoboMaster战队，在队内总共呆了一年，主要是负责哨兵机器人的嵌入式开发；说来惭愧，我并没有为我们队伍做出过什么大的贡献，自己也没有做什么创新性的东西，充其量我的表现是中规中矩，大多数时候都是在补充自己的知识储备，不过正是通过这个比赛我才更近距离地接触到大疆，也认识了挺多学长学姐，这对我的影响很大；\u003c/p\u003e\n\u003cp\u003e在备赛过程中，我用过大疆的无刷电机，很强，功能很强，控制尤其方便，还勾起了我对无刷电机控制的兴趣，学了一点FOC，然后用开源的Simple FOC，做了一个简单的无刷电机驱动器，很有意思，在大疆的面试中，面试官问我：“你能不能说一个你用过的大疆的产品的一个缺点？”，我就提到了这件事，并说了一个我认为大疆的无刷电机所存在的缺点。\u003c/p\u003e\n\u003cp\u003e我只去过一次比赛现场，感受过那种赛场的氛围，很难忘的经历，在面试中，面试官问了我一个问题：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e“你对大疆印象最深的是什么？”\n“我印象最深的就是是大疆的社会责任感，因为我去过比赛现场，所以我很感激大疆提供的这种难得的机会，对我这样的人来说，这是很难忘也很难得的机会；”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e不知道面试官对我的回答是否满意，但这的确是我心里的想法；\u003c/p\u003e\n\u003cp\u003e我想，今天这个日子，几年或者更久的时间后，再回头来看，可能会说今天是一个十分重要的日子；\u003c/p\u003e\n\u003cp\u003e4月25日，我投出了我的简历；然后按部就班，面试面试还是面试，一共三轮面试，最后就是等offer；\u003c/p\u003e\n\u003cp\u003e在准备面试中，花了很多精力，学了好多东西，实时操作系统的知识还有C语言和ARM内核相关的知识，也把自己做过的项目，参加过的比赛都捋了好多遍，生怕面试官问到这方面的内容，自己却答不上来；\u003c/p\u003e\n\u003cp\u003e这是我第一次投简历和面试，之前听说找工作时永远不要将自己最想去的公司放在第一个，因为第一次总是没有经验，总会紧张。所以如果第一次就要投最想去的公司，那就要认真准备了；\u003c/p\u003e\n\u003cp\u003e准备面试的那几个星期我记了好多笔记，梳理了知识点，理通了自己做过的所有的项目还考虑了面试官可能问到的各种问题，记了几万字的笔记；\u003c/p\u003e\n\u003cp\u003e我还记得那个下午，我在自习室坐了一天，把FreeRTOS的相关知识从头到尾梳理了一遍；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"52d913c451869c28fbf9d526b1ff5ab\" loading=\"lazy\" src=\"/posts/%E6%88%91%E4%BC%9A%E6%88%90%E4%B8%BA%E8%87%AA%E5%B7%B1%E6%89%80%E6%9C%9F%E6%9C%9B%E7%9A%84%E9%82%A3%E4%B8%AA%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%90%97/img-2.png\"\u003e\n\u003cimg alt=\"9a4943faae6f7706365b1445cd0b9cf\" loading=\"lazy\" src=\"/posts/%E6%88%91%E4%BC%9A%E6%88%90%E4%B8%BA%E8%87%AA%E5%B7%B1%E6%89%80%E6%9C%9F%E6%9C%9B%E7%9A%84%E9%82%A3%E4%B8%AA%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%90%97/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e准备的越多，自己就越有底气；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e虽然面试中并没有问到很多自己准备的问题，但是准备的过程还是很大程度上减弱了自己的紧张；\u003c/p\u003e\n\u003cp\u003e虽然付出并不总是有收获的；\u003c/p\u003e\n\u003cp\u003e但是想到自己经常熬夜去解决一个又一个Bug，熬夜画一个又一个板子，自己乐在其中，享受做东西的快乐；\u003c/p\u003e\n\u003ch2 id=\"关于我的愿望\"\u003e关于我的愿望\u003c/h2\u003e\n\u003cp\u003e我QQ签名是：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e立志成为一名嵌入式开发攻城狮！\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e是的，成为一名工程师是我一直以来的愿望；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e前些天我又看了一遍《三傻大闹宝莱坞》，每次看的感觉都不一样；\u003c/p\u003e\n\u003cp\u003e第一次看这部电影是在我六年级的时候，那个时候觉得做一名工程师好酷，好像什么都能做出来，好像能解决遇到的一切问题，还有着不错的薪资；\u003c/p\u003e\n\u003cp\u003e现在想来，梦想成为一名工程师的想法就是从那个时候开始的吧；\u003c/p\u003e\n\u003cp\u003e兰彻，聪明，幽默，还有很多奇葩的想法，这部电影，让我觉得，做一名工程师会是多么有意思的事情呀；\u003c/p\u003e\n\u003cp\u003e能用自己的知识解决遇到的各种问题，而且乐在其中；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e路一步步走，越走就离自己最初的想法越近；\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"最后我想说\"\u003e最后我想说\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e我想，我会成为自己想成为的那个工程师，掌握很多技能，面对问题，自己总能想到办法；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e我相信我会成为这样的人：聪明、乐观、幽默、不言放弃、坚信问题终会被解决；\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"正式offer2022年10月31号更新\"\u003e正式OFFER（2022年10月31号更新）\u003c/h2\u003e\n\u003cp\u003e上周五中午接到电话，谈了薪水，晚上收到了正式OFFER，然后沟通了三方，尘埃落定；\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image-20221031195615496\" loading=\"lazy\" src=\"/posts/%E6%88%91%E4%BC%9A%E6%88%90%E4%B8%BA%E8%87%AA%E5%B7%B1%E6%89%80%E6%9C%9F%E6%9C%9B%E7%9A%84%E9%82%A3%E4%B8%AA%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%90%97/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e说真的，大疆开的是真的高，福利也不错，我是非常满意了，所以就决定去了。\u003c/p\u003e\n\u003cp\u003e下面整理一下整个流程，也许可以帮助到后面的人：\u003c/p\u003e\n\u003cp\u003e事件\n日期\u003c/p\u003e\n\u003cp\u003e投递简历\n4月25日\u003c/p\u003e\n\u003cp\u003e性格测评\n4月27日\u003c/p\u003e\n\u003cp\u003e第一次面试\n5月3日\u003c/p\u003e\n\u003cp\u003e第二次面试\n5月13日\u003c/p\u003e\n\u003cp\u003e第三次面试\n5月20日\u003c/p\u003e\n\u003cp\u003e电话OC\n7月22日\u003c/p\u003e\n\u003cp\u003e收到录用意向书\n7月22日\u003c/p\u003e\n\u003cp\u003e谈薪电话\n10月27日\u003c/p\u003e\n\u003cp\u003e收到正式OFFER\n10月27日\u003c/p\u003e\n\u003cp\u003e沟通三方信息\n10月28日\u003c/p\u003e","title":"我会成为自己所期望的那个工程师吗"},{"content":" 几种常见格式的区别；\n一、bin文件 Bin文件是最纯粹的二进制机器代码, 或者说是”顺序格式”。按照assembly code顺序翻译成binary machine code，内部没有地址标记。Bin是直接的内存映象表示，二进制文件大小即为文件所包含的数据的实际大小。\nBIN文件就是直接的二进制文件，一般用编程器烧写时从地址0x00开始，而如果下载运行，则下载到编译时的地址即可。可以直接在裸机上运行。\n二、hex文件 Intel hex 文件常用来保存单片机或其他处理器的目标程序代码。它保存物理程序存储区中的目标代码映象。一般的编程器都支持这种格式。 就是机器代码的十六进制形式,并且是用一定文件格式的ASCII码来表示。\nHEX文件由记录（RECORD）组成。在HEX文件里面，每一行代表一个记录。每条记录都由一个冒号“:”打头，其格式如下：\n文件格式： :BBAAAATTHHHH…HHHHCC　含义分别如下：　BB：字节个数； AAAA：数据记录的开始地址，高位在前，低位在后； TT: Type ；\nType： 00数据记录，用来记录数据；01记录结束，放在文件末尾，用来标识文件结束；02用来标识扩展段地址的记录；04扩展地址记录(表示32位地址的前缀)；\nHHHH：一个字(Word)的数据记录,高字节在前,低字节在后；\nTT之后共有 BB/2 个字的数据 。\nCC: 占据一个Byte的CheckSum；\n举例：\n每行中的数据并不是一定有的，第二个字节数据长度为0，那么这行就没有数据。\n由于每行标识数据地址的只有2Byte，所以最大只能到64K，为了可以保存高地址的数据，就有了Extended Linear Address Record。如果这行的数据类型是0x04，那么，这行的数据就是随后数据的基地址。例如：\n:020000040004F6 :1000000018F09FE518F09FE518F09FE518F09FE5C0 :1000100018F09FE5805F20B9F0FF1FE518F09FE51D :00000001FF\n第一行，是Extended Linear Address Record，里面的数据，也就是基地址是0x0004；\n第二行是Data Record，里面的地址值是0x0000。那么数据18F09FE518F09FE518F09FE518F09FE5要写入FLASH中的地址为 (0x0004 \u0026laquo; 16) | 0x0000，也就是写入FLASH的0x40000这个地址。\n同样，第三行的数据的写入地址为0x40010。当一个HEX文件的数据超过64k的时候，文件中就会出现多个Extended Linear Address Record。\n校验值：每一行的最后一个值为此行数据的校验和。例如：\n:1000000018F09FE518F09FE518F09FE518F09FE5C0 这行中的 0xC0； :1000100018F09FE5805F20B9F0FF1FE518F09FE51D 这行中的 0x1D；\n校验和的算法为：计算从0x3A 以后（不包括0x3A）的所有各字节的和模256的余。即各字节二进制算术和，不计超过256的溢出值，然后用0x100减去这个算数累加和，得出得值就是此行得校验和。\n如手头的STM32 HEX 第一行 020000040800F2；\nEnd of File Record 行是每一个HEX文件的最后一行。例如：\n:00000001FF；\n这样的一行数据内容是固定的，数据长度为0，地址为0。\nhex和bin文件的区别： 1、HEX文件包含地址信息而BIN文件只包含数据本身,烧写或下载HEX文件时，一般不需要用户指定地址，因为HEX文件内部已经包含了地址信息。烧写BIN文件时则需要用户指定烧录的地址信息。 2、HEX文件是用ASCII码来表示二进制的数值。例如8-BIT的二进制数值0x4E，用ASCII来表示就需要分别表示字符‘4’和字符‘E’，每个字符均需要一个字节，因此HEX文件至少需要2倍BIN文件的空间。\n三、elf文件 ELF（Executableand linking format）文件是x86 Linux系统下的一种常用目标文件(objectfile)格式，有三种主要类型:\n适于连接的可重定位文件(relocatablefile)，可与其它目标文件一起创建可执行文件和共享目标文件；\n适于执行的可执行文件(executable file)，用于提供程序的进程映像，加载到内存执行；\n共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件，动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像；\n小结：可由elf文件转化为hex和bin两种文件，hex也可以直接转换为bin文件，但是bin要转化为hex文件必须要给定一个基地址。而hex和bin不能转化为elf文件，因为elf的信息量要大。\n四、axf文件 Axf文件由ARM编译器产生，除了包含bin的内容之外，还附加其他调试信息，这些调试信息加在可执行的二进制数据之前。调试时这些调试信息不会下载到RAM中，真正下载到RAM中的信息仅仅是可执行代码。因此，如果ram的大小小于axf文件的大小，程序是完全有可能在ram中调试的，只要axf除去调试信息后文件大小小于ram的大小即可。\n调试信息有以下功用：\n可将源代码包括注释夹在反汇编代码中，这样我们可随时切换到源代码中进行调试。\n我们还可以对程序中的函数调用情况进行跟踪(通过Watch \u0026amp; Call Stack Window查看)。\n对变量进行跟踪(利用Watch \u0026amp; Call Stack Window)。\n调试信息虽然有用，但程序功能实现后，在目标文件和库中减少调试信息却是非常有益的。减少调试信息可减少目标文件和库大小、加快链接速度、减小最终镜象代码。以下几种方法可用来减少每个源文件产生的调试信息：\n避免在头文件中条件性使用#define，链接器不能移除共用的调试部分，除非这些部分是完全一样的。\n更改C/C++源文件，使#included包含的所有头文件有相同顺序。\n尽量使用数量较多的小头文件而不是较大的单一头文件，这有利于链接器获取更多的通用块。\n程序中最好只包含必须用到的头文件。避免重复包含头文件，可使用编译器选项—remarks来产生警告信息；\naxf和elf文件的区别： axf和elf都是编译器生成的可执行文件。 区别是：ADS编译出来的是AXF文件。gcc编译出来的是ELF文件。两者虽然很像，但还是有差别的。这是文件格式的差别，不涉及调试格式。\naxf/elf是带格式的映象，bin是直接的内存映象的表示。\nLinux OS下，ELF通常就是可执行文件，通常gcc -o test test.c，生成的test文件就是ELF格式的，在Linux Shell下输入./test就可以执行。在嵌入式中，上电开始运行，没有OS系统，如果将ELF格式的文件烧写进去，包含一些ELF格式的东西，arm运行碰到这些指令，就会导致失败，如果用bin文件，程序就可以一步一步运行。\n","permalink":"https://fan-pengfei.top/posts/binhexaxf%E5%92%8Celf%E7%9A%84%E5%8C%BA%E5%88%AB/","summary":"\u003cblockquote\u003e\n\u003cp\u003e几种常见格式的区别；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一bin文件\"\u003e一、bin文件\u003c/h2\u003e\n\u003cp\u003eBin文件是最纯粹的二进制机器代码, 或者说是”顺序格式”。按照assembly code顺序翻译成binary machine  code，内部没有地址标记。\u003cstrong\u003eBin是直接的内存映象表示\u003c/strong\u003e，二进制文件大小即为文件所包含的数据的实际大小。\u003c/p\u003e\n\u003cp\u003eBIN文件就是直接的二进制文件，一般用编程器烧写时从地址0x00开始，而如果下载运行，则下载到编译时的地址即可。\u003cstrong\u003e可以直接在裸机上运行。\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"二hex文件\"\u003e二、hex文件\u003c/h2\u003e\n\u003cp\u003eIntel hex 文件常用来保存单片机或其他处理器的目标程序代码。它保存物理程序存储区中的目标代码映象。一般的编程器都支持这种格式。 就是机器代码的十六进制形式,并且是用一定文件格式的ASCII码来表示。\u003c/p\u003e\n\u003cp\u003eHEX文件由记录（RECORD）组成。在HEX文件里面，每一行代表一个记录。每条记录都由一个冒号“:”打头，其格式如下：\u003c/p\u003e\n\u003ch3 id=\"文件格式\"\u003e文件格式：\u003c/h3\u003e\n\u003cp\u003e:BBAAAATTHHHH…HHHHCC　\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e含义分别如下：\u003c/strong\u003e　\u003c/p\u003e\n\u003cp\u003eBB：字节个数；\nAAAA：数据记录的开始地址，高位在前，低位在后；\nTT: Type ；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eType：\n00数据记录，用来记录数据；01记录结束，放在文件末尾，用来标识文件结束；02用来标识扩展段地址的记录；04扩展地址记录(表示32位地址的前缀)；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eHHHH：一个字(Word)的数据记录,高字节在前,低字节在后；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eTT之后共有 BB/2 个字的数据 。\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eCC: 占据一个Byte的CheckSum；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e举例：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e每行中的数据并不是一定有的，第二个字节数据长度为0，那么这行就没有数据。\u003c/p\u003e\n\u003cp\u003e由于每行标识数据地址的只有2Byte，所以最大只能到64K，为了可以保存高地址的数据，就有了Extended Linear Address Record。如果这行的数据类型是0x04，那么，这行的数据就是随后数据的基地址。例如：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e:020000040004F6\n:1000000018F09FE518F09FE518F09FE518F09FE5C0\n:1000100018F09FE5805F20B9F0FF1FE518F09FE51D\n:00000001FF\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e第一行，是Extended Linear Address Record，里面的数据，也就是基地址是0x0004；\u003c/p\u003e\n\u003cp\u003e第二行是Data  Record，里面的地址值是0x0000。那么数据18F09FE518F09FE518F09FE518F09FE5要写入FLASH中的地址为  \u003cstrong\u003e(0x0004 \u0026laquo; 16) |  0x0000\u003c/strong\u003e，也就是写入FLASH的0x40000这个地址。\u003c/p\u003e\n\u003cp\u003e同样，第三行的数据的写入地址为0x40010。当一个HEX文件的数据超过64k的时候，文件中就会出现多个Extended Linear Address Record。\u003c/p\u003e\n\u003cp\u003e校验值：每一行的最后一个值为此行数据的校验和。例如：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e:1000000018F09FE518F09FE518F09FE518F09FE5C0 这行中的 0xC0；\n:1000100018F09FE5805F20B9F0FF1FE518F09FE51D 这行中的 0x1D；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e校验和的算法为：计算从0x3A 以后（不包括0x3A）的所有各字节的和模256的余。即各字节二进制算术和，不计超过256的溢出值，然后用0x100减去这个算数累加和，得出得值就是此行得校验和。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e如手头的STM32 HEX 第一行 020000040800F2；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eEnd of File Record 行是每一个HEX文件的最后一行。例如：\u003c/p\u003e","title":"BIN、HEX、AXF和ELF的区别"},{"content":" 记录自己刷算法题中遇到的问题和自己的思路；\n1、无重复长度的最长子串： 题目描述： 提示： 0 \u0026lt;= s.length \u0026lt;= 5 * 104； s` 由英文字母、数字、符号和空格组成；\n思路： 就是做两个标志位，表示当前字符串的起始位置和正在比较的字符位置；\n遍历整个字符串；\n从起始标志位开始，到结束位置，依次比对是否有重复字符；\n如果无，就结束位置加加；\n如果有，就计算长度，并移动起始位置到重复字符的下一个位置；\n然后一直循环；\n要特别考虑：\nau abb bba 这些特殊排序的字符串；\n是因为最后一个没有办法比较；\n题解代码： int lengthOfLongestSubstring(char *s) { int str_length = strlen(s); int end_flag = 1, start_flag = 0, max_length = 1; char flag = 0; int i = 0; if (str_length == 0) { return 0; } while (*(s + end_flag) != \u0026#39;\\0\u0026#39;) { for (i = start_flag; i max_length) { max_length = end_flag - start_flag; } flag = 1; break; } if (i == str_length - 2) { if ((*(s + end_flag + 1) == \u0026#39;\\0\u0026#39;)) { if (end_flag - start_flag + 1 \u0026gt; max_length) { max_length = end_flag - start_flag + 1; } } } } if (flag == 1) { flag = 0; start_flag = i + 1; } else { end_flag++; } } return max_length; 改进： //方法2 int lengthOfLongestSubstring(char * s){ int str_length=strlen(s); int end_flag=1,start_flag=0,max_length=1; char flag=0; int i=0; if(str_length==0) { return 0; } while(*(s+end_flag)!=\u0026#39;\\0\u0026#39;) { for(i=start_flag;imax_length) { max_length=end_flag-start_flag; } flag=0; start_flag=i+1; } else { if(end_flag-start_flag+1\u0026gt;max_length) { max_length=end_flag-start_flag+1; } end_flag++; } } return max_length; } 2、括号匹配 bool isValid(char * s){ char *temp_s; int p_temp=-1; if(strlen(s)%2!=0) { return false; } temp_s=(char *)malloc(strlen(s)); if((*s==\u0026#39;)\u0026#39;)||(*s==\u0026#39;}\u0026#39;)||(*s==\u0026#39;]\u0026#39;)) { return false; } while(*s!=\u0026#39;\\0\u0026#39;) { switch(*s) { case \u0026#39;(\u0026#39;: p_temp++; if(p_tempmax) { max=temp; } if(height[p_left]valval) { pre-\u0026gt;next=list1; list1=list1-\u0026gt;next; } else { pre-\u0026gt;next=list2; list2=list2-\u0026gt;next; } pre=pre-\u0026gt;next; } if(list1==NULL) { pre-\u0026gt;next=list2; } else { pre-\u0026gt;next=list1; } return dummy-\u0026gt;next; } 思路： 主要是不用考虑申请空间的问题，因为空间本身就是存在的，只需要改变链表中各个节点间的指向关系就可以；\n最后看哪一个先为NULL，然后把不为空的整体连接上去，返回即可；\n","permalink":"https://fan-pengfei.top/posts/%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e记录自己刷算法题中遇到的问题和自己的思路；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"1无重复长度的最长子串\"\u003e1、无重复长度的最长子串：\u003c/h3\u003e\n\u003ch4 id=\"题目描述\"\u003e题目描述：\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"bba26f3a71a90a406c18dc5f4fbb88e\" loading=\"lazy\" src=\"/posts/%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e提示：\n\u003ccode\u003e0 \u0026lt;= s.length \u0026lt;= 5 * 104； \u003c/code\u003es` 由英文字母、数字、符号和空格组成；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"思路\"\u003e思路：\u003c/h4\u003e\n\u003cp\u003e就是做两个标志位，表示当前字符串的起始位置和正在比较的字符位置；\u003c/p\u003e\n\u003cp\u003e遍历整个字符串；\u003c/p\u003e\n\u003cp\u003e从起始标志位开始，到结束位置，依次比对是否有重复字符；\u003c/p\u003e\n\u003cp\u003e如果无，就结束位置加加；\u003c/p\u003e\n\u003cp\u003e如果有，就计算长度，并移动起始位置到重复字符的下一个位置；\u003c/p\u003e\n\u003cp\u003e然后一直循环；\u003c/p\u003e\n\u003cp\u003e要特别考虑：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eau\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eabb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebba\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这些特殊排序的字符串；\u003c/p\u003e\n\u003cp\u003e是因为最后一个没有办法比较；\u003c/p\u003e\n\u003ch4 id=\"题解代码\"\u003e题解代码：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elengthOfLongestSubstring\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003es)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e str_length \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estrlen\u003c/span\u003e(s);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e end_flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, start_flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, max_length \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (str_length \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e(s \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e end_flag) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\0\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e start_flag; i  max_length)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    max_length \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e end_flag \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e start_flag;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (i \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e str_length \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ((\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e(s \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e end_flag \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\0\u0026#39;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (end_flag \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e start_flag \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e max_length)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        max_length \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e end_flag \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e start_flag \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (flag \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            start_flag \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            end_flag\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e max_length;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"改进\"\u003e改进：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//方法2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elengthOfLongestSubstring\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e s){\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e str_length\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003estrlen\u003c/span\u003e(s);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e end_flag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,start_flag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,max_length\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e flag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e(str_length\u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e(s\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003eend_flag)\u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\0\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e(i\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003estart_flag;imax_length)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 max_length\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eend_flag\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003estart_flag;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            flag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            start_flag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ei\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e(end_flag\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003estart_flag\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003emax_length)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                 max_length\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eend_flag\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003estart_flag\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            end_flag\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e max_length;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"2括号匹配\"\u003e2、括号匹配\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"test\" loading=\"lazy\" src=\"/posts/%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95/img-2.png\"\u003e\u003c/p\u003e","title":"力扣刷题记录"},{"content":" 在C语言中，内存管理是很重要的，最近刷算法题目也用到了malloc和free函数，在此记录一下；\n一、函数原型及说明： malloc()： 函数原型：\nvoid *malloc(long NumBytes); 该函数分配了NumBytes个字节，并返回了指向这块内存的指针。如果分配失败，则返回一个空指针（NULL）。 关于分配失败的原因，应该有多种，比如说空间不足就是一种。\nfree()： 函数原型：\nvoid free(void *FirstByte); 该函数是将之前用malloc分配的空间还给程序或者是操作系统，也就是释放了这块内存，让它重新得到自由。\n二、函数使用注意事项： 1、申请了内存空间后，必须检查是否分配成功；\n2、当不需要再使用申请的内存时，记得释放；\n释放后应该把指向这块内存的指针指向NULL，防止程序后面不小心使用了它。\n3、这两个函数应该是配对使用；\n如果申请后不释放就是内存泄露；如果无故释放那就是什么也没有做。释放只能一次，如果释放两次及两次以上会出现错误（释放空指针例外，释放空指针其实也等于啥也没做，所以释放空指针释放多少次都没有问题）。\n**4、虽然malloc()函数的类型是(void )，任何类型的指针都可以转换成(void )，但是最好还是在前面进行强制类型转换，因为这样可以躲过一些编译器的检查；\n三、具体例子： // Code... char *Ptr = NULL; Ptr = (char *)malloc(100 * sizeof(char)); if (NULL == Ptr) { exit (1); } gets(Ptr); // code... free(Ptr); Ptr = NULL; // code... 四、原理部分： 1、malloc内存空间的来源： 答案是从堆里面获得空间。 也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时，就会遍历该链表，然后就寻找第一个空间大于所申请空间的堆结点，然后就将该结点从空闲结点链表中删除，并将该结点的空间分配给程序。\n在使用malloc()分配内存空间后，一定要记得释放内存空间，否则就会出现内存泄漏。\n2、free释放了什么： free()释放的是指针指向的内存。 注意！释放的是内存，不是指针！指针并没有被释放，指针仍然指向原来的存储空间。指针是一个变量，只有程序结束时才被销毁。释放了内存空间后，原来指向这块空间的指针还是存在！只不过现在指针指向的内容的垃圾，是未定义的，所以说是垃圾。因此，释放内存后把指针指向NULL，防止指针在后面不小心又被解引用了。 free() 不会改变 指针变量本身的值，调用 free() 后它仍然会指向相同的内存空间，但是此时该内存已无效，不能被使用。所以建议将 指针的值设置为 NULL，例如：\nfree(ptr); ptr = NULL; free()的源代码：\nstruct mem_control_block { int is_available; //一般来说应该是一个可用空间的首地址，但这里英文单词却显示出空间是否可用的一个标记 int size; //这是实际空间的大小 }; void free(void *ptr) { struct mem_control_block *free; free = ptr - sizeof(struct mem_control_block); free-\u0026gt;is_available = 1; return; } 五、直接定义一个数组和使用malloc动态分配的区别： malloc作为一个包含在头文件下的函数，用于申请空间。\n我们平常定义数组一般习惯性的就直接定义，比如int a[5]，此处就是直接定义了一个数组，数组内的元素都是int型，并且有五个，从一开始就确定了数组a的大小，并且无法被改变。\n并且变量被存放在栈区，栈区的变量都是由系统自己开辟空间存储，并自动销毁空间释放的。\n其实，还有一种定义数组的方式，那就是借助人为申请空间的方式定义一个数组。请看如下程序：\n#include #include int main() { int *a = (int *)malloc(sizeof(int[5])); a[0]=1; a[1]=2; a[2]=3; a[3]=4; a[4]=5; int i; for(i=0;i 定义的方法是首先得定义一个指针，然后在这个指针的指向下，利用malloc去开辟一个自定义大小的空间。 此方法定义的数组变量被存放在堆区，是由用户自己开辟和释放的。 下面来看结果运行结果： ![img](img-1.png) 可见结果是理想无误的。 **使用这种先定义指针后开辟空间的方式可以适用于一些开始并不知道数组空间大小的场景。** ## 六、一个思维导图： 参考链接：[链接](https://blog.51cto.com/panyujie/3669817) ![动态内存开辟+柔性数组](img-2.png) ","permalink":"https://fan-pengfei.top/posts/malloc%E5%92%8Cfree%E7%9A%84%E7%94%A8%E6%B3%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e在C语言中，内存管理是很重要的，最近刷算法题目也用到了malloc和free函数，在此记录一下；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一函数原型及说明\"\u003e一、函数原型及说明：\u003c/h2\u003e\n\u003ch3 id=\"malloc\"\u003emalloc()：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e函数原型：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003emalloc\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e NumBytes);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e该函数分配了NumBytes个字节，并返回了指向这块内存的指针。如果分配失败，则返回一个空指针（NULL）。\n关于分配失败的原因，应该有多种，比如说空间不足就是一种。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"free\"\u003efree()：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e函数原型：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efree\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eFirstByte);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e该函数是将之前用malloc分配的空间还给程序或者是操作系统，也就是释放了这块内存，让它重新得到自由。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"二函数使用注意事项\"\u003e二、函数使用注意事项：\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e1、申请了内存空间后，必须检查是否分配成功；\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2、当不需要再使用申请的内存时，记得释放；\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e释放后应该把指向这块内存的指针指向NULL，防止程序后面不小心使用了它。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e3、这两个函数应该是配对使用；\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e如果申请后不释放就是内存泄露；如果无故释放那就是什么也没有做。释放只能一次，如果释放两次及两次以上会出现错误（释放空指针例外，释放空指针其实也等于啥也没做，所以释放空指针释放多少次都没有问题）。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e**4、虽然malloc()函数的类型是(void \u003cem\u003e)，任何类型的指针都可以转换成(void \u003cem\u003e)，但是最好还是在前面进行强制类型转换，因为这样可以躲过一些编译器的检查；\u003c/em\u003e\u003c/em\u003e\u003c/p\u003e\n\u003ch2 id=\"三具体例子\"\u003e三、具体例子：\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Code...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003ePtr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e NULL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePtr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#a6e22e\"\u003emalloc\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003esizeof\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (NULL \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e Ptr)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eexit\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003egets\u003c/span\u003e(Ptr);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// code...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efree\u003c/span\u003e(Ptr);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePtr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e NULL;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// code...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"四原理部分\"\u003e四、原理部分：\u003c/h2\u003e\n\u003ch3 id=\"1malloc内存空间的来源\"\u003e1、malloc内存空间的来源：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e答案是从堆里面获得空间。\n也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时，就会遍历该链表，然后就寻找第一个空间大于所申请空间的堆结点，然后就将该结点从空闲结点链表中删除，并将该结点的空间分配给程序。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e在使用malloc()分配内存空间后，一定要记得释放内存空间，否则就会出现内存泄漏。\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"2free释放了什么\"\u003e2、free释放了什么：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003efree()释放的是指针指向的内存。\n注意！释放的是内存，不是指针！指针并没有被释放，指针仍然指向原来的存储空间。指针是一个变量，只有程序结束时才被销毁。释放了内存空间后，原来指向这块空间的指针还是存在！只不过现在指针指向的内容的垃圾，是未定义的，所以说是垃圾。因此，释放内存后把指针指向NULL，防止指针在后面不小心又被解引用了。\nfree() 不会改变 指针变量本身的值，调用 free() 后它仍然会指向相同的内存空间，但是此时该内存已无效，不能被使用。所以建议将 指针的值设置为 NULL，例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efree\u003c/span\u003e(ptr);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eptr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e NULL;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003efree()的源代码：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e mem_control_block {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e is_available;    \u003cspan style=\"color:#75715e\"\u003e//一般来说应该是一个可用空间的首地址，但这里英文单词却显示出空间是否可用的一个标记\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e size;            \u003cspan style=\"color:#75715e\"\u003e//这是实际空间的大小\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efree\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eptr)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e mem_control_block \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003efree;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    free \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ptr \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003esizeof\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e mem_control_block);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    free\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003eis_available \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"五直接定义一个数组和使用malloc动态分配的区别\"\u003e五、直接定义一个数组和使用malloc动态分配的区别：\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003emalloc作为一个包含在头文件下的函数，用于申请空间。\u003c/p\u003e","title":"malloc和free的用法"},{"content":" 一些比较好的网站的集锦；\n1、阮一峰的网络日志： Make 命令教程 主要是关于Makefile的简单教程，看了之后基本能够完成一个比较完备的Makefile；\n这是我自己总结的例子：Makefile基本示例；\n跟我一起写Makefile教程：跟我一起写Makefile；\n网站上还有不少比较好的教程和博客：网站首页；\n2、Bash脚本教程： 阮一峰的Bash脚本教程； 3、C语言入门教程： 阮一峰的C语言入门教程； ","permalink":"https://fan-pengfei.top/posts/%E6%AF%94%E8%BE%83%E5%A5%BD%E7%9A%84%E7%9F%A5%E8%AF%86%E7%BD%91%E7%AB%99%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003e一些比较好的网站的集锦；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"1阮一峰的网络日志\"\u003e1、阮一峰的网络日志：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.ruanyifeng.com/blog/2015/02/make.html\"\u003eMake 命令教程\u003c/a\u003e\n主要是关于Makefile的简单教程，看了之后基本能够完成一个比较完备的Makefile；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e这是我自己总结的例子：\u003ca href=\"https://www.fan-pengfei.top/2022/07/07/%E7%AE%80%E5%8D%95Makefile/#more\"\u003eMakefile基本示例\u003c/a\u003e；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e跟我一起写Makefile教程：\u003ca href=\"https://seisman.github.io/how-to-write-makefile/\"\u003e跟我一起写Makefile\u003c/a\u003e；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e网站上还有不少比较好的教程和博客：\u003ca href=\"https://www.ruanyifeng.com/\"\u003e网站首页\u003c/a\u003e；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"2bash脚本教程\"\u003e2、Bash脚本教程：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://wangdoc.com/bash/expansion.html\"\u003e阮一峰的Bash脚本教程\u003c/a\u003e；\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"3c语言入门教程\"\u003e3、C语言入门教程：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://wangdoc.com/clang/intro.html\"\u003e阮一峰的C语言入门教程\u003c/a\u003e；\u003c/li\u003e\n\u003c/ul\u003e","title":"比较好的知识网站记录"},{"content":" GCC工具介绍以及常见的用法；\nGCC编译命令： 1. GCC工具 GCC编译器：\nGCC（GNU Compiler Collection）是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”，当时只支持C语言。 后来又扩展能够支持更多编程语言，包括 C++、Fortran 和 Java 等。 因此，GCC也被重新定义为“GNU Compiler Collection”，成为历史上最优秀的编译器， 其执行效率与一般的编译器相比平均效率要高 20%~30%。\nGCC的官网地址为：https://gcc.gnu.org/，在Ubuntu系统下系统默认已经安装好GCC编译器，可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径：\nGCC编译工具链：\nGCC编译工具链（toolchain），是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容：\ngcc-core：即GCC编译器，用于完成预处理和编译过程，把C代码转换成汇编代码。\nBinutils ：除GCC编译器外的一系列小工具包括了链接器ld，汇编器as、目标文件格式查看器readelf等。\nglibc：包含了主要的 C语言标准函数库，C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。\n在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。\nBinutils工具集：\nBinutils（bin utility），是GNU二进制工具集，通常跟GCC编译器一起打包安装到系统，它的官方说明网站地址为： https://www.gnu.org/software/binutils/ 。\n在进行程序开发的时候通常不会直接调用这些工具，而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具：\nas：汇编器，把汇编语言代码转换为机器码（目标文件）。\nld：链接器，把编译生成的多个目标文件组织成最终的可执行程序文件。\nreadelf：可用于查看目标文件或可执行程序文件的信息。\nnm ： 可用于查看目标文件中出现的符号。\nobjcopy： 可用于目标文件格式转换，如.bin 转换成 .elf 、.elf 转换成 .bin等。\nobjdump：可用于查看目标文件的信息，最主要的作用是反汇编。\nsize：可用于查看目标文件不同部分的尺寸和总尺寸，例如代码段大小、数据段大小、使用的静态内存、总大小等。\n系统默认的Binutils工具集位于/usr/bin目录下，可使用如下命令查看系统中存在的Binutils工具集：\n# 在Ubantu上执行如下命令 ls /usr/bin/ | grep linux-gnu- glibc库：\nglibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库，因为绝大部分C程序都依赖该函数库，该文件甚至会直接影响到系统的正常运行，例如常用的文件操作函数read、write、open，打印函数printf、动态内存申请函数malloc等。\n在Ubuntu系统下，libc.so.6是glibc的库文件，可直接执行该库文件查看版本，在主机上执行如下命令：\n# 在Ubantu上执行如下命令 # 以下是Ubuntu 64位机的glibc库文件路径，可直接执行 /lib/x86_64-linux-gnu/libc.so.6 2. GCC编译 编写HelloWorld文件：\n#include int main() { printf(\u0026#34;hello, world! This is a C program.\\n\u0026#34;); for(int i=0;i -E，只执行到预编译。直接输出预编译结果； **2**、`gcc -S source_file.c` \u0026gt; -S，只执行到源代码到汇编代码的转换，输出汇编代码； **3**、`gcc -c source_file.c` \u0026gt; -c，只执行到编译，输出目标文件； **4**、`gcc (-E/S/c/) source_file.c -o output_filename` \u0026gt; -o： 指定输出文件名，可以配合以上三种标签使用； -o 参数可以被省略。这种情况下编译器将使用以下默认名称输出： \u0026gt; -E：预编译结果将被输出到标准输出端口（通常是显示器） -S：生成名为source_file.s的汇编代码 -c：生成名为source_file.o的目标文件。 无标签情况：生成名为a.out的可执行文件。 **5**、`gcc -g source_file.c` \u0026gt; -g，生成供调试用的可执行文件，可以在gdb中运行。由于文件中包含了调试信息因此运行效率很低，且文件也大不少。 这里可以用strip命令重新将文件中debug信息删除。这是会发现生成的文件甚至比正常编译的输出更小了，这是因为strip把原先正常编译中的一些额外信息（如函数名之类）也删除了。用法为 strip a.out **6**、`gcc -s source_file.c` \u0026gt; -s，直接生成与运用strip同样效果的可执行文件（删除了所有符号信息）。 **7、**`gcc -O source_file.c` \u0026gt; -O（大写的字母O），编译器对代码进行自动优化编译，输出效率更高的可执行文件。 -O 后面还可以跟上数字指定优化级别，如： \u0026gt; `gcc -O2 source_file.c` 数字越大，越加优化。但是通常情况下，自动的东西都不是太聪明，太大的优化级别可能会使生成的文件产生一系列的bug。一般可选择2；3会有一定风险。 **8、**`gcc -Wall source_file.c` \u0026gt; -W，在编译中开启一些额外的警告（warning）信息； \u0026gt; -Wall，将所有的警告信息全开； **9、**`gcc source_file.c -L/path/to/lib -lxxx -I/path/to/include` \u0026gt; -l，指定所使用到的函数库，本例中链接器会尝试链接名为libxxx.a的函数库； -L，指定函数库所在的文件夹，本例中链接器会尝试搜索/path/to/lib文件夹； -I，指定头文件所在的文件夹，本例中预编译器会尝试搜索/path/to/include文件夹； ### 3、调试选项： **1**、`-g` \u0026gt; 只是编译器，在编译的时候，产生调试信息； **2**、-`gstabs` \u0026gt; 此选项以stabs格式声称调试信息,但是不包括gdb调试信息； **3**、`3-gstabs+` \u0026gt; 此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息； **4**、`-ggdb` \u0026gt; 此选项将尽可能的生成gdb的可以使用的调试信息； **5**、`-glevel` \u0026gt; 请求生成调试信息，同时用level指出需要多少信息，默认的level值是2； ### 4、链接选项： **1**、`-static` 此选项将禁止使用动态库。 \u0026gt; 优点：程序运行不依赖于其他库； \u0026gt; 缺点：文件比较大； **2**、`-shared (-G)` 此选项将尽量使用动态库，为默认选项 \u0026gt; 优点：生成文件比较小； \u0026gt; 缺点：运行时需要系统提供动态库； **3**、`-symbolic` 建立共享目标文件的时候,把引用绑定到全局符号上. \u0026gt; 对所有无法解析的引用作出警告(除非用连接编辑选项 `-Xlinker -z -Xlinker defs’取代)； \u0026gt; **注：只有部分系统支持该选项；** ### 5、错误和警告选项： **1**、`-Wall` \u0026gt; 一般使用该选项，允许发出GCC能够提供的所有有用的警告。也可以用-W{warning}来标记指定的警告； **2**、`-pedantic` \u0026gt; 允许发出ANSI/ISO C标准所列出的所有警告； **3**、`-pedantic-errors` \u0026gt; 允许发出ANSI/ISO C标准所列出的错误； **4**、`-werror` \u0026gt; 把所有警告转换为错误，以在警告发生时中止编译过程； **5**、`-w` \u0026gt; 关闭所有警告,建议不要使用此项； ### 6、预处理选项： **1**、`-Dmacro` \u0026gt; 相当于C语言中的#define macro； **2**、`-Dmacro=defn` \u0026gt; 相当于C语言中的#define macro=defn； **3**、`-Umacro` \u0026gt; 相当于C语言中的#undef macro； **4**、`-undef` \u0026gt; 取消对任何非标准宏的定义； ### 7、其他选项： **1**、`-fpic` \u0026gt; 编译器就生成位置无关目标码.适用于共享库(shared library). **2**、`-fPIC` \u0026gt; 编译器就输出位置无关目标码.适用于动态连接(dynamic linking),即使分支需要大范围转移. **3**、`-v` \u0026gt; 显示详细的编译、汇编、连接命令 ","permalink":"https://fan-pengfei.top/posts/gcc%E5%B8%B8%E8%A7%81%E5%91%BD%E4%BB%A4%E5%8F%8A%E7%94%A8%E6%B3%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003eGCC工具介绍以及常见的用法；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"gcc编译命令\"\u003eGCC编译命令：\u003c/h1\u003e\n\u003ch2 id=\"1-gcc工具\"\u003e1. GCC工具\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eGCC编译器：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eGCC（GNU Compiler Collection）是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”，当时只支持C语言。 后来又扩展能够支持更多编程语言，包括 C++、Fortran 和 Java 等。 因此，GCC也被重新定义为“GNU Compiler Collection”，成为历史上最优秀的编译器， 其执行效率与一般的编译器相比平均效率要高 20%~30%。\u003c/p\u003e\n\u003cp\u003eGCC的官网地址为：\u003ca href=\"https://link.zhihu.com/?target=https%3A//gcc.gnu.org/\"\u003ehttps://gcc.gnu.org/\u003c/a\u003e，在Ubuntu系统下系统默认已经安装好GCC编译器，可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/gcc%E5%B8%B8%E8%A7%81%E5%91%BD%E4%BB%A4%E5%8F%8A%E7%94%A8%E6%B3%95/img-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eGCC编译工具链：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eGCC编译工具链（toolchain），是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003egcc-core：即GCC编译器，用于完成预处理和编译过程，把C代码转换成汇编代码。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eBinutils ：除GCC编译器外的一系列小工具包括了链接器ld，汇编器as、目标文件格式查看器readelf等。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eglibc：包含了主要的 C语言标准函数库，C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBinutils工具集：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBinutils（bin utility），是GNU二进制工具集，通常跟GCC编译器一起打包安装到系统，它的官方说明网站地址为： \u003ca href=\"https://link.zhihu.com/?target=https%3A//www.gnu.org/software/binutils/\"\u003ehttps://www.gnu.org/software/binutils/\u003c/a\u003e 。\u003c/p\u003e\n\u003cp\u003e在进行程序开发的时候通常不会直接调用这些工具，而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eas：汇编器，把汇编语言代码转换为机器码（目标文件）。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eld：链接器，把编译生成的多个目标文件组织成最终的可执行程序文件。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ereadelf：可用于查看目标文件或可执行程序文件的信息。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003enm ： 可用于查看目标文件中出现的符号。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eobjcopy： 可用于目标文件格式转换，如.bin 转换成 .elf 、.elf 转换成 .bin等。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eobjdump：可用于查看目标文件的信息，最主要的作用是反汇编。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003esize：可用于查看目标文件不同部分的尺寸和总尺寸，例如代码段大小、数据段大小、使用的静态内存、总大小等。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e系统默认的Binutils工具集位于/usr/bin目录下，可使用如下命令查看系统中存在的Binutils工具集：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 在Ubantu上执行如下命令\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els /usr/bin/ | grep linux-gnu-\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/gcc%E5%B8%B8%E8%A7%81%E5%91%BD%E4%BB%A4%E5%8F%8A%E7%94%A8%E6%B3%95/img-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eglibc库：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eglibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库，因为绝大部分C程序都依赖该函数库，该文件甚至会直接影响到系统的正常运行，例如常用的文件操作函数read、write、open，打印函数printf、动态内存申请函数malloc等。\u003c/p\u003e\n\u003cp\u003e在Ubuntu系统下，libc.so.6是glibc的库文件，可直接执行该库文件查看版本，在主机上执行如下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 在Ubantu上执行如下命令\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 以下是Ubuntu 64位机的glibc库文件路径，可直接执行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/lib/x86_64-linux-gnu/libc.so.6\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/gcc%E5%B8%B8%E8%A7%81%E5%91%BD%E4%BB%A4%E5%8F%8A%E7%94%A8%E6%B3%95/img-3.jpg\"\u003e\u003c/p\u003e","title":"GCC常见命令及用法"},{"content":" 字符串算式先转为逆波兰式(后缀表达式)，然后计算；\n一、求逆波兰表达式 核心思想： 逆波兰算法的核心思想是将普通的中缀表达式转换为后缀表达式。\n**什么是中缀表达式？**例如a+b，运算符在两个操作数的中间。这是我们从小学开始学习数学就一直使用的表达式形式。 **什么是后缀表达式？**例如a b + ，运算符在两个操作数的后面。后缀表达式虽然看起来奇怪，不利于人阅读，但利于计算机处理。 转换为后缀表达式的好处是： 去除原来表达式中的括号，因为括号只指示运算顺序，不是实际参与计算的元素。 使得运算顺序有规律可寻，计算机能编写出代码完成计算。\n核心步骤： 逆波兰算法的核心步骤就2个：\n将中缀表达式转换为后缀表达式，例如输入的原始表达式是 3*(5+7) ，转换得到 3 5 7 + * 根据后缀表达式，按照特定的计算规则得到最终计算结果\n具体步骤： 中缀表达式转换为后缀表达式：\n你需要设定一个栈SOP,和一个线性表 L 。SOP用于临时存储运算符和左括号分界符( ，L用于存储后缀表达式。\n遍历原始表达式中的每一个表达式元素： 如果是操作数，则直接追加到 L中。只有 运算符 或者 分界符（ 才可以存放到 栈SOP中；\n如果是分界符： 如果是左括号 ( ， 则 直接压入SOP，等待下一个最近的 右括号 与之配对。\n如果是右括号 ) ，则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈，丢弃它，然后从SOP中出栈，得到元素e，将e依次追加到L里。一直循环，直到出栈元素e 是 左括号 ( ，同样丢弃他。\n如果是运算符（用op1表示）： 如果SOP栈顶元素（用op2表示） 不是运算符，则二者没有可比性，则直接将此运算符op1压栈。 例如栈顶是左括号 ( ，或者栈为空。\n如果SOP栈顶元素（用op2表示） 是运算符 ，则比较op1和 op2的优先级。如果op1 \u0026gt; op2 ，则直接将此运算符op1压栈。\n如果不满足op1 \u0026gt; op2，则将op2出栈，并追加到L，再试图将op1压栈，如果如果依然不满足 op1\u0026gt;新的栈顶op2，继续将新的op2弹出追加到L ，直到op1可以压入栈中为止。\n也就是说，如果在SOP栈中，有2个相邻的元素都是运算符，则他们必须满足：下层运算符的优先级一定小于上层元素的优先级，才能相邻。\n最后，如果SOP中还有元素，则依次弹出追加到L后，就得到了后缀表达式。\n举例： 中缀式子：\n(2*(3-4))*5 逆波兰式（后缀式子）：\n2 3 4-*5*\n二、逆波兰表达式的计算 步骤： 从左到右扫描表达式，如果当前字符为数字，则入栈。\n如果当前字符为运算符，则将栈顶两个元素出栈，作相应运算，结果再入栈。\n最后当表达式扫描完后，栈里的就是计算结果了。\n","permalink":"https://fan-pengfei.top/posts/%E7%AE%97%E6%95%B0%E5%BC%8F%E8%AE%A1%E7%AE%97/","summary":"\u003cblockquote\u003e\n\u003cp\u003e字符串算式先转为逆波兰式(后缀表达式)，然后计算；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一求逆波兰表达式\"\u003e一、求逆波兰表达式\u003c/h2\u003e\n\u003ch3 id=\"核心思想\"\u003e核心思想：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e逆波兰算法的核心思想是将普通的中缀表达式转换为后缀表达式。\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e**什么是中缀表达式？**例如a+b，运算符在两个操作数的中间。这是我们从小学开始学习数学就一直使用的表达式形式。\n**什么是后缀表达式？**例如a b + ，运算符在两个操作数的后面。后缀表达式虽然看起来奇怪，不利于人阅读，但利于计算机处理。\n\u003cstrong\u003e转换为后缀表达式的好处是：\u003c/strong\u003e\n去除原来表达式中的括号，因为括号只指示运算顺序，不是实际参与计算的元素。\n使得运算顺序有规律可寻，计算机能编写出代码完成计算。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"核心步骤\"\u003e核心步骤：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e逆波兰算法的核心步骤就2个：\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e将中缀表达式转换为后缀表达式，例如输入的原始表达式是 3*(5+7) ，转换得到 3 5 7 + *\n根据后缀表达式，按照特定的计算规则得到最终计算结果\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"具体步骤\"\u003e具体步骤：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e中缀表达式转换为后缀表达式：\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e你需要设定一个栈SOP,和一个线性表 L 。SOP用于临时存储运算符和左括号分界符( ，L用于存储后缀表达式。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e遍历原始表达式中的每一个表达式元素：\n如果是操作数，则直接追加到 L中。只有 运算符 或者 分界符（ 才可以存放到 栈SOP中；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果是分界符：\n如果是左括号 ( ， 则 直接压入SOP，等待下一个最近的 右括号 与之配对。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果是右括号 ) ，则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈，丢弃它，然后从SOP中出栈，得到元素e，将e依次追加到L里。一直循环，直到出栈元素e 是 左括号 ( ，同样丢弃他。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果是运算符（用op1表示）：\n如果SOP栈顶元素（用op2表示） 不是运算符，则二者没有可比性，则直接将此运算符op1压栈。 例如栈顶是左括号 ( ，或者栈为空。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果SOP栈顶元素（用op2表示） 是运算符 ，则比较op1和 op2的优先级。如果op1 \u0026gt; op2 ，则直接将此运算符op1压栈。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果不满足op1 \u0026gt; op2，则将op2出栈，并追加到L，再试图将op1压栈，如果如果依然不满足 op1\u0026gt;新的栈顶op2，继续将新的op2弹出追加到L ，直到op1可以压入栈中为止。\u003c/p\u003e","title":"算数式计算"},{"content":" 一个简单的Makefile，可以直接Copy使用；\n一、用字符常量简化 Makefile文件：\n#定义常量 objects =main.o fun1.o #中间文件 cc=gcc #编译器 prom=main #输出文件 prom: $(objects) $(cc) $(objects) -o $(prom) main.o: main.c fun1.h $(cc) -c main.c -o main.o @echo 正在编译main文件 #前面加@避免重复输出信息 fun1.o: fun1.c fun1.h $(cc) -c fun1.c -o fun1.o @echo 正在编译其他文件 .PHONY: clean clean: -rm $(prom) $(objects) 文件夹结构：\n二、更简化的写法 Makefile文件：\nobj=main.o fun1.o cc=gcc prom=main deps=fun1.h $(prom):$(obj) $(cc) -o $(prom) $(obj) %.o:%.c $(deps) $(cc) -c $\u0026lt; -o $@ 在这里，我们用到了几个特殊的宏。首先是 %.o:%.c，这是一个模式规则，表示所有的 .o 目标都依赖于与它同名的 .c 文件（当然还有 deps 中列出的头文件）。再来就是命令部分的 $\u0026lt; 和 $@，其中 $\u0026lt; 代表的是依赖关系表中的第一项（如果我们想引用的是整个关系表，那么就应该使用 $^），具体到我们这里就是 %.c。 而 $@ 代表的是当前语句的目标，即 %.o。这样一来，make 命令就会自动将所有的 .c 源文件编译成同名的 .o 文件。不用我们一项一项去指定了。整个代码自然简洁了许多。\n文件夹结构同上；\n三、自动添加源文件 Makefile文件：\ncc=gcc prom=main deps=$(shell find ./ -name \u0026#34;*.h\u0026#34;) src=$(shell find ./ -name \u0026#34;*.c\u0026#34;) obj=$(src:%.c=%.o) $(prom):$(obj) $(cc) -o $(prom) $(obj) $.o:%.c $(deps) $(cc) -c $ 其中，shell函数主要用于执行shell命令，具体到这里就是找出当前目录下所有的.c和.h文件。而`$(src:%.c=%.o)`则是一个字符替换函数，它会将src所有的.c字串替换成.o，实际上就等于列出了所有.c文件要编译的结果。有了这两个设定，无论我们今后在该工程加入多少.c和.h文件，Makefile都能自动将其纳入到工程中来。 **文件夹结构同上；** ### 四、多文件夹下的Makefile \u0026gt; Makefile文件： ```makefile #指定编译器 CC = gcc #指定输出目标 TARGET= main.exe #指定源文件路径 DIR_INC= ./Inc DIR_SRC= ./Src DIR_BIN= ./Bin DIR_BULID= ./Build SRC=$(wildcard $(DIR_SRC)/*.c) OBJS=$(patsubst %.c,$(DIR_BULID)/%.o,$(notdir $(SRC))) #给定编译标志位 FLAGS_C:= -Og FLAGS_C+= -Wall #生成所有警告信息 #FLAGS_LD #最终目标 $(DIR_BIN)/$(TARGET): $(OBJS) @# $@ 指代当前目标 @# $ tree /f D:. │ Makefile ├─Bin │ main.exe ├─Build │ func.o │ main.o ├─Inc │ func.h └─Src func.c main.c ","permalink":"https://fan-pengfei.top/posts/%E7%AE%80%E5%8D%95makefile/","summary":"\u003cblockquote\u003e\n\u003cp\u003e一个简单的Makefile，可以直接Copy使用；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"一用字符常量简化\"\u003e一、用字符常量简化\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMakefile文件：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#定义常量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eobjects \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emain.o fun1.o \u003cspan style=\"color:#75715e\"\u003e#中间文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecc\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003egcc    \u003cspan style=\"color:#75715e\"\u003e#编译器\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprom\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emain \u003cspan style=\"color:#75715e\"\u003e#输出文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprom: \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eobjects\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eobjects\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -o \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eprom\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emain.o: main.c fun1.h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -c main.c -o main.o\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t@echo 正在编译main文件 \u003cspan style=\"color:#75715e\"\u003e#前面加@避免重复输出信息\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efun1.o: fun1.c fun1.h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -c fun1.c -o fun1.o\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t@echo 正在编译其他文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.PHONY: clean\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eclean:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t-rm \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eprom\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eobjects\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e文件夹结构：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"d41af99c56828f3430586aacbec9b96\" loading=\"lazy\" src=\"/posts/%E7%AE%80%E5%8D%95makefile/img-1.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"二更简化的写法\"\u003e二、更简化的写法\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMakefile文件：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eobj\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emain.o fun1.o\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecc\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003egcc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprom\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emain\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeps\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efun1.h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eprom\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e:\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eobj\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -o \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eprom\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eobj\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e%.o:%.c \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edeps\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -c $\u0026lt; -o $@\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e在这里，我们用到了几个特殊的宏。首先是 \u003ccode\u003e%.o:%.c\u003c/code\u003e，这是一个模式规则，表示所有的 .o 目标都依赖于与它同名的 .c 文件（当然还有 deps 中列出的头文件）。再来就是命令部分的 \u003ccode\u003e$\u0026lt;\u003c/code\u003e 和 \u003ccode\u003e$@\u003c/code\u003e，其中 \u003ccode\u003e$\u0026lt;\u003c/code\u003e 代表的是依赖关系表中的第一项（如果我们想引用的是整个关系表，那么就应该使用 \u003ccode\u003e$^\u003c/code\u003e），具体到我们这里就是 %.c。\n而 \u003ccode\u003e$@\u003c/code\u003e 代表的是当前语句的目标，即 %.o。这样一来，make 命令就会自动将所有的 .c 源文件编译成同名的 .o 文件。不用我们一项一项去指定了。整个代码自然简洁了许多。\u003c/p\u003e","title":"简单Makefile"},{"content":" 基本的正则表达式，记录一下，方便之后查询；\n基本的正则表达式： 在线练习\n1. 基本匹配 正则表达式其实就是在执行搜索时的格式，它由一些字母和数字组合而成。 例如：一个正则表达式 the，它表示一个规则：由字母t开始，接着是h，再接着是e。\n\u0026#34;the\u0026#34; =\u0026gt; The fat cat sat on the mat. 正则表达式123匹配字符串123。它逐个字符的与输入的正则表达式做比较。\n正则表达式是大小写敏感的，所以The不会匹配the。\n\u0026#34;The\u0026#34; =\u0026gt; The fat cat sat on the mat. 2. 元字符 正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思，他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍：\n元字符描述.句号匹配任意单个字符除了换行符。[ ]字符种类。匹配方括号内的任意字符。否定的字符种类。匹配除了方括号里的任意字符匹配\u0026gt;=0 个重复的在号之前的字符。+匹配\u0026gt;=1 个重复的+号前的字符。?标记?之前的字符为可选.{n,m}匹配 num 个大括号之间的字符 (n \u0026lt;= num \u0026lt;= m).(xyz)字符集，匹配与 xyz 完全相等的字符串.|或运算符，匹配符号前或后的字符.\\转义字符,用于匹配一些保留的字符 { } . * + ? ^ $ \\ |^从开始行开始匹配.$从末端开始匹配.\n2.1 点运算符 . .是元字符中最简单的例子。 .匹配任意单个字符，但不匹配换行符。 例如，表达式.ar匹配一个任意字符后面跟着是a和r的字符串。\n\u0026#34;.ar\u0026#34; =\u0026gt; The car parked in the garage. 2.2 字符集 字符集也叫做字符类。 方括号用来指定一个字符集。 在方括号中使用连字符来指定字符集的范围。 在方括号中的字符集不关心顺序。 例如，表达式[Tt]he 匹配 the 和 The。\n\u0026#34;[Tt]he\u0026#34; =\u0026gt; The car parked in the garage. 方括号的句号就表示句号。 表达式 ar[.] 匹配 ar.字符串\n\u0026#34;ar[.]\u0026#34; =\u0026gt; A garage is a good place to park a car. 2.2.1 否定字符集 一般来说 ^ 表示一个字符串的开头，但它用在一个方括号的开头的时候，它表示这个字符集是否定的。 例如，表达式[^c]ar 匹配一个后面跟着ar的除了c的任意字符。\n\u0026#34;[^c]ar\u0026#34; =\u0026gt; The car parked in the garage. 2.3 重复次数 后面跟着元字符 +，* or ? 的，用来指定匹配子模式的次数。 这些元字符在不同的情况下有着不同的意思。\n2.3.1 * 号 *号匹配 在*之前的字符出现大于等于0次。 例如，表达式 a* 匹配 0 或更多个以 a 开头的字符。表达式[a-z]* 匹配一个行中所有以小写字母开头的字符串。\n\u0026#34;[a-z]*\u0026#34; =\u0026gt; The car parked in the garage #21. *字符和.字符搭配可以匹配所有的字符.*。 *和表示匹配空格的符号\\s连起来用，如表达式\\s*cat\\s*匹配 0 或更多个空格开头和 0 或更多个空格结尾的 cat 字符串。\n\u0026#34;\\s*cat\\s*\u0026#34; =\u0026gt; The fat cat sat on the concatenation. 2.3.2 + 号 +号匹配+号之前的字符出现 \u0026gt;=1 次。 例如表达式c.+t 匹配以首字母c开头以t结尾，中间跟着至少一个字符的字符串。\n\u0026#34;c.+t\u0026#34; =\u0026gt; The fat cat sat on the mat. 2.3.3 ? 号 在正则表达式中元字符 ? 标记在符号前面的字符为可选，即出现 0 或 1 次。 例如，表达式 [T]?he 匹配字符串 he 和 The。\n\u0026#34;[T]he\u0026#34; =\u0026gt; The car is parked in the garage. \u0026#34;[T]?he\u0026#34; =\u0026gt; The car is parked in the garage. 2.4 {} 号 在正则表达式中 {} 是一个量词，常用来一个或一组字符可以重复出现的次数。 例如， 表达式 [0-9]{2,3} 匹配最少 2 位最多 3 位 0~9 的数字。\n\u0026#34;[0-9]{2,3}\u0026#34; =\u0026gt; The number was 9.9997 but we rounded it off to 10.0. 我们可以省略第二个参数。 例如，[0-9]{2,} 匹配至少两位 0~9 的数字。\n\u0026#34;[0-9]{2,}\u0026#34; =\u0026gt; The number was 9.9997 but we rounded it off to 10.0. 如果逗号也省略掉则表示重复固定的次数。 例如，[0-9]{3} 匹配 3 位数字\n\u0026#34;[0-9]{3}\u0026#34; =\u0026gt; The number was 9.9997 but we rounded it off to 10.0. 2.5 (\u0026hellip;) 特征标群 特征标群是一组写在 (...) 中的子模式。例如之前说的 {} 是用来表示前面一个字符出现指定次数。但如果在 {} 前加入特征标群则表示整个标群内的字符重复 N 次。例如，表达式 (ab)* 匹配连续出现 0 或更多个 ab。\n我们还可以在 () 中用或字符 | 表示或。例如，(c|g|p)ar 匹配 car 或 gar 或 par.\n\u0026#34;(c|g|p)ar\u0026#34; =\u0026gt; The car is parked in the garage. 2.6 | 或运算符 或运算符就表示或，用作判断条件。\n例如 (T|t)he|car 匹配 (T|t)he 或 car。\n\u0026#34;(T|t)he|car\u0026#34; =\u0026gt; The car is parked in the garage. 2.7 转码特殊字符 反斜线 \\ 在表达式中用于转码紧跟其后的字符。用于指定 { } [ ] / \\ + * . $ ^ | ? 这些特殊字符。如果想要匹配这些特殊字符则要在其前面加上反斜线 \\。\n例如 . 是用来匹配除换行符外的所有字符的。如果想要匹配句子中的 . 则要写成 \\. 以下这个例子 \\.?是选择性匹配.\n\u0026#34;(f|c|m)at\\.?\u0026#34; =\u0026gt; The fat cat sat on the mat. 2.8 锚点 在正则表达式中，想要匹配指定开头或结尾的字符串就要使用到锚点。^ 指定开头，$ 指定结尾。\n2.8.1 ^ 号 ^ 用来检查匹配的字符串是否在所匹配字符串的开头。\n例如，在 abc 中使用表达式 ^a 会得到结果 a。但如果使用 ^b 将匹配不到任何结果。因为在字符串 abc 中并不是以 b 开头。\n例如，^(T|t)he 匹配以 The 或 the 开头的字符串。\n\u0026#34;(T|t)he\u0026#34; =\u0026gt; The car is parked in the garage. \u0026#34;^(T|t)he\u0026#34; =\u0026gt; The car is parked in the garage. 2.8.2 $ 号 同理于 ^ 号，$ 号用来匹配字符是否是最后一个。\n例如，(at\\.)$ 匹配以 at. 结尾的字符串。\n\u0026#34;(at\\.)\u0026#34; =\u0026gt; The fat cat. sat. on the mat. \u0026#34;(at\\.)$\u0026#34; =\u0026gt; The fat cat. sat. on the mat. 3. 简写字符集 正则表达式提供一些常用的字符集简写。如下:\n简写描述.除换行符外的所有字符\\w匹配所有字母数字，等同于 [a-zA-Z0-9_]\\W匹配所有非字母数字，即符号，等同于： [^\\w]\\d匹配数字： [0-9]\\D匹配非数字： [^\\d]\\s匹配所有空格字符，等同于： [\\t\\n\\f\\r\\p{Z}]\\S匹配所有非空格字符： [^\\s]\\f匹配一个换页符\\n匹配一个换行符\\r匹配一个回车符\\t匹配一个制表符\\v匹配一个垂直制表符\\p匹配 CR/LF（等同于 \\r\\n），用来匹配 DOS 行终止符\n4. 零宽度断言（前后预查） 先行断言和后发断言都属于非捕获簇（不捕获文本 ，也不针对组合计进行计数）。 先行断言用于判断所匹配的格式是否在另一个确定的格式之前，匹配结果不包含该确定格式（仅作为约束）。\n例如，我们想要获得所有跟在 符号后的数字，我们可以使用正后发断言(? 开头，之后跟着 0,1,2,3,4,5,6,7,8,9,. 这些字符可以出现大于等于 0 次。\n零宽度断言如下：\n符号描述?=正先行断言-存在?!负先行断言-排除?\u0026lt;=正后发断言-存在?\u0026lt;!负后发断言-排除\n4.1 ?=\u0026hellip; 正先行断言 ?=... 正先行断言，表示第一部分表达式之后必须跟着 ?=...定义的表达式。\n返回结果只包含满足匹配条件的第一部分表达式。 定义一个正先行断言要使用 ()。在括号内部使用一个问号和等号： (?=...)。\n正先行断言的内容写在括号中的等号后面。 例如，表达式 (T|t)he(?=\\sfat) 匹配 The 和 the，在括号中我们又定义了正先行断言 (?=\\sfat) ，即 The 和 the 后面紧跟着 (空格)fat。\n\u0026#34;(T|t)he(?=\\sfat)\u0026#34; =\u0026gt; The fat cat sat on the mat. 4.2 ?!\u0026hellip; 负先行断言 负先行断言 ?! 用于筛选所有匹配结果，筛选条件为 其后不跟随着断言中定义的格式。 正先行断言 定义和 负先行断言 一样，区别就是 = 替换成 ! 也就是 (?!...)。\n表达式 (T|t)he(?!\\sfat) 匹配 The 和 the，且其后不跟着 (空格)fat。\n\u0026#34;(T|t)he(?!\\sfat)\u0026#34; =\u0026gt; The fat cat sat on the mat. 4.3 ?\u0026lt;= \u0026hellip; 正后发断言 正后发断言 记作`(? The fat cat sat on the mat.\n#### 4.4 ?\u0026lt;!... 负后发断言 负后发断言 记作 `(? The cat sat on cat. 5. 标志 标志也叫模式修正符，因为它可以用来修改表达式的搜索结果。 这些标志可以任意的组合使用，它也是整个正则表达式的一部分。\n标志描述i忽略大小写。g全局搜索。m多行修饰符：锚点元字符 ^$ 工作范围在每行的起始。\n5.1 忽略大小写（Case Insensitive） 修饰语 i 用于忽略大小写。 例如，表达式 /The/gi 表示在全局搜索 The，在后面的 i 将其条件修改为忽略大小写，则变成搜索 the 和 The，g 表示全局搜索。\n\u0026#34;The\u0026#34; =\u0026gt; The fat cat sat on the mat. \u0026#34;/The/gi\u0026#34; =\u0026gt; The fat cat sat on the mat. 5.2 全局搜索（Global search） 修饰符 g 常用于执行一个全局搜索匹配，即（不仅仅返回第一个匹配的，而是返回全部）。 例如，表达式 /.(at)/g 表示搜索 任意字符（除了换行）+ at，并返回全部结果。\n\u0026#34;/.(at)/\u0026#34; =\u0026gt; The fat cat sat on the mat. \u0026#34;/.(at)/g\u0026#34; =\u0026gt; The fat cat sat on the mat. 5.3 多行修饰符（Multiline） 多行修饰符 m 常用于执行一个多行匹配。\n像之前介绍的 (^,$) 用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效，我们需要用到多行修饰符 m。\n例如，表达式 /at(.)?$/gm 表示小写字符 a 后跟小写字符 t ，末尾可选除换行符外任意字符。根据 m 修饰符，现在表达式匹配每行的结尾。\n\u0026#34;/.at(.)?$/\u0026#34; =\u0026gt; The fat cat sat on the mat. \u0026#34;/.at(.)?$/gm\u0026#34; =\u0026gt; The fat cat sat on the mat. 6. 贪婪匹配与惰性匹配（Greedy vs lazy matching）\n正则表达式默认采用贪婪匹配模式，在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式。\n\u0026#34;/(.*at)/\u0026#34; =\u0026gt; The fat cat sat on the mat. \u0026#34;/(.*?at)/\u0026#34; =\u0026gt; The fat cat sat on the mat. ","permalink":"https://fan-pengfei.top/posts/%E5%9F%BA%E6%9C%AC%E7%9A%84%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003e基本的正则表达式，记录一下，方便之后查询；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"基本的正则表达式\"\u003e基本的正则表达式：\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https%3A//regex101.com/r/DOc5Nu/1\"\u003e在线练习\u003c/a\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"1-基本匹配\"\u003e1. 基本匹配\u003c/h3\u003e\n\u003cp\u003e正则表达式其实就是在执行搜索时的格式，它由一些字母和数字组合而成。 例如：一个正则表达式 \u003ccode\u003ethe\u003c/code\u003e，它表示一个规则：由字母\u003ccode\u003et\u003c/code\u003e开始，接着是\u003ccode\u003eh\u003c/code\u003e，再接着是\u003ccode\u003ee\u003c/code\u003e。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026#34;the\u0026#34; =\u0026gt; The fat cat sat on the mat.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e正则表达式\u003ccode\u003e123\u003c/code\u003e匹配字符串\u003ccode\u003e123\u003c/code\u003e。它逐个字符的与输入的正则表达式做比较。\u003c/p\u003e\n\u003cp\u003e正则表达式是大小写敏感的，所以\u003ccode\u003eThe\u003c/code\u003e不会匹配\u003ccode\u003ethe\u003c/code\u003e。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026#34;The\u0026#34; =\u0026gt; The fat cat sat on the mat.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"2-元字符\"\u003e2. 元字符\u003c/h3\u003e\n\u003cp\u003e正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思，他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍：\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e元字符描述\u003c/strong\u003e.句号匹配任意单个字符除了换行符。[ ]字符种类。匹配方括号内的任意字符。\u003ca href=\"#fn_\"\u003e\u003c/a\u003e否定的字符种类。匹配除了方括号里的任意字符\u003cem\u003e匹配\u0026gt;=0 个重复的在\u003c/em\u003e号之前的字符。+匹配\u0026gt;=1 个重复的+号前的字符。?标记?之前的字符为可选.{n,m}匹配 num 个大括号之间的字符 (n \u0026lt;= num \u0026lt;= m).(xyz)字符集，匹配与 xyz 完全相等的字符串.|或运算符，匹配符号前或后的字符.\\转义字符,用于匹配一些保留的字符 \u003ccode\u003e{ } . * + ? ^ $ \\ |\u003c/code\u003e^从开始行开始匹配.$从末端开始匹配.\u003c/p\u003e\n\u003ch4 id=\"21-点运算符-\"\u003e2.1 点运算符 .\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003e.\u003c/code\u003e是元字符中最简单的例子。 \u003ccode\u003e.\u003c/code\u003e匹配任意单个字符，但不匹配换行符。 例如，表达式\u003ccode\u003e.ar\u003c/code\u003e匹配一个任意字符后面跟着是\u003ccode\u003ea\u003c/code\u003e和\u003ccode\u003er\u003c/code\u003e的字符串。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026#34;.ar\u0026#34; =\u0026gt; The car parked in the garage.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"22-字符集\"\u003e2.2 字符集\u003c/h4\u003e\n\u003cp\u003e字符集也叫做字符类。 方括号用来指定一个字符集。 在方括号中使用连字符来指定字符集的范围。 在方括号中的字符集不关心顺序。 例如，表达式\u003ccode\u003e[Tt]he\u003c/code\u003e 匹配 \u003ccode\u003ethe\u003c/code\u003e 和 \u003ccode\u003eThe\u003c/code\u003e。\u003c/p\u003e","title":"基本的正则表达式"},{"content":" 最近在学Linux，发现grep命令很常用，所以记录一下，方便之后查询；\ngrep命令常见用法 1、字符串搜索： 在文件中搜索一个单词，命令会返回一个包含 “match_pattern” 的文本行：\ngrep match_pattern file_name grep \u0026#34;match_pattern\u0026#34; file_name 例子： 2、在多个文件中查找字符串： grep \u0026#34;match_pattern\u0026#34; file_1 file_2 file_3 ... 例子： 3、输出除之外的所有行 -v 选项： grep -v \u0026#34;match_pattern\u0026#34; file_name 例子： 4、标记匹配颜色 —color=auto 选项：\ngrep \u0026#34;match_pattern\u0026#34; file_name --color=auto 例子： 5、使用正则表达式： 使用正则表达式 -E 选项：\ngrep -E \u0026#34;[1-9]+\u0026#34; # 或 egrep \u0026#34;[1-9]+\u0026#34; 使用正则表达式 -P 选项：\ngrep -P \u0026#34;(\\d{3}\\-){2}\\d{4}\u0026#34; file_name 只输出文件中匹配到的部分 -o 选项：\necho this is a test line. | grep -o -E \u0026#34;[a-z]+\\.\u0026#34; line. echo this is a test line. | egrep -o \u0026#34;[a-z]+\\.\u0026#34; line. 6、统计文件或者文本中包含匹配字符串的行数： -c 选项：\ngrep -c \u0026#34;text\u0026#34; file_name 7、搜索命令行历史记录中 输入过 git 命令的记录： history | grep git 8、输出包含匹配字符串的行数 -n 选项： grep \u0026#34;text\u0026#34; -n file_name # 或 cat file_name | grep \u0026#34;text\u0026#34; -n #多个文件 grep \u0026#34;text\u0026#34; -n file_1 file_2 9、打印样式匹配所位于的字符或字节偏移： echo gun is not unix | grep -b -o \u0026#34;not\u0026#34; 7:not #一行中字符串的字符便宜是从该行的第一个字符开始计算，起始值为0。选项 **-b -o** 一般总是配合使用。 10、搜索多个文件并查找匹配文本在哪些文件中： grep -l \u0026#34;text\u0026#34; file1 file2 file3... 例子： 11、grep递归搜索文件： 在多级目录中对文本进行递归搜索：\ngrep \u0026#34;text\u0026#34; . -r -n # .表示当前目录。 例子：\n12、忽略匹配样式中的字符大小写： echo \u0026#34;hello world\u0026#34; | grep -i \u0026#34;HELLO\u0026#34; # hello 13、选项 -e 制动多个匹配样式： echo this is a text line | grep -e \u0026#34;is\u0026#34; -e \u0026#34;line\u0026#34; -o is is line #也可以使用 **-f** 选项来匹配多个样式，在样式文件中逐行写出需要匹配的字符。 cat patfile aaa bbb echo aaa bbb ccc ddd eee | grep -f patfile -o 14、在grep搜索结果中包括或者排除指定文件： # 只在目录中所有的.php和.html文件中递归搜索字符\u0026#34;main()\u0026#34; grep \u0026#34;main()\u0026#34; . -r --include *.{php,html} # 在搜索结果中排除所有README文件 grep \u0026#34;main()\u0026#34; . -r --exclude \u0026#34;README\u0026#34; # 在搜索结果中排除filelist文件列表里的文件 grep \u0026#34;main()\u0026#34; . -r --exclude-from filelist 15、使用0值字节后缀的grep与xargs： # 测试文件： echo \u0026#34;aaa\u0026#34; \u0026gt; file1 echo \u0026#34;bbb\u0026#34; \u0026gt; file2 echo \u0026#34;aaa\u0026#34; \u0026gt; file3 grep \u0026#34;aaa\u0026#34; file* -lZ | xargs -0 rm # 执行后会删除file1和file3，grep输出用-Z选项来指定以0值字节作为终结符文件名（\\0），xargs -0 读取输入并用0值字节终结符分隔文件名，然后删除匹配文件，-Z通常和-l结合使用。 15、grep静默输出： grep -q \u0026#34;test\u0026#34; filename # 不会输出任何信息，如果命令运行成功返回0，失败则返回非0值。一般用于条件测试。 16、打印出匹配文本之前或者之后的行： # 显示匹配某个结果之后的3行，使用 -A 选项： seq 10 | grep \u0026#34;5\u0026#34; -A 3 5 6 7 8 # 显示匹配某个结果之前的3行，使用 -B 选项： seq 10 | grep \u0026#34;5\u0026#34; -B 3 2 3 4 5 # 显示匹配某个结果的前三行和后三行，使用 -C 选项： seq 10 | grep \u0026#34;5\u0026#34; -C 3 2 3 4 5 6 7 8 # 如果匹配结果有多个，会用“--”作为各匹配结果之间的分隔符： echo -e \u0026#34;a\\nb\\nc\\na\\nb\\nc\u0026#34; | grep a -A 1 a b -- a b 补充说明 grep （global search regular expression(RE) and print out the line，全面搜索正则表达式并把行打印出来）是一种强大的文本搜索工具，它能使用正则表达式搜索文本，并把匹配的行打印出来。用于过滤/搜索的特定字符。可使用正则表达式能配合多种命令使用，使用上十分灵活。\n选项 -a --text # 不要忽略二进制数据。 -A --after-context= # 除了显示符合范本样式的那一行之外，并显示该行之后的内容。 -b --byte-offset # 在显示符合范本样式的那一行之外，并显示该行之前的内容。 -B --before-context= # 除了显示符合样式的那一行之外，并显示该行之前的内容。 -c --count # 计算符合范本样式的列数。 -C --context=或- # 除了显示符合范本样式的那一列之外，并显示该列之前后的内容。 -d --directories= # 当指定要查找的是目录而非文件时，必须使用这项参数，否则grep命令将回报信息并停止动作。 -e --regexp= # 指定字符串作为查找文件内容的范本样式。 -E --extended-regexp # 将范本样式为延伸的普通表示法来使用，意味着使用能使用扩展正则表达式。 -f --file= # 指定范本文件，其内容有一个或多个范本样式，让grep查找符合范本条件的文件内容，格式为每一列的范本样式。 -F --fixed-regexp # 将范本样式视为固定字符串的列表。 -G --basic-regexp # 将范本样式视为普通的表示法来使用。 -h --no-filename # 在显示符合范本样式的那一列之前，不标示该列所属的文件名称。 -H --with-filename # 在显示符合范本样式的那一列之前，标示该列的文件名称。 -i --ignore-case # 忽略字符大小写的差别。 -l --file-with-matches # 列出文件内容符合指定的范本样式的文件名称。 -L --files-without-match # 列出文件内容不符合指定的范本样式的文件名称。 -n --line-number # 在显示符合范本样式的那一列之前，标示出该列的编号。 -P --perl-regexp # PATTERN 是一个 Perl 正则表达式 -q --quiet或--silent # 不显示任何信息。 -R/-r --recursive # 此参数的效果和指定“-d recurse”参数相同。 -s --no-messages # 不显示错误信息。 -v --revert-match # 反转查找。 -V --version # 显示版本信息。 -w --word-regexp # 只显示全字符合的列。 -x --line-regexp # 只显示全列符合的列。 -y # 此参数效果跟“-i”相同。 -o # 只输出文件中匹配到的部分。 -m --max-count= # 找到num行结果后停止查找，用来限制匹配行数 规则表达式 ^ # 锚定行的开始 如：\u0026#39;^grep\u0026#39;匹配所有以grep开头的行。 $ # 锚定行的结束 如：\u0026#39;grep$\u0026#39; 匹配所有以grep结尾的行。 . # 匹配一个非换行符的字符 如：\u0026#39;gr.p\u0026#39;匹配gr后接一个任意字符，然后是p。 * # 匹配零个或多个先前字符 如：\u0026#39;*grep\u0026#39;匹配所有一个或多个空格后紧跟grep的行。 .* # 一起用代表任意字符。 [] # 匹配一个指定范围内的字符，如\u0026#39;[Gg]rep\u0026#39;匹配Grep和grep。 [^] # 匹配一个不在指定范围内的字符，如：\u0026#39;[^A-FH-Z]rep\u0026#39;匹配不包含A-R和T-Z的一个字母开头，紧跟rep的行。 \\(..\\) # 标记匹配字符，如\u0026#39;\\(love\\)\u0026#39;，love被标记为1。 \\ # 锚定单词的结束，如\u0026#39;grep\\\u0026gt;\u0026#39;匹配包含以grep结尾的单词的行。 x\\{m\\} # 重复字符x，m次，如：\u0026#39;0\\{5\\}\u0026#39;匹配包含5个o的行。 x\\{m,\\} # 重复字符x,至少m次，如：\u0026#39;o\\{5,\\}\u0026#39;匹配至少有5个o的行。 x\\{m,n\\} # 重复字符x，至少m次，不多于n次，如：\u0026#39;o\\{5,10\\}\u0026#39;匹配5--10个o的行。 \\w # 匹配文字和数字字符，也就是[A-Za-z0-9]，如：\u0026#39;G\\w*p\u0026#39;匹配以G后跟零个或多个文字或数字字符，然后是p。 \\W # \\w的反置形式，匹配一个或多个非单词字符，如点号句号等。 \\b # 单词锁定符，如: \u0026#39;\\bgrep\\b\u0026#39;只匹配grep。 ","permalink":"https://fan-pengfei.top/posts/linux_grep%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近在学Linux，发现grep命令很常用，所以记录一下，方便之后查询；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"grep命令常见用法\"\u003egrep命令常见用法\u003c/h3\u003e\n\u003ch4 id=\"1字符串搜索\"\u003e1、字符串搜索：\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e在文件中搜索一个单词，命令会返回一个包含 “match_pattern” 的文本行：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep match_pattern file_name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;match_pattern\u0026#34;\u003c/span\u003e file_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e例子：\n\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/linux_grep%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/img-1.png\"\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"2在多个文件中查找字符串\"\u003e2、在多个文件中查找字符串：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;match_pattern\u0026#34;\u003c/span\u003e file_1 file_2 file_3 ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e例子：\n\u003cimg alt=\"2\" loading=\"lazy\" src=\"/posts/linux_grep%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/img-2.png\"\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"3输出除之外的所有行--v-选项\"\u003e3、输出除之外的所有行 -v 选项：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;match_pattern\u0026#34;\u003c/span\u003e file_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e例子：\n\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/linux_grep%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/img-3.png\"\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"4标记匹配颜色\"\u003e4、标记匹配颜色\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e—color=auto 选项：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;match_pattern\u0026#34;\u003c/span\u003e file_name --color\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eauto\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e例子：\n\u003cimg alt=\"3\" loading=\"lazy\" src=\"/posts/linux_grep%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/img-4.png\"\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"5使用正则表达式\"\u003e5、使用正则表达式：\u003c/h4\u003e\n\u003cp\u003e使用正则表达式 \u003cstrong\u003e-E\u003c/strong\u003e 选项：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -E \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[1-9]+\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 或\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eegrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[1-9]+\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e使用正则表达式 \u003cstrong\u003e-P\u003c/strong\u003e 选项：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -P \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;(\\d{3}\\-){2}\\d{4}\u0026#34;\u003c/span\u003e file_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e只输出文件中匹配到的部分 \u003cstrong\u003e-o\u003c/strong\u003e 选项：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho this is a test line. | grep -o -E \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[a-z]+\\.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eline.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho this is a test line. | egrep -o \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[a-z]+\\.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eline.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"6统计文件或者文本中包含匹配字符串的行数\"\u003e6、统计文件或者文本中包含匹配字符串的行数：\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e-c 选项：\u003c/strong\u003e\u003c/p\u003e","title":"Linux_grep命令使用"},{"content":" 前些天做微机课设，给小一加了几个功能，其中一个重要功能是显示中文文本；\n字体取模： 屏幕要显示图案，例如某个汉字或者数字、图案等，都需要对图案进行取模操作；\n使用的是LvglFontTool工具，LVGL官网的字体转化用于单个字体取模比较方便；批量的话，使用这个离线取模软件比较方便；\n操作界面如下所示：\n步骤还是很简单的：\n首先选择字体，包括一个TFF字体文件还有选择需要取模的字体大小；\n然后加入汉字，我是将所有常用的汉字都加入了；\n然后在右边配置一些选项，按照图片上的配置就可以；\n最后点击开始转换就可以生成一个myFont.c和myFont.bin文件，bin文件加载到SPI FLASH中，C文件加入Keil工程即可;\n如果只需要显示数量比较少的文本，取模后得到的数组可以直接放在一个.c或者.h文件中，直接下载到单片机的FLASH中即可，但是如果要显示各种不同样式和不同大小的字体，取模后得到的文件会很大，加载到FLASH中存放是不合理的。\n文件放置： 小一这一版的硬件是带了一个8M的SPI FLASH，所以取模后的数据可以放在这块SPI FLASH中，可以用哪些方法通过单片机读取bin文件中的内容呢？\n一般是有两个方法：\n放入移植好的Fatfs文件系统中，通过文件系统提供的接口读取该bin文件；\n加载进SPI FLASH中，直接通过最底层的读取函数读取；\n两种方案各有优缺点，第一种更换字体取模文件很方便，但是由于字体取模文件会频繁被读取，所以这个方案的效率会比较差；第二种方案更换字体取模文件比较麻烦，但是读取的效率会高不少。我是选择了第二种方案，第一种我也试了，效率确实不是很高。\n对于第二种方案，首先要将取模文件从PC机放入SPI FLASH中，我采用的方案是将SPI FLASH划分为两部分：\n前4MB 后4MB\n用于存储字体取模数据 用于建立文件系统\n然后将SPI FLASH模拟为USB设备，插入PC机，会弹出一个U盘，将字体文件拖入；然后通过一个函数，将bin文件分段读取并分段写入SPI FLASH的前4MB部分中，具体函数如下所示：\nvoid write_to_flash(void) { uint8_t i; f_res = f_open(\u0026amp;file1, \u0026#34;myFont.bin\u0026#34;, FA_READ);//打开对应文件 count_f = 0; for (i = 0; i 如果有多个字体文件，可以将写入的地址偏移一个大小即可。 ## 文件读取： 字体取模文件读取只需要修改`myFont.c`中的一个函数： ```c static uint8_t __g_font_buf[324]; //如bin文件存在SPI FLASH可使用此buff static uint8_t *__user_font_getdata(int offset, int size) { //如字模保存在SPI FLASH, SPIFLASH_Read(__g_font_buf,offset,size); //如字模已加载到SDRAM,直接返回偏移地址即可如:return (uint8_t*)(sdram_fontddr+offset); my_W25QXX_Read(__g_font_buf, offset, size); return __g_font_buf; } 如果有多个字体文件，可以在对应的myFont文件中将读出的地址偏移一个大小即可。\n字体使用： 使用LVGL提供的函数接口即可调用：\nLV_FONT_DECLARE(myFont24); // 字体声明,24号字体 lv_style_init(\u0026amp;font_style_24); lv_style_set_text_font(\u0026amp;font_style_24, LV_STATE_DEFAULT, \u0026amp;myFont24); lv_obj_t *scr = lv_disp_get_scr_act(NULL); /* 获取当前屏幕 */ lv_obj_t *label1 = lv_label_create(scr, NULL); /* 创建 label 控件 */ lv_obj_set_pos(label1, 0, 0); /* 设置控件的坐标 */ sprintf((uint8_t *)text_temp, \u0026#34;你好，我是小一!\u0026#34;); lv_label_set_text(label1, text_temp);\t/* 设置文字 */ lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0); /* 设置控件的对齐方式-相对坐标 */ lv_obj_add_style(label1, LV_LABEL_PART_MAIN, \u0026amp;font_style_24); // 应用效果风格 这里边有很多坑：\n第一个是文本的字体编码格式，要修改为UTF-8编码格式才能正常显示；\n第二个是换行符要进行修改，Windows下换行是\\r\\n，而LVGL中换行是\\n，所以要进行一下转换(可以使用VScode完成)；\n最后： 刚开始字体取模还有读取取模文件就卡了好久，首先是文字编码问题，然后是myFont中的字体读取函数卡死，然后还有换行符的问题，然后中间莫名其妙系统还卡死很多次，初步判断是LVGL中的任务切换函数不能放在优先级较高的定时器中断中，必须要放在while循环中才可以正常运行，然后就是LVGL会卡死在某一个函数中，按照提示修改一般都可以解决问题。\n在项目中遇到了很多很多问题，花费了很大的精力才解决，不过这也是一个学习的过程，学到了很多知识还有解决问题时的办法。\n最后附上小一的图片还有开源代码地址:\n图片： 开源地址： GitHub地址\n","permalink":"https://fan-pengfei.top/posts/lvgl%E6%98%BE%E7%A4%BAtxt%E6%96%87%E6%9C%AC%E6%8C%87%E5%AE%9A%E5%AD%97%E4%BD%93/","summary":"\u003cblockquote\u003e\n\u003cp\u003e前些天做微机课设，给小一加了几个功能，其中一个重要功能是显示中文文本；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"字体取模\"\u003e字体取模：\u003c/h2\u003e\n\u003cp\u003e屏幕要显示图案，例如某个汉字或者数字、图案等，都需要对图案进行取模操作；\u003c/p\u003e\n\u003cp\u003e使用的是\u003ccode\u003eLvglFontTool\u003c/code\u003e工具，\u003ccode\u003eLVGL\u003c/code\u003e官网的字体转化用于单个字体取模比较方便；批量的话，使用这个离线取模软件比较方便；\u003c/p\u003e\n\u003cp\u003e操作界面如下所示：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"c941fa311425ab16f5bc3f354d99fc2\" loading=\"lazy\" src=\"/posts/lvgl%E6%98%BE%E7%A4%BAtxt%E6%96%87%E6%9C%AC%E6%8C%87%E5%AE%9A%E5%AD%97%E4%BD%93/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e步骤还是很简单的：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e首先选择字体，包括一个\u003ccode\u003eTFF\u003c/code\u003e字体文件还有选择需要取模的字体大小；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e然后加入汉字，我是将所有常用的汉字都加入了；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e然后在右边配置一些选项，按照图片上的配置就可以；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e最后点击开始转换就可以生成一个\u003ccode\u003emyFont.c\u003c/code\u003e和\u003ccode\u003emyFont.bin\u003c/code\u003e文件，\u003ccode\u003ebin\u003c/code\u003e文件加载到\u003ccode\u003eSPI FLASH\u003c/code\u003e中，C文件加入\u003ccode\u003eKeil\u003c/code\u003e工程即可;\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"efcc7c6005ad5950310dbec4ab39cd3\" loading=\"lazy\" src=\"/posts/lvgl%E6%98%BE%E7%A4%BAtxt%E6%96%87%E6%9C%AC%E6%8C%87%E5%AE%9A%E5%AD%97%E4%BD%93/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e如果只需要显示数量比较少的文本，取模后得到的数组可以直接放在一个\u003ccode\u003e.c\u003c/code\u003e或者\u003ccode\u003e.h\u003c/code\u003e文件中，直接下载到单片机的\u003ccode\u003eFLASH\u003c/code\u003e中即可，但是如果要显示各种不同样式和不同大小的字体，取模后得到的文件会很大，加载到\u003ccode\u003eFLASH\u003c/code\u003e中存放是不合理的。\u003c/p\u003e\n\u003ch2 id=\"文件放置\"\u003e文件放置：\u003c/h2\u003e\n\u003cp\u003e小一这一版的硬件是带了一个\u003ccode\u003e8M\u003c/code\u003e的\u003ccode\u003eSPI FLASH\u003c/code\u003e，所以取模后的数据可以放在这块\u003ccode\u003eSPI FLASH\u003c/code\u003e中，可以用哪些方法通过单片机读取\u003ccode\u003ebin\u003c/code\u003e文件中的内容呢？\u003c/p\u003e\n\u003cp\u003e一般是有两个方法：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e放入移植好的\u003ccode\u003eFatfs\u003c/code\u003e文件系统中，通过文件系统提供的接口读取该bin文件；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e加载进\u003ccode\u003eSPI FLASH\u003c/code\u003e中，直接通过最底层的读取函数读取；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e两种方案各有优缺点，第一种更换字体取模文件很方便，但是由于字体取模文件会频繁被读取，所以这个方案的效率会比较差；第二种方案更换字体取模文件比较麻烦，但是读取的效率会高不少。我是选择了第二种方案，第一种我也试了，效率确实不是很高。\u003c/p\u003e\n\u003cp\u003e对于第二种方案，首先要将取模文件从PC机放入\u003ccode\u003eSPI FLASH\u003c/code\u003e中，我采用的方案是将\u003ccode\u003eSPI FLASH\u003c/code\u003e划分为两部分：\u003c/p\u003e\n\u003cp\u003e前4MB\n后4MB\u003c/p\u003e\n\u003cp\u003e用于存储字体取模数据\n用于建立文件系统\u003c/p\u003e\n\u003cp\u003e然后将\u003ccode\u003eSPI FLASH\u003c/code\u003e模拟为USB设备，插入PC机，会弹出一个U盘，将字体文件拖入；然后通过一个函数，将\u003ccode\u003ebin\u003c/code\u003e文件分段读取并分段写入\u003ccode\u003eSPI FLASH\u003c/code\u003e的前\u003ccode\u003e4MB\u003c/code\u003e部分中，具体函数如下所示：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewrite_to_flash\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    f_res \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ef_open\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003efile1, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;myFont.bin\u0026#34;\u003c/span\u003e, FA_READ);\u003cspan style=\"color:#75715e\"\u003e//打开对应文件\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    count_f \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; i  \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e如果有多个字体文件，可以将写入的地址偏移一个大小即可。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## 文件读取：\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e字体取模文件读取只需要修改`\u003c/span\u003emyFont.c\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e`中的一个函数：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ec\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e __g_font_buf[\u003cspan style=\"color:#ae81ff\"\u003e324\u003c/span\u003e]; \u003cspan style=\"color:#75715e\"\u003e//如bin文件存在SPI FLASH可使用此buff\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint8_t\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e__user_font_getdata\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e offset, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e size)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//如字模保存在SPI FLASH, SPIFLASH_Read(__g_font_buf,offset,size);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//如字模已加载到SDRAM,直接返回偏移地址即可如:return (uint8_t*)(sdram_fontddr+offset);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003emy_W25QXX_Read\u003c/span\u003e(__g_font_buf, offset, size);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e __g_font_buf;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e如果有多个字体文件，可以在对应的\u003ccode\u003emyFont\u003c/code\u003e文件中将读出的地址偏移一个大小即可。\u003c/p\u003e","title":"lvgl显示txt文本(指定字体)"},{"content":" 前些天看了一个RobMaster哨兵机器人的开源代码，里边用了C语言结构体定义函数指针，看得有点迷糊，所以自己查了一些相关的资料；\nC语言结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。所以，标准C中的结构体是不允许包含成员函数的，当然C++中的结构体对此进行了扩展。那么，我们在C语言的结构体中，只能通过定义函数指针的方式，用函数指针指向相应函数，以此达到调用函数的目的。\n结构体定义： struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; 完整例子： #include struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; int add_demo(int i, int j) { return i+j; } int sub_demo(int i, int j) { return i-j; } int main(void) { struct data_demo data = {0, add_demo, sub_demo}; // 也可以如下初始化结构体 完全等价 /* struct data_demo data = { .result = 0, .add = add_demo, .sub = sub_demo }; */ data.result = data.add(20, 30); printf(\u0026#34;add:\\t%d\\n\u0026#34;, data.result); data.result = data.sub(30, 20); printf(\u0026#34;sub:\\t%d\\n\u0026#34;, data.result); return 0; } 运行结果： 可以看到，使用struct data_demo data = {0, add_demo, sub_demo}初始化结构体后，使用data.add(int,int)就可以调用add_demo(int,int)这个函数；优点是代码结构会比较清晰，之前写过Linux下的LCD驱动，感觉那个地方就这样用了。\n","permalink":"https://fan-pengfei.top/posts/%E7%BB%93%E6%9E%84%E4%BD%93%E4%B8%AD%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88/","summary":"\u003cblockquote\u003e\n\u003cp\u003e前些天看了一个RobMaster哨兵机器人的开源代码，里边用了C语言结构体定义函数指针，看得有点迷糊，所以自己查了一些相关的资料；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eC语言结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。所以，标准C中的结构体是不允许包含成员函数的，当然C++中的结构体对此进行了扩展。那么，我们在C语言的结构体中，只能通过定义函数指针的方式，用函数指针指向相应函数，以此达到调用函数的目的。\u003c/p\u003e\n\u003ch2 id=\"结构体定义\"\u003e结构体定义：\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e data_demo\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e result;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eadd)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003esub)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"完整例子\"\u003e完整例子：\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003estruct data_demo\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e result;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eadd)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003esub)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd_demo\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e j)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e i\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003ej;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esub_demo\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e j)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e i\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ej;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e data_demo data \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e  {\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, add_demo, sub_demo};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 也可以如下初始化结构体 完全等价\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    struct data_demo data =\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        .result = 0,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        .add = add_demo,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        .sub = sub_demo\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    };\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data.result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e data.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;add:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\t\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%d\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, data.result);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data.result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e data.\u003cspan style=\"color:#a6e22e\"\u003esub\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sub:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\t\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%d\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, data.result);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"运行结果\"\u003e运行结果：\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"运行结果\" loading=\"lazy\" src=\"/posts/%E7%BB%93%E6%9E%84%E4%BD%93%E4%B8%AD%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88/img-1.png\"\u003e\u003c/p\u003e","title":"结构体中定义函数指针"},{"content":" 常用通讯协议(SPI、IIC、UART)；\n一、USART和UART: USART:通用同步异步收发器，USART是一个串行通信设备，可以灵活地与外部设备进行全双工数据交换。\nUART: 通用异步收发器，异步串行通信口(UART)就是我们在嵌入式中常说的串口，它还是一种通用的数据通信议。\n异步通讯时二者无区别，同步通讯时USART可以提供主动时钟。\n均为全双工通信。\n起始位：先发出一个逻辑”0”的信号，表示传输数据的开始。 数据位：传输N bits。 校验位(可选)：数据位加上这一位后，使得“1”的位数应为偶数(偶校验)或奇数(奇校验)，以此来校验数据传送的正确性。 如传输“A”(01000001)为例,”A”字符的8个bit位中有两个1。当为奇数校验时该位为1；当为偶数校验时该位为0。 停止位：它是一帧数据的结束标志。可以是1bit、1.5bit、2bit的空闲电平。 空闲位：没有数据传输时线路上的电平状态。为逻辑1。 传输方向：即数据是从高位(MSB)开始传输还是从低位(LSB)开始传输。比如传输“A”如果是MSB那么就是01000001，如果是LSB那么就是10000010 帧间隔：即传送数据的帧与帧之间的间隔大小，可以以位为计量也可以用时间(知道波特率那么位数和时间可以换算)。比如传送”A”完后，这为一帧数据，再传”B”，那么A与B之间的间隔即为帧间隔。 波特率定义：有效数据信号调制载波的速率，每秒传输1或0的个数； 例如：串口传输速率为9600bps，每秒可传输多少字节？ 起始位：1 数据位：8 停止位：1 校验位：0 传输1字节数据，需要传输10bit，因此： 9600 ÷ 10 = 960Byte\n二、IIC通讯协议： IIC协议为半双工协议。\n全双工指在发送数据的同时也能够接收数据；\n半双工就是指一个时间段内只有一个动作发生；\n数据有效传输在scl信号的高电平期间，sda数据线保持稳定，在scl为低电平时允许sda数据线变化。\n起始条件在scl为高电平期间，sda出现下降沿，则为起始信号。\n结束条件在scl为高电平期间，sda出现上升沿，则为结束信号。\n注意：注意起始和终止信号都是由主机发出的，总线在起始条件之后，视为忙状态，在停止条件之后被视为空闲状态。 应答（ACK，Acknowledgement）。即确认字符，在数据通信中，接收站发给发送站的一种传输类控制字符。主机每向从机发送完一个字节的数据，主机总是需要等待从机给出一个应答信号，来确认从机是否成功接收到了数据，从机应答主机所需要的时钟也是由主机提供的，应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期，低电平0表示应答，1表示非应答。，需要应答时，数据发出方将SDA总线设置为3态输入，由于IIC总线上有上拉电阻，因此此时总线默认高电平，若数据接收方正确接收到数据，则数据接收方将SDA总线拉低，以示正确应答。\nIIC传输时时从MSB开始传输到LSB结束。MSB是Most Significant Bit的缩写，最高有效位。在二进制数中，MSB是最高加权位。与十进制数字中最左边的一位类似。通常，MSB位于二进制数的最左侧，LSB位于二进制数的最右侧。LSB，英文 least significant bit，中文义最低有效位。\n写时序：\n​ ID_Address, REG_Address, W_REG_Data\n产生start位； 传送器件地址ID_Address，器件地址的最后一位为数据的传输方向位，R/W，低电平0表示主机往从机写数据（W），1表示主机从从机读数据（R）。ACK应答，应答是从机发送给主机的应答，这里不用管； 传送写入器件寄存器地址，即数据要写入的位置。同样ACK应答不用管； 传送要写入的数据。ACK应答不用管； 产生stop信号；\n读时序：\n​ {ID_Address + REG_Address} + {ID_Address + R_REG_Data}\n产生start信号 传送器件地址（写ID_Address），ACK。 传送字地址（写REG_Address），ACK。 再次产生start信号 再传送一次器件地址，ACK。 读取一个字节的数据，读数据最后结束前无应答ACK信号。 产生stop信号。\n三、SPI通讯协议 引脚定义： 四根逻辑线：\nMISO：Master input slave output 主机输入，从机输出（数据来自从机）；\nMOSI：Master output slave input 主机输出，从机输入（数据来自主机）；\nSCLK ：Serial Clock 串行时钟信号，由主机产生发送给从机；\nSS：Slave Select 片选信号，由主机发送，以控制与哪个从机通信，通常是低电平有效信号。\n其他制造商可能会遵循其他命名规则，但是最终他们指的相同的含义。以下是一些常用术语；\nMISO也可以是SIMO，DOUT，DO，SDO或SO（在主机端）;\nMOSI也可以是SOMI，DIN，DI，SDI或SI（在主机端）;\nNSS也可以是CE，CS或SSEL;\nSCLK也可以是SCK;\n数据传输： 数据的采集时机可能是时钟信号的上升沿（从低到高）或下降沿（从高到低）。\n具体要看对SPI的配置；\n整体的传输大概可以分为以下几个过程：\n主机先将NSS信号拉低，这样保证开始接收数据；\n当接收端检测到时钟的边沿信号时，它将立即读取数据线上的信号，这样就得到了一位数据（1bit）; 由于时钟是随数据一起发送的，因此指定数据的传输速度并不重要，尽管设备将具有可以运行的最高速度（稍后我们将讨论选择合适的时钟边沿和速度）。\n主机发送到从机时：主机产生相应的时钟信号，然后数据一位一位地将从MOSI信号线上进行发送到从机；\n主机接收从机数据：如果从机需要将数据发送回主机，则主机将继续生成预定数量的时钟信号，并且从机会将数据通过MISO信号线发送；\n具体如下图所示：\n注意，SPI是“全双工”（具有单独的发送和接收线路），因此可以在同一时间发送和接收数据，另外SPI的接收硬件可以是一个简单的移位寄存器。这比异步串行通信所需的完整UART要简单得多，并且更加便宜；\n一些配置： 时钟频率 SPI总线上的主机必须在通信开始时候配置并生成相应的时钟信号。在每个SPI时钟周期内，都会发生全双工数据传输。 主机在MOSI线上发送一位数据，从机读取它，而从机在MISO线上发送一位数据，主机读取它。 就算只进行单向的数据传输，也要保持这样的顺序。这就意味着无论接收任何数据，必须实际发送一些东西！在这种情况下，我们称其为虚拟数据； 从理论上讲，只要实际可行，时钟速率就可以是您想要的任何速率，当然这个速率受限于每个系统能提供多大的系统时钟频率，以及最大的SPI传输速率。\n时钟极性 CKP/Clock Polarity 除了配置串行时钟速率（频率）外，SPI主设备还需要配置时钟极性。 根据硬件制造商的命名规则不同，时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式，比如信号上升沿读取数据还是信号下降沿读取数据； CKP可以配置为1或0。这意味着您可以根据需要将时钟的默认状态（IDLE）设置为高或低。极性反转可以通过简单的逻辑逆变器实现。您必须参考设备的数据手册才能正确设置CKP和CKE。 CKP = 0：时钟空闲IDLE为低电平 0； CKP = 1：时钟空闲IDLE为高电平1；\n时钟相位 CKE /Clock Phase (Edge) 除配置串行时钟速率和极性外，SPI主设备还应配置时钟相位（或边沿）。根据硬件制造商的不同，时钟相位通常写为CKE或CPHA； 顾名思义，时钟相位/边沿，也就是采集数据时是在时钟信号的具体相位或者边沿； CKE = 0：在时钟信号SCK的第一个跳变沿采样； CKE = 1：在时钟信号SCK的第二个跳变沿采样；\n时钟配置总结 综上几种情况，下图总结了所有时钟配置组合，并突出显示了实际采样数据的时刻； 其中黑色线为采样数据的时刻；蓝色线为SCK时钟信号；\n具体如下图所示；\n多从机模式： 前面说到SPI总线必须有一个主机，可以有多个从机，那么具体连接到SPI总线的方法有以下两种：\n第一种方法：多NSS 通常，每个从机都需要一条单独的SS线。\n如果要和特定的从机进行通讯，可以将相应的NSS信号线拉低，并保持其他NSS信号线的状态为高电平；如果同时将两个NSS信号线拉低，则可能会出现乱码，因为从机可能都试图在同一条MISO线上传输数据，最终导致接收数据乱码。\n具体连接方式如下图所示；\n第二种方法：菊花链 在数字通信世界中，在设备信号（总线信号或中断信号）以串行的方式从一 个设备依次传到下一个设备，不断循环直到数据到达目标设备的方式被称为菊花链。\n菊花链的最大缺点是因为是信号串行传输，所以一旦数据链路中的某设备发生故障的时候，它下面优先级较低的设备就不可能得到服务了；\n另一方面，距离主机越远的从机，获得服务的优先级越低，所以需要安排好从机的优先级，并且设置总线检测器，如果某个从机超时，则对该从机进行短路，防止单个从机损坏造成整个链路崩溃的情况；\n具体的连接如下图所示；\n其中红线加粗为数据的流向；\n所以最终的数据流向图可以表示为：\nSCK为时钟信号，8clks表示8个边沿信号；其中D为数据，X为无效数据；\n所以不难发现，菊花链模式充分使用了SPI其移位寄存器的功能，整个链充当通信移位寄存器，每个从机在下一个时钟周期将输入数据复制到输出。\n优缺点： 优势： 使SPI作为串行通信接口脱颖而出的原因很多；\n全双工串行通信；\n高速数据传输速率。\n简单的软件配置；\n极其灵活的数据传输，不限于8位，它可以是任意大小的字；\n非常简单的硬件结构。从站不需要唯一地址（与I2C不同）。从机使用主机时钟，不需要精密时钟振荡器/晶振（与UART不同）。不需要收发器（与CAN不同）。\n缺点： 没有硬件从机应答信号（主机可能在不知情的情况下无处发送）；\n通常仅支持一个主设备；\n需要更多的引脚（与I2C不同）；\n没有定义硬件级别的错误检查协议；\n与RS-232和CAN总线相比，只能支持非常短的距离；\nPS:IIC是MSB first ,UART是 LSB first，SPI是可配置的。 大小端问题描述的是字节之间的关系，而MSB、LSB描述的是bit位之间的关系。\n","permalink":"https://fan-pengfei.top/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/","summary":"\u003cblockquote\u003e\n\u003cp\u003e常用通讯协议(SPI、IIC、UART)；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一usart和uart\"\u003e一、USART和UART:\u003c/h2\u003e\n\u003cp\u003eUSART:通用同步异步收发器，USART是一个串行通信设备，可以灵活地与外部设备进行全双工数据交换。\u003c/p\u003e\n\u003cp\u003eUART: 通用异步收发器，异步串行通信口(UART)就是我们在嵌入式中常说的串口，它还是一种通用的数据通信议。\u003c/p\u003e\n\u003cp\u003e异步通讯时二者无区别，同步通讯时USART可以提供主动时钟。\u003c/p\u003e\n\u003cp\u003e均为全双工通信。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e起始位：先发出一个逻辑”0”的信号，表示传输数据的开始。\n数据位：传输N bits。\n校验位(可选)：数据位加上这一位后，使得“1”的位数应为偶数(偶校验)或奇数(奇校验)，以此来校验数据传送的正确性。\n如传输“A”(01000001)为例,”A”字符的8个bit位中有两个1。当为奇数校验时该位为1；当为偶数校验时该位为0。\n停止位：它是一帧数据的结束标志。可以是1bit、1.5bit、2bit的空闲电平。\n空闲位：没有数据传输时线路上的电平状态。为逻辑1。\n传输方向：即数据是从高位(MSB)开始传输还是从低位(LSB)开始传输。比如传输“A”如果是MSB那么就是01000001，如果是LSB那么就是10000010\n帧间隔：即传送数据的帧与帧之间的间隔大小，可以以位为计量也可以用时间(知道波特率那么位数和时间可以换算)。比如传送”A”完后，这为一帧数据，再传”B”，那么A与B之间的间隔即为帧间隔。\n波特率定义：有效数据信号调制载波的速率，每秒传输1或0的个数；\n例如：串口传输速率为9600bps，每秒可传输多少字节？\n起始位：1    数据位：8\n停止位：1    校验位：0\n传输1字节数据，需要传输10bit，因此：\n9600 ÷ 10 = \u003cstrong\u003e960Byte\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"二iic通讯协议\"\u003e二、IIC通讯协议：\u003c/h2\u003e\n\u003cp\u003eIIC协议为半双工协议。\u003c/p\u003e\n\u003cp\u003e全双工指在发送数据的同时也能够接收数据；\u003c/p\u003e\n\u003cp\u003e半双工就是指一个时间段内只有一个动作发生；\u003c/p\u003e\n\u003cp\u003e数据有效传输在scl信号的高电平期间，sda数据线保持稳定，在scl为低电平时允许sda数据线变化。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e起始条件在scl为高电平期间，sda出现下降沿，则为起始信号。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e结束条件在scl为高电平期间，sda出现上升沿，则为结束信号。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/img-3.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注意：注意起始和终止信号都是由主机发出的，总线在起始条件之后，视为忙状态，在停止条件之后被视为空闲状态。\n应答（ACK，Acknowledgement）。即确认字符，在数据通信中，接收站发给发送站的一种传输类控制字符。主机每向从机发送完一个字节的数据，主机总是需要等待从机给出一个应答信号，来确认从机是否成功接收到了数据，从机应答主机所需要的时钟也是由主机提供的，应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期，低电平0表示应答，1表示非应答。，需要应答时，数据发出方将SDA总线设置为3态输入，由于IIC总线上有上拉电阻，因此此时总线默认高电平，若数据接收方正确接收到数据，则数据接收方将SDA总线拉低，以示正确应答。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIIC传输时时从MSB开始传输到LSB结束。MSB是Most Significant Bit的缩写，最高有效位。在二进制数中，MSB是最高加权位。与十进制数字中最左边的一位类似。通常，MSB位于二进制数的最左侧，LSB位于二进制数的最右侧。LSB，英文 least significant bit，中文义最低有效位。\u003c/p\u003e\n\u003cp\u003e写时序：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/img-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e​    ID_Address, REG_Address, W_REG_Data\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e产生start位；\n传送器件地址ID_Address，器件地址的最后一位为数据的传输方向位，R/W，低电平0表示主机往从机写数据（W），1表示主机从从机读数据（R）。ACK应答，应答是从机发送给主机的应答，这里不用管；\n传送写入器件寄存器地址，即数据要写入的位置。同样ACK应答不用管；\n传送要写入的数据。ACK应答不用管；\n产生stop信号；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e读时序：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/img-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e​    {ID_Address + REG_Address} + {ID_Address + R_REG_Data}\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e产生start信号\n传送器件地址（写ID_Address），ACK。\n传送字地址（写REG_Address），ACK。\n再次产生start信号\n再传送一次器件地址，ACK。\n读取一个字节的数据，读数据最后结束前无应答ACK信号。\n产生stop信号。\u003c/p\u003e","title":"常用的通信协议"},{"content":" 测试自己的电脑是小端模式还是大端模式；\n方法一：地址转换 将int 48存起来，然后取得其地址，再将这个地址转为char* ，这时候，如果是小端存储，那么char*指针就指向48；\n48对应的ASCII码为字符0；\nvoid judge_bigend_littleend1() { int i = 48; int* p = \u0026amp;i; char c = 0; c = *((char*)p); if (c == \u0026#39;0\u0026#39;) printf(\u0026#34;小端\\n\u0026#34;); else printf(\u0026#34;大端\\n\u0026#34;); } 方法二：同方法一 定义变量int i=1;将 i 的地址拿到，强转成char*型，这时候就取到了 i 的低地址，这时候如果是1就是小端存储，如果是0就是大端存储。\nvoid judge_bigend_littleend2() { int i = 1; char c = (*(char*)\u0026amp;i); if (c) printf(\u0026#34;小端\\n\u0026#34;); else printf(\u0026#34;大端\\n\u0026#34;); } 方法三：利用联合体对齐规则 定义联合体，一个成员是多字节，一个是单字节，给多字节的成员赋一个最低一个字节不为0，其他字节为0 的值，再用第二个成员来判断，如果第二个字节不为0，就是小端，若为0，就是大端。\nvoid judge_bigend_littleend3()//因为联合体小的总是在低位 { union { int i; char c; }un; un.i = 1; if (un.c == 1) printf(\u0026#34;小端\\n\u0026#34;); else printf(\u0026#34;大端\\n\u0026#34;); } ","permalink":"https://fan-pengfei.top/posts/%E5%88%A4%E6%96%AD%E5%A4%A7%E7%AB%AF%E5%92%8C%E5%B0%8F%E7%AB%AF%E6%A8%A1%E5%BC%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003e测试自己的电脑是小端模式还是大端模式；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"方法一地址转换\"\u003e方法一：地址转换\u003c/h2\u003e\n\u003cp\u003e将int 48存起来，然后取得其地址，再将这个地址转为char* ，这时候，如果是小端存储，那么char*指针就指向48；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e48对应的ASCII码为字符\u003ccode\u003e0\u003c/code\u003e；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejudge_bigend_littleend1\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e48\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e p \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ei;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    c \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e((\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)p);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (c \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;0\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;小端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;大端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"方法二同方法一\"\u003e方法二：同方法一\u003c/h2\u003e\n\u003cp\u003e定义变量int i=1;将 i 的地址拿到，强转成char*型，这时候就取到了 i 的低地址，这时候如果是1就是小端存储，如果是0就是大端存储。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejudge_bigend_littleend2\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ei);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (c)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;小端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;大端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"方法三利用联合体对齐规则\"\u003e方法三：利用联合体对齐规则\u003c/h2\u003e\n\u003cp\u003e定义联合体，一个成员是多字节，一个是单字节，给多字节的成员赋一个最低一个字节不为0，其他字节为0 的值，再用第二个成员来判断，如果第二个字节不为0，就是小端，若为0，就是大端。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejudge_bigend_littleend3\u003c/span\u003e()\u003cspan style=\"color:#75715e\"\u003e//因为联合体小的总是在低位\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eunion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e c;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }un;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    un.i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (un.c \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;小端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;大端\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"判断大端和小端模式"},{"content":" CAN通讯解析；\n控制器局域网 (Controller Area Network，简称CAN或者CAN bus) 是一种功能丰富的车用总线标准。被设计用于在不需要主机（Host）的情况下，允许网络上的单片机和仪器相互通信。 它基于[消息传递协议，设计之初在车辆上采用复用通信线缆，以降低铜线使用量，后来也被其他行业所使用。\nCAN创建在基于信息导向传输协定的广播机制（Broadcast Communication Mechanism）上。其根据信息的内容，利用信息标志符（Message Identifier，每个标志符在整个网络中独一无二）来定义内容和消息的优先顺序进行传递，而并非指派特定站点地址（Station Address）的方式。\n因此，CAN拥有了良好的弹性调整能力，可以在现有网络中增加节点而不用在软、硬件上做出调整。除此之外，消息的传递不基于特殊种类的节点，增加了升级网络的便利性。\n架构： CAN是一个用于连接电子控制单元（ECU）的多主机串行总线标准。电子控制单元有时也被称作节点。CAN网络上需要至少两个节点才可进行通信。节点的复杂程度可以只是简单的输入输出设备，也可以是包含有CAN交互器并搭载了软件的嵌入式组件。节点还可能是一个网关，允许普通计算机通过USB或以太网端口与CAN网络上的设备通信。\n所有节点通过两根平行的总线连接在一起。两条电线组成一条双绞线，并且接有120Ω的特性阻抗。\nISO 11898-2，也称为高速度CAN。它在总线的两端均接有120Ω电阻。\n高速CAN网络 ISO 11898-2\n高速CAN总线在传输显性（0）信号时，会将CAN_H端抬向5V高电平，将CAN_L拉向0V低电平。当传输隐性（1)信号时，并不会驱动CAN_H或者CAN_L端。 显性信号CAN_H和CAN_L两端差分标称电压为2V。 终端电阻在没有驱动时，将差分标称电压降回0V。显性信号（0）的共模电压需要在1.5V到3.5V之间。隐性信号（1）的共模电压需要在+/-12V。\n高速CAN信令 ISO 11898-2\nISO 11898-3，也被称作低速或者容错CAN。它使用线性主线，星形主线或者连接到一个线性主线上的多星结构主线著称。每个节点都有终端电阻作为全局终端电阻的一部分。全局终端电阻不应低于100 Ω。\n低速容错CAN网络 ISO 11898-3\n低速/容错CAN信号在传输显性信号（0）时，驱动CANH端抬向5V，将CANL端降向0V。在传输隐性信号（1）时并不驱动CAN 总线的任何一端。在电源电压Vcc为5V时，显性信号差分电压需要大于2.3V，隐性信号的差分电压需要小于0.6V。CAN总线两端未被驱动时，终端电阻使CANL端回归到RTH电压（当电源电压Vcc为5V时，RTH电压至少为Vcc-0.3V=4.7V），同时使CANH端回归至RTL电压（RTL电压最大为0.3V）。两根线需要能够承受-27V至40V的电压而不被损坏。\n低速CAN信令 ISO 11898-3\n在高速和低速CAN中,从隐性信号向显性信号过渡的速度更快，因为此时CAN线缆被主动积极地驱动。显性向隐性的过渡速度主要取决于CAN网络的长度和导线的电容。\n高速CAN通常被用于汽车和工业应用，在这些应用环境中，总线通常从一端横跨至另一端。容错CAN总线则经常被用在需要连接在一起的一组节点。\nISO规格只要求总线共模电压必须保持在最小和最大范围内，但不定义如何将总线电压保持在这个范围。\nCAN总线必须使用终端电阻。终端电阻可以用来抑制信号反射，同时可以使总线电压回到隐性状态或者闲置状态。\n高速CAN在总线两端使用120Ω电阻。低速CAN在每个节点均使用电阻。也有其他类型的终端，例如ISO 11783中定义了终端偏压电路。\n终端偏压电路使用由4条导线组成的线缆，除了CAN信号线以外还有电源线和地线。这在每段总线两端提供自动偏压和终端功能。ISO11783网络是专为热拔插总线段和电子控制单元设计的。\nCAN通信节点： 每个节点需要:\n中央处理器、微处理器或主处理器 处理主机决定收到的信息的意思以及想要传输的信息。\n传感器、驱动器和控制设备可以与主处理器连接。\nCAN控制器；通常是集成单片机的一部分 接收：CAN控制器将从总线上接收的串位字节存储直到整个消息可用，之后主处理器可以获取这个消息（通常由于CAN控制器触发一个中断）。\n发送：主处理器发送传递信息到CAN控制器，之后当总线空闲时将串位信息传递至总线。\n收发器；由ISO11898-2/3介质访问单元（MAU）标准定义 接收：把数据流从CAN总线层转换成CAN控制器可以使用的标准。 CAN控制器通常配有保护电路。\n传输：把来自CAN控制器的数据流转换至CAN总线层。\n每个节点能够发送和接收信息，但不是同时进行的。 一个消息或帧主要包括标识符(ID)，它表示信息的优先级，最多八个数据字节。CRC、ACK和其他帧部分也是消息的一部分。改进了的CAN FD将每个帧拓展至最多64字节。 消息采用不归零(NRZ)格式串联传送到主线并可被所有节点接收。\n被CAN网络连接的设备通常是传感器，驱动器和其他控制设备。 这些设备通过一个中央处理器、一个CAN控制器和一个CAN接收器连接至总线。\n数据传输： CAN数据传输如果出现争执，将会使用无损位仲裁解决办法。该仲裁法要求CAN网络上的所有节点同步，对每一位的采样都在同一时间。这就是为什么有人称之为CAN同步。然而，同步这个术语在此并不精确，因为数据以异步格式传输而不包含时钟信号。\nCAN规范中使用术语”显性”位和”隐性”位来表示逻辑高低。显性是逻辑0(由发信器积极驱动通过电压)而隐性是逻辑1(被动地通过电阻返回到一个电压)。 闲置状态代表隐性的水平，也就是逻辑1。如果一个节点发送了显性位而另一个节点发送一个隐性位，那么总线上就有冲突，最终结果是显性位“获胜”。这意味着，更高优先级的信息没有延迟。较低优先级的节点信息自动在显性位传输结束，6个时钟位之后尝试重新传输。这使得CAN适合成为一个实时优先通讯系统。\n逻辑0或1的确切电压取决于所使用的物理层，但CAN的基本原则要求每个节点监听CAN网络上的数据，包括发信节点本身。如果所有节点都在同时发送逻辑1，所有节点都会看到这个逻辑1信号，包括发信节点个接受节点。如果所有发信节点同时发送逻辑0信号，那么所有节点都会看到这个逻辑0信号。当一个或多个发信节点发送逻辑0信号，但是有一个或多个发信节点发送了逻辑1信号，所有节点包括发送逻辑1信号的节点也会看到逻辑0信号。当一个节点发送逻辑1信号但是看到一个逻辑0信号，它会意识到线上有争执并退出发射。通过这个过程，任何传送逻辑1的节点在其他节点传送逻辑0时退出或者失去仲裁。失去仲裁的节点会在稍后把信息重新加入队列，CAN帧的比特流保持没有故障继续进行直到只剩下一个发信节点。这意味着传送第一个逻辑1的节点丧失仲裁。由于所有节点在开始CAN帧时传输11位(或CAN 2.0B中是29位)标识符，拥有最低标识符的发信节点在起始处拥有更多0。那个节点赢得仲裁并且拥有最高优先级。\n例如，一个11位标识符的CAN网络，有两个节点，他们的ID分别为15（二进制表示为00000001111）和16（二进制表示为00000010000）。如果这两个节点同时传输，每个都会优先传输它们标识符中的前6个0而不触发仲裁。\n起始位 ID位 ID位 ID位 ID位 ID位 ID位 ID位 ID位 ID位 ID位 ID位 剩下的部分\n10 9 8 7 6 5 4 3 2 1 0\n节点15 0 0 0 0 0 0 0 0 1 1 1 1\n节点16 0 0 0 0 0 0 0 1 停止传输\nCAN数据 0 0 0 0 0 0 0 0 1 1 1 1\n当ID中的第7位传输时，节点16为其ID发送1(隐性)，而节点15为其ID发送0（显性）。当这种情况发生时，该节点16知道自己发送了1，但在总线上看到了0，意识到有冲突发生并且自己失去仲裁。节点16停止传送而节点15继续传输自己的ID，没有丢失任何数据。拥有最低ID的节点总是赢得仲裁，因此具有最高优先级。\n长度小于40m的网络最高支持的比特率高达1百万比特/秒。降低比特率可以允许使用更长的网络距离（例如，125千比特/秒支持最大500米）。改进的CAN FD标准允许仲裁后升高比特率，可以将数据区块速度增加至仲裁位速率的八倍。\nID分配： 信息ID在单条CAN总线上必须是唯一的，否则两个节点将在仲裁位(ID)传送结束后继续传输，造成错误。\n1990年代早期，为信息选择标志符（ID）的准则仅仅基于数据的种类和发信节点。但是，当标志符同样代表着信息的优先级时，这会带来不好的实时响应。在这种情况下，通常要求CAN总线只能使用大概30%才能保证信息可以在截止时间之前到达。然而，如果信息的标志符根据信息的优先级决定，更低标志符的信息获得更高优先级，那么在不损失数据的前提下，总线的使用率可以达到70%到80%。\n位时序 CAN网络上的所有节点必须运行在相同的标称比特率下，但噪音、相移、振荡频率容差和振荡频率漂移导致实际的比特率可能与标称比特率不同。由于没有使用一个单独的时钟信号，需要一个同步节点方法。同步在仲裁机制中十分重要，因为仲裁中的节点需要能够同时看到它们传输数据的数据和其他节点的传输数据。 同步在确保节点间震荡时间不同时不发生错误上十分重要。\n总线闲置一段时间后，在第一个隐性信号向显性信号转换时（起始位） 进行硬同步。再次同步发生在传输帧期间的每次从隐性向显性转换时。CAN控制器期望在标称位时间内发生多次转换。如果并没有在期望的确定时间发生，控制器将根据这调整标称位时间。\n调整是通过将每一位划分成多个称为量子的时间段,并分配一定数量的量子到位中的四个阶段完成的。这四个阶段分别为：同步、传播、相位段1和相位段2。\n每位10个量子的CAN位时序的例子\n位被分成的量子数量会因控制器的不同而不同，每一个阶段分配的量子数会因比特率和网络状况的不同而改变。\n在预期时刻之前或之后发生的过渡会促使控制器计算时间差，并根据计算所得的时间差延长相位段1或者缩短相位段2。这有效地改变接收器到发信器的时序，将它们同步在一起。这个重新同步过程不断地在每次隐性向显性过渡时进行已确保发信器和接收器保持同步。不断地重新同步降低了噪声产生的错误，让同步至已经失去仲裁的节点的接收节点重新同步到赢得仲裁的节点。\n层级： CAN协议与很多网络协议相似，可以被分解为下列抽象层：\n应用层 对象层 信息过滤\n消息和状态处理\n传输层 大多数CAN标准应用在传输层。传输层从物理层接收消息并将这些信息传递给对象层。传输层负责特定时序、同步、信息位构架、仲裁、确认、错误检测及发信和故障约束。它的职责为：\n故障约束\n错误监测\n消息验证\n信息确认\n仲裁\n信息帧\n传输速率和时间\n路由信息\n物理层 帧： CAN网络可以配置为使用两种不同的消息（或“帧”）格式：标准或基本帧格式（在CAN 2.0 A和CAN 2.0 B中描述）和扩展帧格式（仅由CAN 2.0 B描述）。两种格式之间的唯一区别是，“CAN基本帧”支持标识符长度为11位，“CAN扩展帧”支持标识符长度为29位，由11位标识符（“基本标识符”）和一个18位扩展（“标识符扩展”）组成。CAN基本帧格式和CAN扩展帧格式之间的是通过使用IDE位进行区分的，该位在传输显性时为11位帧，而在传输隐性时使用29位帧。支持扩展帧格式消息的CAN控制器也能够发送和接收CAN基本帧格式信息。所有的帧都以开始位（SOF）作为信息传输的起始。\nCAN有4种帧类型：\n数据帧：包含用于传输的节点数据的帧\n远程帧：请求传输特定标识符的帧\n错误帧：由任何检测到错误的节点发送的帧\n过载帧：在数据帧或远程帧之间插入延迟的帧\n数据帧 数据帧是唯一用于实际数据传输的帧。它有两种信息结构：\n基本帧格式：有11个标识符位\n扩展帧格式：有29个标识符位\nCAN标准要求必须接受基本帧格式并可能接受扩展帧格式，但必须能承受扩展帧格式。\n基本帧格式： 带有电平信息的CAN基础帧格式（不包含填充位）\n帧格式如下：位值是用于描述CAN-LO信号的.\n字段名 字长 （位） 作用\n起始位（SOF） 1 表示帧的传输开始\n识别码（ID\\green） 11 唯一识别码，同样代表了优先级\n远程传输请求（RTR\\蓝色） 1 数据帧时一定是显性（0），远程请求帧时一定是隐性（1）\n标志码拓展位（IDE） 1 对于只有11位标志码的基本帧格式，此段一定为显性（0）\n预留位（R0） 1 预留位一定是显性（0），但是隐性（1）同样是可接受的\n数据长度代码（DLC\\黄色） 4 数据的字节数（0-8字节）\n数据段（Data field\\红色） 0–64 (0-8 字节) 待传输数据（长度由数据长度码DLC指定）\n循环冗余校验（CRC） 15 循环冗余校验\n循环冗余校验定界码 1 一定是隐性（1）\n确认槽（ACK） 1 发信器发送隐性（1）但是任何接收器可以宣示显性（0）\n确认定界码（ACK delimiter） 1 一定是隐性（1）\n结束位(EOF) 7 一定是隐性（1）\n拓展帧格式： 帧的格式如下表所示：\n字段名 字长 （位） 作用\n起始位（SOF） 1 表示帧的传输开始\n标志符A（ID A\\green） 11 唯一识别码的第一部分，同样代表了优先级\n替代远程请求（SRR） 1 数据帧时一定是显性（0），远程请求帧时一定是隐性（1）\n标志符拓展位（IDE） 1 对于有29位标志符的拓展帧格式，此段一定为隐性（1）\n标志符B（ID B\\green） 18 唯一识别码的第二部分，同样代表了优先级\n远程传输请求（RTR\\蓝色） 1 数据帧时一定是显性（0），远程请求帧时一定是隐性（1）\n预留位（r1，r0） 2 预留位一定是显性（0），但是隐性（1）同样是可接受的\n数据长度代码（DLC\\黄色） 4 数据的字节数（0-8字节）\n数据段（Data field\\红色） 0–64 (0-8 字节) 待传输数据（长度由数据长度码DLC指定）\n循环冗余校验（CRC） 15 循环冗余校验\n循环冗余校验定界符 1 一定是隐性（1）\n确认槽（ACK） 1 发送器发送隐性（1），任何接收器都可以发送显性（0）\n确认定界符（ACK delimiter） 1 一定是隐性（1）\n结束位(EOF) 7 一定是隐性（1）\n两个定位符区域A和B共同组成29位定位符。\n远程帧 通常数据传输是在数据源节点（例如传感器）发出数据帧的情况下自主执行的。但是，目标节点也可以通过发送远程帧来从信息源请求数据。\n数据帧和远程帧之间有两个区别。首先，RTR位在数据帧中作为显性位传输，其次在远程帧中没有数据段。DLC字段表示所请求的消息的数据长度，而不是发送的数据长度。\n也就是说：\n在数据帧和具有相同标识符的远程帧同时发送的情况下，由于数据帧标识符之后的RTR位是显性，它将赢得仲裁。\n错误帧 错误帧由两个不同的字段组成：\n第一段由不同站点提供的错误标志（6-12个显性位/隐性位）的叠加给出。\n接下来的第二段是错误帧定界符（ERROR DELIMITER，8个隐性位）。\n错误标志也有两种：\n主动错误标志 六个显性位 - 由网络上错误状态为“主动错误”的出错的节点传送。\n被动错误标志 六个隐性位 - 由网络上错误状态为“被动错误”的出错的节点传送。\nCAN有两种错误计数器：\n1.传输错误计数器（Transmit error counter，简称TEC） 2.接受错误计数器（Receive error counter，简称REC）\n当传输错误计数器TEC或接受错误计数器REC大于127且小于255时，将在总线上传输被动错误帧。\n当传输错误计数器TEC或接受错误计数器REC小于128时，将在总线上传输主动错误帧。\n当传输错误计数器TEC或接受错误计数器REC大于255时，节点进入主线离线状态，不会传输帧。\n过载帧 过载帧包含两个位字段：过载标志（Overload Flag）和过载定界符（Overload Delimiter）。有两种过载条件可导致过载标志的传输：\n接收器的内部条件，要求延迟下一个数据帧或远程帧。\n中断检测到一个显性位。\n由于情况1引起的过载帧只允许在预期中断的第一位时间开始，而由情况2引起的过载帧在检测到显性位后一位开始。过载标志由六个显性位组成，其整体形式与主动错误标志的形式相对应。过载标志的形式破坏了中断区的固定形式。因此，所有其他站点也会检测到过载情况，并在它们自己的部分开始传输过载标志。过载定界符由8个隐性位组成，与错误分隔符的形式相同。\n调试机制 CAN提供了五种调试机制，使其错误发生率低于4.7×10-11。当一个以上的上述错误发生时，发送中的传输将会失败中止并且产生错误数据包，发讯端则会试着重新发送消息数据包。各个节点将会重新争取优先权。\nCAN的五种侦测错误机制：\n循环冗余校验（CRC） CRC在消息结尾处加上一个FCS（frame check sequence）来确保消息的正确。接收消息端会将其FCS重新演算并与所接收到的FCS比对，如果不相符，表示有CRC错误。\nFrame check 检查数据包中几个固定值的字段以验证该数据包是否有被信号干扰导致内容错误。\nACK errors 接收端在收到数据包后会告知发讯端，发讯端若没有收到确认消息，ACK错误便发生。\nMonitoring 传输一位到网络上，再从网络读取来检查是否一致。\nBit stuffing 用于消息同步。\n确认槽（ACK） 确认插槽用于确认收到的CAN帧有效。接收到帧而没有发现错误的每个节点在ACK槽中发送显性水平，来覆盖发射机的隐性水平。如果发射机在ACK时隙中只检测到隐性电平，它就知道没有任何接收器获得有效的帧。接收节点可以发送隐性信号来指示它没有接收到有效帧，但是确实接收到有效帧的其它节点可以用显性信号覆盖它。发送节点无法知道CAN网络上的是否所有节点都收到了该消息。\n帧间内容 数据帧和远程帧通过称为帧间空间的区域与前面的帧分开。帧间空间由至少三个连续的隐性（1）位组成。之后，如果检测到一个显性位，它将被视为下一帧的“起始位”。 过载帧和错误帧不比帧间空间重要，并且多个过载帧也不由帧间空间分隔。帧间空间包含了字段中断和总线空闲，并且如果前一消息的发送器是被动错误站点，会将总线暂挂。\n位填充 [\nCAN帧在填充位之前和之后（紫色）\n传输器会在相同极性的五个连续位之后插入一个相反的极性的位，以确保足够的转换来保持同步。这种做法被称为位填充，并且对于CAN这样的不归零（NRZ）编码是必要的。填充的数据帧由接收器去掉填充。\n除了CRC定界符，ACK字段和结束位这样固定字长的区域之外，帧中其他所有字段都会被填充，这些字段是固定大小且未被填充。在使用位填充的字段中，具有相同极性的六个连续位（111111或000000）被视为错误。 当检测到错误时，节点可以发送主动错误标志。主动错误标志由六个连续的显性位组成，违反了位填充规则。\n位填充意味着数据帧可能比上述表中列举的预期的要长。CAN帧（基本格式下）的最大尺寸的情况是\n11111000011110000…\n被填充为：（填充位用粗体显示）\n111110000011111000001…\n填充位本身可能成为五个连续相同位中的第一个，所以在最坏的情况下，每四个原始位有一个填充位。\n长度由下面公式给出：\n8n+47+[\\frac{34+8n-1}{4}]\\tag{1} 其中8n+47是填充前帧的长度，在最坏情况下，原数据除了第一个4位后，在每个4位后增加一位（所以分子减去1），同时由于位的结构，固有的47位中只有34位能够被填充。\n","permalink":"https://fan-pengfei.top/posts/can%E9%80%9A%E8%AE%AF%E8%A7%A3%E6%9E%90/","summary":"\u003cblockquote\u003e\n\u003cp\u003eCAN通讯解析；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e控制器局域网\u003c/strong\u003e (\u003cstrong\u003eController Area Network\u003c/strong\u003e，简称\u003cstrong\u003eCAN\u003c/strong\u003e或者\u003cstrong\u003eCAN bus\u003c/strong\u003e) 是一种功能丰富的车用总线标准。被设计用于在不需要主机（Host）的情况下，允许网络上的单片机和仪器相互通信。 它基于[消息传递协议，设计之初在车辆上采用复用通信线缆，以降低铜线使用量，后来也被其他行业所使用。\u003c/p\u003e\n\u003cp\u003eCAN创建在基于信息导向传输协定的广播机制（Broadcast Communication Mechanism）上。其根据信息的内容，利用信息标志符（Message Identifier，每个标志符在整个网络中独一无二）来定义内容和消息的优先顺序进行传递，而并非指派特定站点地址（Station Address）的方式。\u003c/p\u003e\n\u003cp\u003e因此，CAN拥有了良好的弹性调整能力，可以在现有网络中增加节点而不用在软、硬件上做出调整。除此之外，消息的传递不基于特殊种类的节点，增加了升级网络的便利性。\u003c/p\u003e\n\u003ch2 id=\"架构\"\u003e架构：\u003c/h2\u003e\n\u003cp\u003eCAN是一个用于连接电子控制单元（ECU）的多主机串行总线标准。电子控制单元有时也被称作节点。CAN网络上需要至少两个节点才可进行通信。节点的复杂程度可以只是简单的输入输出设备，也可以是包含有CAN交互器并搭载了软件的嵌入式组件。节点还可能是一个网关，允许普通计算机通过USB或以太网端口与CAN网络上的设备通信。\u003c/p\u003e\n\u003cp\u003e所有节点通过两根平行的总线连接在一起。两条电线组成一条双绞线，并且接有120Ω的特性阻抗。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eISO 11898-2\u003c/strong\u003e，也称为高速度CAN。它在总线的两端均接有120Ω电阻。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/can%E9%80%9A%E8%AE%AF%E8%A7%A3%E6%9E%90/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e高速CAN网络 ISO 11898-2\u003c/p\u003e\n\u003cp\u003e高速CAN总线在传输显性（0）信号时，会将CAN_H端抬向5V高电平，将CAN_L拉向0V低电平。当传输隐性（1)信号时，并不会驱动CAN_H或者CAN_L端。 显性信号CAN_H和CAN_L两端差分标称电压为2V。 终端电阻在没有驱动时，将差分标称电压降回0V。显性信号（0）的共模电压需要在1.5V到3.5V之间。隐性信号（1）的共模电压需要在+/-12V。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/can%E9%80%9A%E8%AE%AF%E8%A7%A3%E6%9E%90/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e高速CAN信令 ISO 11898-2\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eISO 11898-3\u003c/strong\u003e，也被称作低速或者容错CAN。它使用线性主线，星形主线或者连接到一个线性主线上的多星结构主线著称。每个节点都有终端电阻作为全局终端电阻的一部分。全局终端电阻不应低于100 Ω。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/can%E9%80%9A%E8%AE%AF%E8%A7%A3%E6%9E%90/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e低速容错CAN网络 ISO 11898-3\u003c/p\u003e\n\u003cp\u003e低速/容错CAN信号在传输显性信号（0）时，驱动CANH端抬向5V，将CANL端降向0V。在传输隐性信号（1）时并不驱动CAN 总线的任何一端。在电源电压Vcc为5V时，显性信号差分电压需要大于2.3V，隐性信号的差分电压需要小于0.6V。CAN总线两端未被驱动时，终端电阻使CANL端回归到RTH电压（当电源电压Vcc为5V时，RTH电压至少为Vcc-0.3V=4.7V），同时使CANH端回归至RTL电压（RTL电压最大为0.3V）。两根线需要能够承受-27V至40V的电压而不被损坏。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/can%E9%80%9A%E8%AE%AF%E8%A7%A3%E6%9E%90/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e低速CAN信令 ISO 11898-3\u003c/p\u003e\n\u003cp\u003e在高速和低速CAN中,从隐性信号向显性信号过渡的速度更快，因为此时CAN线缆被主动积极地驱动。显性向隐性的过渡速度主要取决于CAN网络的长度和导线的电容。\u003c/p\u003e\n\u003cp\u003e高速CAN通常被用于汽车和工业应用，在这些应用环境中，总线通常从一端横跨至另一端。容错CAN总线则经常被用在需要连接在一起的一组节点。\u003c/p\u003e\n\u003cp\u003eISO规格只要求总线共模电压必须保持在最小和最大范围内，但不定义如何将总线电压保持在这个范围。\u003c/p\u003e\n\u003cp\u003eCAN总线必须使用终端电阻。终端电阻可以用来抑制信号反射，同时可以使总线电压回到隐性状态或者闲置状态。\u003c/p\u003e\n\u003cp\u003e高速CAN在总线两端使用120Ω电阻。低速CAN在每个节点均使用电阻。也有其他类型的终端，例如ISO 11783中定义了终端偏压电路。\u003c/p\u003e\n\u003cp\u003e终端偏压电路使用由4条导线组成的线缆，除了CAN信号线以外还有电源线和地线。这在每段总线两端提供自动偏压和终端功能。ISO11783网络是专为热拔插总线段和电子控制单元设计的。\u003c/p\u003e\n\u003ch2 id=\"can通信节点\"\u003eCAN通信节点：\u003c/h2\u003e\n\u003cp\u003e每个节点需要:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e中央处理器、微处理器或主处理器\n处理主机决定收到的信息的意思以及想要传输的信息。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e传感器、驱动器和控制设备可以与主处理器连接。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eCAN控制器；通常是集成单片机的一部分\n接收：CAN控制器将从总线上接收的串位字节存储直到整个消息可用，之后主处理器可以获取这个消息（通常由于CAN控制器触发一个中断）。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e发送：主处理器发送传递信息到CAN控制器，之后当总线空闲时将串位信息传递至总线。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e收发器；由ISO11898-2/3介质访问单元（MAU）标准定义\n接收：把数据流从CAN总线层转换成CAN控制器可以使用的标准。 CAN控制器通常配有保护电路。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e传输：把来自CAN控制器的数据流转换至CAN总线层。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e每个节点能够发送和接收信息，但不是同时进行的。 一个消息或帧主要包括标识符(ID)，它表示信息的优先级，最多八个数据字节。CRC、ACK和其他帧部分也是消息的一部分。改进了的CAN FD将每个帧拓展至最多64字节。 消息采用不归零(NRZ)格式串联传送到主线并可被所有节点接收。\u003c/p\u003e\n\u003cp\u003e被CAN网络连接的设备通常是传感器，驱动器和其他控制设备。 这些设备通过一个中央处理器、一个CAN控制器和一个CAN接收器连接至总线。\u003c/p\u003e\n\u003ch2 id=\"数据传输\"\u003e数据传输：\u003c/h2\u003e\n\u003cp\u003eCAN数据传输如果出现争执，将会使用无损位仲裁解决办法。该仲裁法要求CAN网络上的所有节点同步，对每一位的采样都在同一时间。这就是为什么有人称之为CAN同步。然而，同步这个术语在此并不精确，因为数据以异步格式传输而不包含时钟信号。\u003c/p\u003e","title":"CAN通讯解析"},{"content":" 了解stm32的启动过程，方便遇到问题时的调试；\n主要是两个问题： STM32是如何启动的，如何执行到main函数；\n如何保证编译后的代码可以烧录到正确的地址；\n作为一个计算机系统的核心，CPU的实际工作就是取指令和计算。大体上它可以看做是三个部分组成的：寄存器组、算术逻辑单元(ALU) 、指令队列。 在Cortex-M4的寄存器中有一个特殊的寄存器PC(Program Counter,程序计数器)， 用于控制程序的执行。在每个时钟周期中CPU都会根据PC中的值，从地址空间中取一条指令放到指令队列中， 同时从指令队列中取出一条指令进行解析和运算(实际上ARM采用的是一种流水线的指令处理方式，与这里所讲的内容还是有很大差异的，但大体思想差不多)。 并把上次的运算结果写到寄存器中。\nCPU运算所用的指令和数据都来自地址空间。在Cortex-M4的内存系统中， CPU可以访问4G的地址空间，根据所映射的物理对象不同大体上被划分成了6块。我们烧写到芯片内部的程序一般都在其中的Code段中， 在STM32中这个段对应的是一块FLASH。上电的时候，基本上只有这块FLASH中的内容是确定的，其它地址空间以及CPU内部的寄存器中的值都是随机的。 当然为了防止上电的时候外设产生意外，片上外设(Peripheral)段中的值在上电的时候也会有初始值。\n所以，从处理器的角度来看，启动过程实际上是给各个寄存器赋初值的过程，更具体的是给PC寄存器赋初值的过程。从MCU和系统的角度来看， 启动过程是初始化处理器和外设的过程。\n以STM32F4为例： STM32是如何启动的： 上电后系统进行复位，等到时钟稳定后才可以正常工作，这个过程通常需要几个毫秒。 图1中描述了处理器的复位过程，Cortex-M内核会先从地址0x0000处读取栈地址，并写到CPU内部的SP寄存器中。 再从地址0x0004读取Reset Vector到PC寄存器中，进而跳转到Reset Vector所指的地址上开始执行程序。\n图1 Cortex-M复位流程\n栈空间是处理器实现函数调用和中断服务的工具。函数调用和中断服务有一个共同的特点就是，它们都需要先把当前正在处理的内容暂时保存下来，转而执行要调用的函数， 或者中断服务函数，等待新的函数执行完毕返回后，在从原来保存的内容恢复回来继续执行原来的函数。而函数的调用是支持嵌套的，也就是说一个函数中可以调用子函数， 在子函数中又可以调用其它子函数。那么从函数的调用和返回的顺序上来看，最后调用的函数一定先返回。栈这种数据结构的特点就是其中的数据是后进先出的， 与函数调用和返回的顺序是一致的。因而，人们就专门在内存空间中划分出来一块用作栈空间，并从CPU中拿出一个宝贵的寄存器用于指示栈顶， 该寄存器被记为SP (Stack Pointer)。\n所以，前面所说的从CPU的角度看启动过程就是PC寄存器初始化的过程还不完善。虽然对PC寄存器进行初始化后，CPU就可以正常的取指令并进行运算了， 但这时所能完成的功能十分有限，并不能支持对我们很重要的函数和中断。因此，从CPU角度看启动过程是对PC和SP两个寄存器的初始化过程。\nCortex-M4中规定0x0000起始的地址存放的是系统向量表(vector table)。在STM32中0x0000本身并不对应什么物理设备， ==通过配置引脚BOOT[1:0]我们可以控制0x0000映射到地址空间中的其它地址中，也就实现了不同的启动方式。==一共有三种可选的启动方式如表1所示， 从主闪存或者系统存储器启动时，硬件上会把0x0000 0000映射到0x0800 0000或者0x1FFF F000上，这样我们从地址0x0000 0000访问的空间实际上就是主闪存或者系统存储器的空间。 从SRAM启动时，只能在0x2000 0000开始的地址访问SRAM。一般我都是从主闪存启动的，也就是说系统的向量表应当烧写在0x0800 0000的地址上。至于如何从SRAM启动需要查看其他资料todo。\n启动模式选择引脚\n启动模式 偏移地址\nBOOT1 BOOT0\nX 0 主闪存(Main Flash Memory) 0x0800 0000\n0 1 系统存储器(system memory) 0x1FFF F000\n1 1 内置SRAM(Embedded SRAM) 0x2000 0000\n表1 STM32的启动模式\n确定代码的烧写地址： 在STM32中，一般都会有一个片上的Flash和SRAM。Flash用于烧录我们编译后生成的目标代码，SRAM则用于栈空间和保存全局变量， 它们分别对应图1中地址空间的Code段和SRAM段。此外STM32中的Flash一般都映射在0x0800 0000的地址上的， 因此为了保证向量表写在0x0800 0000的位置上，我们必须保证生成的目标代码中一开始就是向量表的内容。\n我们的源文件不止一个，向量表怎么就写在了0x0800 0000的地址上呢？我们打开“Options for Target ‘Project Name’”窗口， 如图3所示，在Linker选项卡下，可以看到链接器的配置。其中”R/O Base”一栏中标注了只读代码的起始位置0x0800 0000，”R/W Base”一栏标注了可读写的地址空间。 我们只需要保证向量表链接在只读代码段的起始位置就可以了。这一点由链接器的控制参数(Linker control string)和分布文件(Scatter File)实现。 这两项都是由μVision自动生成的，刚刚提到的只读代码段地址和可读写的地址实际上只是为生成分布文件提供了一些数据而已。 我们也可以根据自己的需要重新编辑分布文件，比如实现应用程序自编程时，我们就会对其做些简单的修改。\nLinker control string描述了链接器的工作参数，我们的项目的具体配置信息如下。\n--cpu Cortex-M4.fp *.o --strict --scatter \u0026#34;.\\Objects\\Particle.sct\u0026#34; --summary_stderr --info summarysizes --map --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list \u0026#34;.\\Listings\\Particle.map\u0026#34; -o .\\Objects\\Particle.axf 这里把关于Scater File的选项是--strict --scatter \u0026quot;.\\Objects\\Particle.sct\u0026quot;，按照其描述的路径就可以找到Particle所用的分布文件。 Scater File则是用来告知编译器各段代码在最后生成的可执行文件中的位置。下面是由μVision生成的分布文件内容：\nLR_IROM1 0x08000000 0x00100000 { ; load region size_region ER_IROM1 0x08000000 0x00100000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } } LR_IROM1描述了可以烧录代码的起始地址(0x0800 000)和大小(0x0010 0000)，最后生成的代码分为ER_IROM1和RW_IRAM1两个部分。 这里的起始地址正好就是向量表所要在的位置。\n参考数据手册(Data Sheet)的第四章内存映射内容我们知道从0x0800 0000开始长度为0x0010 0000的这段地址空间是一段片上的FLASH， 系统掉电后它仍保有原来的数据，一般用来保存系统的代码。 所以这里的ER_IROM1就是用来告知链接器把生成的各个*.o工程文件以及只读的代码放置在从0x0800 0000起始的地址空间中。 *.o (RESET, +First)语句告知链接器把RESET代码段放置到起始位置。后面分析启动文件时就会发现RESET代码段一开始就定义了系统的向量表。\n任何可读可写的代码段都被放置到了RW_IRAM1中，RW_IRAM1起始于0x2000 0000对应于一段片上的RAM区域， 这段地址空间中的数据掉电后是不能保留的，通常用作数据段保存系统工作过程中的数据， 一般被抽象为栈空间和堆空间(关于栈和堆以后会有专题介绍)。\n启动文件(.s)分析： 启动文件中定义了系统的向量表、栈空间和堆空间。系统复位后首先就执行该文件中的代码。\n在启动文件中，首先定义了400字节的栈空间和200字节的堆空间。 由于这两段空间是可读可写的(READWRITE)，根据链接器和分布文件的配置，这两段代码将被放置到RW_IRAM1声明的地址空间下，即0x20000000。\n接着启动文件定义了系统的向量表。 这段代码被定义为只读的(READONLY)，并且被标记为RESET，因此将被映射到ER_IROM1声明的地址空间下的起始位置，而__Vectors标识了向量表的起始位置。 其中的第一个数据就是系统的栈顶向量__initial_sp， 紧接着就是复位处理程序向量Reset_Handler，这正是系统复位时首先读取的两个数据。\n复位处理程序很简单只有几行，它首先执行函数SystemInit，然后执行函数__main。 这两个函数就是那两个空函数，SystemInit和main（main函数在编译过程中会被加两个下划线）。\nmain is your main procedure form main.c file, once main is an internal procedure created by Keil toolchain which is calling at the end your main, but before it is initializing all variables (copying variables from FLASH to proper positions in RAM). In gcc it is seen explicitly, in Keil you can see it within debug process. main 是由 Keil 工具链创建的内部过程，它初始化所有变量（将变量从 FLASH 复制到 RAM 中的适当位置），并在最后调用您的 main，在 gcc 中它是明确可见的，在 Keil 中你可以在调试过程中看到它； 也就是说，__main 是库自带的东西，在编译时会由编译器链接到二进制程序里；\n此时我们可以看到，系统在跳转到Reset_Handler所指的代码后，先执行了函数SystemInit。 在官方的库中，这个函数做了一些系统时钟初始化的工作。 然后就开始执行我们的main函数，实际上我们完全可以把SystemInit这个函数删掉，把初始化工作移到main函数中完成。\n一些概念： Code （代码段） ZI （Zero-Inintialize Data段） RO （ReadOnly Data段） RW (ReacWrite Data段) 占用计算： FLASH 储存：Code + RO + RW RAM 内存： RW + ZI\n总结： STM32是如何启动的，为什么能够执行到main函数: 上电后，系统先后从向量表中读取栈顶地址和复位处理程序地址，并跳转到复位处理程序；\n在复位处理程序中，首先执行一些系统初始化的工作，然后执行main函数。\n如何保证编译后的代码能够烧写到芯片的正确地址中： 在项目链接选项中指定只读代码段的起始地址，并标记向量表的符号__Vectors作为代码的起始符号；\n在启动文件把向量表定义为只读的代码段并用__Vectors标识。\n参考：\nhttps://gaoyichao.com/Xiaotu/?book=stm32\u0026amp;title=STM32%E7%9A%84%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B\nhttps://zhuanlan.zhihu.com/p/337203514\nhttps://blog.51cto.com/u_14114084/3651520\n","permalink":"https://fan-pengfei.top/posts/stm32%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/","summary":"\u003cblockquote\u003e\n\u003cp\u003e了解stm32的启动过程，方便遇到问题时的调试；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"主要是两个问题\"\u003e主要是两个问题：\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eSTM32是如何启动的，如何执行到main函数；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如何保证编译后的代码可以烧录到正确的地址；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e作为一个计算机系统的核心，CPU的实际工作就是取指令和计算。大体上它可以看做是三个部分组成的：寄存器组、算术逻辑单元(ALU) 、指令队列。 在\u003ccode\u003eCortex-M4\u003c/code\u003e的寄存器中有一个特殊的寄存器PC(Program Counter,程序计数器)， 用于控制程序的执行。在每个时钟周期中CPU都会根据PC中的值，从地址空间中取一条指令放到指令队列中， 同时从指令队列中取出一条指令进行解析和运算(实际上ARM采用的是一种流水线的指令处理方式，与这里所讲的内容还是有很大差异的，但大体思想差不多)。 并把上次的运算结果写到寄存器中。\u003c/p\u003e\n\u003cp\u003eCPU运算所用的指令和数据都来自地址空间。在\u003ccode\u003eCortex-M4\u003c/code\u003e的内存系统中， CPU可以访问4G的地址空间，根据所映射的物理对象不同大体上被划分成了6块。我们烧写到芯片内部的程序一般都在其中的Code段中， 在STM32中这个段对应的是一块FLASH。上电的时候，基本上只有这块FLASH中的内容是确定的，其它地址空间以及CPU内部的寄存器中的值都是随机的。 当然为了防止上电的时候外设产生意外，片上外设(\u003ccode\u003ePeripheral\u003c/code\u003e)段中的值在上电的时候也会有初始值。\u003c/p\u003e\n\u003cp\u003e所以，从处理器的角度来看，启动过程实际上是给各个寄存器赋初值的过程，更具体的是给PC寄存器赋初值的过程。从MCU和系统的角度来看， 启动过程是初始化处理器和外设的过程。\u003c/p\u003e\n\u003ch2 id=\"以stm32f4为例\"\u003e以STM32F4为例：\u003c/h2\u003e\n\u003ch3 id=\"stm32是如何启动的\"\u003eSTM32是如何启动的：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e上电后系统进行复位，等到时钟稳定后才可以正常工作，这个过程通常需要几个毫秒。 图1中描述了处理器的复位过程，Cortex-M内核会先从地址\u003ccode\u003e0x0000\u003c/code\u003e处读取栈地址，并写到CPU内部的SP寄存器中。 再从地址\u003ccode\u003e0x0004\u003c/code\u003e读取\u003ccode\u003eReset Vector\u003c/code\u003e到PC寄存器中，进而跳转到\u003ccode\u003eReset Vector\u003c/code\u003e所指的地址上开始执行程序。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"cortex_m复位流程\" loading=\"lazy\" src=\"/posts/stm32%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e图1 Cortex-M复位流程\u003c/p\u003e\n\u003cp\u003e栈空间是处理器实现函数调用和中断服务的工具。函数调用和中断服务有一个共同的特点就是，它们都需要先把当前正在处理的内容暂时保存下来，转而执行要调用的函数， 或者中断服务函数，等待新的函数执行完毕返回后，在从原来保存的内容恢复回来继续执行原来的函数。而函数的调用是支持嵌套的，也就是说一个函数中可以调用子函数， 在子函数中又可以调用其它子函数。那么从函数的调用和返回的顺序上来看，最后调用的函数一定先返回。栈这种数据结构的特点就是其中的数据是后进先出的， 与函数调用和返回的顺序是一致的。因而，人们就专门在内存空间中划分出来一块用作栈空间，并从CPU中拿出一个宝贵的寄存器用于指示栈顶， 该寄存器被记为SP (Stack Pointer)。\u003c/p\u003e\n\u003cp\u003e所以，前面所说的从CPU的角度看启动过程就是PC寄存器初始化的过程还不完善。虽然对PC寄存器进行初始化后，CPU就可以正常的取指令并进行运算了， 但这时所能完成的功能十分有限，并不能支持对我们很重要的函数和中断。因此，从CPU角度看启动过程是对PC和SP两个寄存器的初始化过程。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eCortex-M4\u003c/code\u003e中规定\u003ccode\u003e0x0000\u003c/code\u003e起始的地址存放的是系统向量表(\u003ccode\u003evector table\u003c/code\u003e)。在STM32中\u003ccode\u003e0x0000\u003c/code\u003e本身并不对应什么物理设备， ==通过配置引脚BOOT[1:0]我们可以控制\u003ccode\u003e0x0000\u003c/code\u003e映射到地址空间中的其它地址中，也就实现了不同的启动方式。==一共有三种可选的启动方式如表1所示， 从主闪存或者系统存储器启动时，硬件上会把\u003ccode\u003e0x0000 0000\u003c/code\u003e映射到\u003ccode\u003e0x0800 0000\u003c/code\u003e或者\u003ccode\u003e0x1FFF F000\u003c/code\u003e上，这样我们从地址\u003ccode\u003e0x0000 0000\u003c/code\u003e访问的空间实际上就是主闪存或者系统存储器的空间。 从SRAM启动时，只能在\u003ccode\u003e0x2000 0000\u003c/code\u003e开始的地址访问SRAM。一般我都是从主闪存启动的，也就是说系统的向量表应当烧写在\u003ccode\u003e0x0800 0000\u003c/code\u003e的地址上。至于如何从SRAM启动需要查看其他资料\u003ccode\u003etodo\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e启动模式选择引脚\u003c/p\u003e\n\u003cp\u003e启动模式\n偏移地址\u003c/p\u003e\n\u003cp\u003eBOOT1\nBOOT0\u003c/p\u003e\n\u003cp\u003eX\n0\n主闪存(Main Flash Memory)\n0x0800 0000\u003c/p\u003e\n\u003cp\u003e0\n1\n系统存储器(system memory)\n0x1FFF F000\u003c/p\u003e\n\u003cp\u003e1\n1\n内置SRAM(Embedded SRAM)\n0x2000 0000\u003c/p\u003e","title":"stm32启动过程"},{"content":" python向bin文件添加CRC32校验码；\n1、keil添加配置，编译后调用脚本生成bin文件 D:\\Keil_v5\\ARM\\ARMCC\\bin\\fromelf.exe --bin -o fromelf --bin -o \u0026#34;$L@L.bin\u0026#34; \u0026#34;#L\u0026#34; 2、keil添加配置，调用python脚本，向bin文件中插入crc32校验码 python ./crc32_bin.py python脚本如下：\n# -*- coding:utf-8 -*- import binascii import os import sys def crc2hex(crc): res=\u0026#39;\u0026#39; for i in range(4): t=crc \u0026amp; 0xFF crc \u0026gt;\u0026gt;= 8 res=\u0026#39;%02X%s\u0026#39; % (t, res) return res inputfile = \u0026#34;E:\\IAP\\APP\\Project\\OBJ\\STM32F407.bin\u0026#34;#实际存放的bin文件路径 isfile = os.path.isfile(inputfile); print(inputfile); fp = open(inputfile, \u0026#34;r+b\u0026#34;) #直接打开一个文件，如果文件不存在则创建文件 filesize = os.path.getsize(inputfile) print(\u0026#34;ZI app firmware size:\u0026#34;, filesize, \u0026#34;bytes.\u0026#34;) #计算bin文件的CRC，首先清空CRC32区域的4个byte fp.seek(0x1c, 0)#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32 clear4bytes = \u0026#39;00000000\u0026#39; c4 =binascii.unhexlify(clear4bytes) fp.write(c4) #将CRC32存放的区域4bytes清零 fp.seek(0, 0)#从0开始读取整个bin file_content = fp.read()#读整个文件内容到 file_content crc = binascii.crc32(file_content) print(\u0026#39;CRC32:\u0026#39;, hex(crc)) fp.seek(0x1c, 0)#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32 #存放计算CRC32四个字节 crcstr_2 = crc2hex(crc) r=binascii.unhexlify(crcstr_2) fp.write(r) fp.close() sys.exit(0)##正常退出 这个脚本我是用在stm32 Bootloader中的，因为经串口或其他方式接收到的程序未必是正确的，而且有时候会加入版本信息什么的，所以加入CRC校验可以方便的检验文件完整性。\n","permalink":"https://fan-pengfei.top/posts/python%E5%90%91bin%E6%96%87%E4%BB%B6%E6%B7%BB%E5%8A%A0crc32%E6%A0%A1%E9%AA%8C%E7%A0%81/","summary":"\u003cblockquote\u003e\n\u003cp\u003epython向bin文件添加CRC32校验码；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1keil添加配置编译后调用脚本生成bin文件\"\u003e1、keil添加配置，编译后调用脚本生成bin文件\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eD:\u003cspan style=\"color:#ae81ff\"\u003e\\K\u003c/span\u003eeil_v5\u003cspan style=\"color:#ae81ff\"\u003e\\A\u003c/span\u003eRM\u003cspan style=\"color:#ae81ff\"\u003e\\A\u003c/span\u003eRMCC\u003cspan style=\"color:#ae81ff\"\u003e\\b\u003c/span\u003ein\u003cspan style=\"color:#ae81ff\"\u003e\\f\u003c/span\u003eromelf.exe --bin -o fromelf --bin -o \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$L\u003cspan style=\"color:#e6db74\"\u003e@L.bin\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;#L\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/python%E5%90%91bin%E6%96%87%E4%BB%B6%E6%B7%BB%E5%8A%A0crc32%E6%A0%A1%E9%AA%8C%E7%A0%81/img-1.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2keil添加配置调用python脚本向bin文件中插入crc32校验码\"\u003e2、keil添加配置，调用python脚本，向bin文件中插入crc32校验码\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython ./crc32_bin.py\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003epython脚本如下：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# -*- coding:utf-8 -*-\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e binascii\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e os\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecrc2hex\u003c/span\u003e(crc):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    res\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        t\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecrc \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0xFF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        crc \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        res\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%02X%s\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e (t, res)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e res\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einputfile \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;E:\\IAP\\APP\\Project\\OBJ\\STM32F407.bin\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#实际存放的bin文件路径\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eisfile \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisfile(inputfile);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(inputfile);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e open(inputfile, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r+b\u0026#34;\u003c/span\u003e)  \u003cspan style=\"color:#75715e\"\u003e#直接打开一个文件，如果文件不存在则创建文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efilesize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egetsize(inputfile)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ZI app firmware size:\u0026#34;\u003c/span\u003e, filesize, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bytes.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#计算bin文件的CRC，首先清空CRC32区域的4个byte\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0x1c\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eclear4bytes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;00000000\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ec4 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ebinascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunhexlify(clear4bytes)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(c4)  \u003cspan style=\"color:#75715e\"\u003e#将CRC32存放的区域4bytes清零\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从0开始读取整个bin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efile_content \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e fp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\u003cspan style=\"color:#75715e\"\u003e#读整个文件内容到 file_content\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecrc \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e binascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecrc32(file_content)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;CRC32:\u0026#39;\u003c/span\u003e, hex(crc))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0x1c\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#存放计算CRC32四个字节\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecrcstr_2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e crc2hex(crc)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003er\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ebinascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunhexlify(crcstr_2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(r)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclose()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esys\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexit(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e##正常退出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e这个脚本我是用在stm32 \u003ccode\u003eBootloader\u003c/code\u003e中的，因为经串口或其他方式接收到的程序未必是正确的，而且有时候会加入版本信息什么的，所以加入CRC校验可以方便的检验文件完整性。\u003c/strong\u003e\u003c/p\u003e","title":"python向bin文件添加CRC32校验码"},{"content":" FreeRTOS学习记录；\nFreeRTOS学习记录 一、任务管理 任务函数： void ATaskFunction( void *pvParameters ); 每一个任务函数都有自己的栈空间、自动变量；\n函数里通常是一个死循环；\n一般函数有可能跳出死循环，则必须删除函数：\nvoid ATaskFunction( void *pvParameters ) { /* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的iVarialbleExample变 量。但如果iVariableExample被定义为static，这一点则不成立 – 这种情况下只存在一个变量，所有的任务实 例将会共享这个变量。 */ int iVariableExample = 0; /* 任务通常实现在一个死循环中。 */ for( ;; ) { /* 完成任务功能的代码将放在这里。 */ } /* 如果任务的具体实现会跳出上面的死循环，则此任务必须在函数运行完之前删除。传入NULL参数表示删除 的是当前任务 */ vTaskDelete( NULL ); } 任务创建函数： portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask ); pvTaskCode 任务只是永不退出的 C 函数，实现常通常是一个死循环。参数 pvTaskCode 是一个指向任务的实现函数的指针(效果上仅仅是函数 名)。\npcName 具有描述性的任务名。这个参数不会被 FreeRTOS 使用。其只是单 纯地用于辅助调试。识别一个具有可读性的名字总是比通过句柄来 识别容易得多。 应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定 义任务名的最大长度——包括’\\0’结束符。如果传入的字符串长度超 过了这个最大值，字符串将会自动被截断。\nusStackDepth 当任务创建时，内核会分为每个任务分配属于任务自己的唯一状态。 usStackDepth 值用于告诉内核为它分配多大的栈空间。 这个值指定的是栈空间可以保存多少个字(word)，而不是多少个字 节(byte)。比如说，如果是 32 位宽的栈空间，传入的 usStackDepth 值为 100，则将会分配 400 字节的栈空间(100 * 4bytes)。栈深度乘 以栈宽度的结果千万不能超过一个 size_t 类型变量所能表达的最大 值。 应用程序通过定义常量 configMINIMAL_STACK_SIZE 来决定空闲 任务任用的栈空间大小。在 FreeRTOS 为微控制器架构提供的 Demo 应用程序中，赋予此常量的值是对所有任务的最小建议值。 如果你的任务会使用大量栈空间，那么你应当赋予一个更大的值。 没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计 算出来虽然是可能的，但大多数用户会先简单地赋予一个自认为合 理的值，然后利用 FreeRTOS 提供的特性来确证分配的空间既不欠 缺也不浪费。第六章包括了一些信息，可以知道如何去查询任务使 用了多少栈空间。\npvParameters 任务函数接受一个指向 void 的指针(void*)。 pvParameters 的值即 是传递到任务中的值。这篇文档中的一些范例程序将会示范这个参 数可以如何使用。\nuxPriority 指定任务执行的优先级。优先级的取值范围可以从最低优先级 0 到 最高优先级(configMAX_PRIORITIES – 1)。 configMAX_PRIORITIES 是一个由用户定义的常量。优先级号并没 有上限(除了受限于采用的数据类型和系统的有效内存空间)，但最 好使用实际需要的最小数值以避免内存浪费。如果 uxPriority 的值 超过了(configMAX_PRIORITIES – 1)，将会导致实际赋给任务的优 先级被自动封顶到最大合法值。\npxCreatedTask pxCreatedTask 用于传出任务的句柄。这个句柄将在 API 调用中对 该创建出来的任务进行引用，比如改变任务优先级，或者删除任务。 如果应用程序中不会用到这个任务的句柄，则 pxCreatedTask 可以 被设为 NULL。\n返回值 有两个可能的返回值： 1. pdTRUE 表明任务创建成功。 2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 由于内存堆空间不足， FreeRTOS 无法分配足够的空间来保存任务 结构数据和任务栈，因此无法创建任务。 第五章将提供更多有关内存管理方面的信息。\nint main( void ) { /* 创建第一个任务。 需要说明的是一个实用的应用程序中应当检测函数xTaskCreate()的返回值，以确保任 务创建成功。 */ xTaskCreate( vTask1, /* 指向任务函数的指针 */ \u0026#34;Task 1\u0026#34;, /* 任务的文本名字，只会在调试中用到 */ 1000, /* 栈深度 – 大多数小型微控制器会使用的值会比此值小得多 */ NULL, /* 没有任务参数 */ 1, /* 此任务运行在优先级1上. */ NULL ); /* 不会用到任务句柄 */ /* Create the other task in exactly the same way and at the same priority. */ xTaskCreate( vTask2, \u0026#34;Task 2\u0026#34;, 1000, NULL, 1, NULL ); /* 启动调度器，任务开始执行 */ vTaskStartScheduler(); /* 如果一切正常， main()函数不应该会执行到这里。但如果执行到这里，很可能是内存堆空间不足导致空闲 任务无法创建。第五章有讲述更多关于内存管理方面的信息 */ for( ;; ); } void vTask1( void *pvParameters ) { const char *pcTaskName = \u0026#34;Task 1 is running\\r\\n\u0026#34;; volatile unsigned long ul; /* 和大多数任务一样，该任务处于一个死循环中。 */ for( ;; ) { /* Print out the name of this task. */ vPrintString( pcTaskName ); /* 延迟，以产生一个周期 */ for( ul = 0; ul 低优先级号表示任务的优先级低，优先级号 0 表示最低优先级。有效的优先级号范围从 0 到(configMAX_PRIORITES – 1) . ​ 时间片的长度通过心跳中断的频率进行设定，心跳中断频率由FreeRTOSConfig.h 中的编译时配置常量 configTICK_RATE_HZ 进行配置。比如说，如果 configTICK_RATE_HZ 设为100(HZ)，则时间片长度为 10ms。 ​ 需要说明的是， FreeRTOS API 函数调用中指定的时间总是以心跳中断为单位（通常的提法为心跳”ticks”)。常量portTICK_RATE_MS 用于将以心跳为单位的时间值转化为以毫秒为单位的时间值。有效精度依赖于系统心跳频率。 \u0026gt; 调度器总是选择具有最高优先级的可运行任务来执行。 \u0026gt; 为了使我们的任务切实有用，我们需要通过某种方式来进行**事件驱动**。一个事件驱动任务只会在事件发生后触发工作(处理)，而在事件没有发生时是不能进入运行态的。调度器总是选择所有能够进入运行态的任务中具有最高优先级的任务。一个高优先级但不能够运行的任务意味着不会被调度器选中，而代之以另一个优先级虽然更低但能够运行的任务。因此，采用事件驱动任务的意义就在于任务可以被创建在许多不同的优先级上，并且最高优先级任务不会把所有的低优先级任务饿死。 - 运行状态 - 阻塞状态 一个任务在等待某一个事件； 定时（时间相关事件）事件：可以是延时或者绝对时间； - 同步事件：源于其他任务或者中断的事件；比如某个任务可以进入阻塞状态以等待队列中有数据到来；FreeRTOS的队列，二值信号量、计数信号量、互斥信号量和互斥量都可以用来实现同步事件。 - 任务可以在进入阻塞态以等待同步事件时指定一个等待超时时间，这样可以有效地实现阻塞状态下同时等待两种类型的事件； - 挂起状态 属于非运行状态。大多数应用程序都不会用到挂起状态； - 进入挂起状态： 调用 vTaskSuspend() API 函数； - 唤醒挂起状态： 调 用 vTaskResume() 或vTaskResumeFromISR() API 函数 ； - 就绪状态 如果任务处于非运行状态，但既没有阻塞也没有挂起，则这个任务处于就绪(ready，准备或就绪)状态。 - 处于就绪态的任务能够被运行，但只是”准备(ready)”运行，而当前尚未运行。 ![0b2f278635226a0096e2b8ac62a0da7](img-1.png) ##### 使用阻塞态实现延时： \u0026gt; 调用 vTaskDelay() API 函数来代替空循环， ```c void vTaskDelay( portTickType xTicksToDelay ); /*xTicksToDelay 延迟多少个心跳周期。调用该延迟函数的任务将进入阻塞态，经延迟指定的心跳周期数后，再转移到就绪态。 举个例子，当某个任务调用 vTaskDelay( 100 )时，心跳计数值 为 10,000，则该任务将保持在阻塞态，直到心跳计数计到 10,100。常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转换为以心跳周期为单位的时间值。*/ vTaskDelay( 250 / portTICK_RATE_MS ); //延时250ms; vTaskDelayUntil() API 函数 ： API 函数 vTaskDelayUntil()可以用于实现一个固定执行周期的需求(当你需要让你的任务以固定频率周期性执行的时候)。由于调用此函数的任务解除阻塞的时间是绝对时刻，比起相对于调用时刻的相对时间更精确(即比调用 vTaskDelay()可以实现更精确的周期性)。\nvoid vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement ); /*pxPreviousWakeTime 此参数命名时假定 vTaskDelayUntil()用于实现某个任务以固定频率周期性执行。这种情况下 pxPreviousWakeTime保存了任务上一次离开阻塞态(被唤醒)的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。 pxPreviousWakeTime指向的变量值会在API函数vTaskDelayUntil()调用过程中自动更新，应用程序除了该变量第一次初始化外，通常都不要修改它的值。程序清单14 展示了这个参数的使用方法。TimeIncrement 此参数命名时同样是假定 vTaskDelayUntil()用于实现某个任 务 以 固 定 频 率 周 期 性 执 行 —— 这 个 频 率 就 是 由xTimeIncrement 指定的。 xTimeIncrement 的 单 位 是 心 跳 周 期 ， 可 以 使 用 常 量 portTICK_RATE_MS 将毫秒转换为心跳周期*/ //举例 void vTaskFunction( void *pvParameters ) { char *pcTaskName; portTickType xLastWakeTime; /* The string to print out is passed in via the parameter. Cast this to a character pointer. */ pcTaskName = ( char * ) pvParameters; /* 变量xLastWakeTime需要被初始化为当前心跳计数值。说明一下，这是该变量唯一一次被显式赋值。之后， xLastWakeTime将在函数vTaskDelayUntil()中自动更新。 */ xLastWakeTime = xTaskGetTickCount(); /* As per most tasks, this task is implemented in an infinite loop. */ for( ;; ) { /* Print out the name of this task. */ vPrintString( pcTaskName ); /* 本任务将精确的以250毫秒为周期执行。同vTaskDelay()函数一样，时间值是以心跳周期为单位的， 可以使用常量portTICK_RATE_MS将毫秒转换为心跳周期。变量xLastWakeTime会在 vTaskDelayUntil()中自动更新，因此不需要应用程序进行显示更新。 */ vTaskDelayUntil( \u0026amp;xLastWakeTime, ( 250 / portTICK_RATE_MS ) ); } } 空闲任务和空线任务钩子函数： 但处理器总是需要代码来执行——所以至少要有一个任务处于运行态。为了保证这一点，当调用vTaskStartScheduler()时，调度器会自动创建一个空闲任务。空闲任务是一个非常短小的循环——和最早的示例任务十分相似，总是可以运行。 空闲任务拥有最低优先级(优先级 0)以保证其不会妨碍具有更高优先级的应用任务进入运行态——当然，没有任何限制说是不能把应用任务创建在与空闲任务相同的优先级上；如果需要的话，你一样可以和空闲任务一起共享优先级 ；\n​ 通过空闲任务钩子函数(或称回调， hook, or call-back)，可以直接在空闲任务中添加应用程序相关的功能。空闲任务钩子函数会被空闲任务每循环一次就自动调用一次。\n通常钩子函数被用于：\n执行低优先级，后台或需要不停处理的功能代码。\n测试处系统处理裕量(空闲任务只会在所有其它任务都不运行时才有机会执行，所以测量出空闲任务占用的处理时间就可以清楚的知道系统有多少富余的处理时间)。\n将处理器配置到低功耗模式——提供一种自动省电方法，使得在没有任何应用功能需要处理的时候，系统自动进入省电模式。\n钩子函数必须遵循的规则：\n绝不能阻塞或挂起。空闲任务只会在其它任务都不运行时才会被执行(除非有应用任务共享空闲任务优先级)。以任何方式阻塞空闲任务都可能导致没有任务能够进入运行态！\n如果应用程序用到了 vTaskDelete() API函数，则空闲钩子函数必须能够尽快返回。因为在任务被删除后，空闲任务负责回收内核资源。如果空闲任务一直运行在钩子函数中，则无法进行回收工作。\n钩子函数的函数原型：\nvoid vApplicationIdleHook( void ); //举例 /* Declare a variable that will be incremented by the hook function. */ unsigned long ulIdleCycleCount = 0UL; /* 空闲钩子函数必须命名为vApplicationIdleHook(),无参数也无返回值。 */ void vApplicationIdleHook( void ) { /* This hook function does nothing but increment a counter. */ ulIdleCycleCount++; } FreeRTOSConfig.h 中的配置常量 configUSE_IDLE_HOOK 必须定义为 1，这样空闲任务钩子函数才会被调用。\n修改任务优先级： //API 函数 vTaskPriofitySet()可以用于在调度器启动后改变任何任务的优先级。 void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ); /*pxTask 被修改优先级的任务句柄(即目标任务)——参考 xTaskCreate()API函数的参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。任务可以通过传入 NULL 值来修改自己的优先级。 uxNewPriority 目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优先级(configMAX_PRIORITIES – 1)，则会被自动封顶为最大值。常量configMAX_PRIORITIES 是在 FreeRTOSConfig.h 头文件中设置的一个编译时选项*/ //uxTaskPriorityGet() API 函数用于查询一个任务的优先级。 unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ); //pxTask 被查询任务的句柄(目标任务) ——参考 xTaskCreate() API 函数的参数pxCreatedTask 以了解如何得到任务句柄方面的信息。任务可以通过传入 NULL 值来查询自己的优先级。 //返回值 被查询任务的当前优先级。 任务删除函数： void vTaskDelete( xTaskHandle pxTaskToDelete ); 任务可以使用 API 函数 vTaskDelete()删除自己或其它任务。任务被删除后就不复存在，也不会再进入运行态。空闲任务的责任是要将分配给已删除任务的内存释放掉。因此有一点很重要，那就是使用 vTaskDelete() API 函数的任务千万不能把空闲任务的执行时间饿死。需要说明一点，只有内核为任务分配的内存空间才会在任务被删除后自动回收。任务自己占用的内存或资源需要由应用程序自己显式地释放 ;\npxTaskToDelete 被删除任务的句柄(目标任务) —— 参考 xTaskCreate() API 函数的 参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。 任务可以通过传入 NULL 值来删除自己.\n调度算法简述： 优先级抢占式调度（固定优先级抢占式调度）： 每个任务都赋予了一个优先级。\n每个任务都可以存在于一个或多个状态。\n在任何时候都只有一个任务可以处于运行状态。\n调度器总是在所有处于就绪态的任务中选择具有最高优先级的任务来执行。\n优先级无法被内核修改，只能被任务修改。\n任务优先级的选择： 为一种通用规则，完成硬实时功能的任务优先级会高于完成软件时功能任务的优先级。但其它一些因素，比如执行时间和处理器利用率，都必须纳入考虑范围，以保证应用程序不会超过硬实时的需求限制。\n单调速率调度(Rate Monotonic Scheduling, RMS)是一种常用的优先级分配技术。其根据任务周期性执行的速率来分配一个唯一的优先级。具有最高周期执行频率的任务赋予高最优先级；具有最低周期执行频率的任务赋予最低优先级。 这种优先级分配方式被证明了可以最大化整个应用程序的可调度性(schedulability)；\n但是运行时间不定以及并非所有任务都具有周期性，会使得对这种方式的全面计算变得相当复杂。\n协作式调度 采用一个纯粹的协作式调度器，只可能在运行态任务进入阻塞态或是运行态任务显式调用 taskYIELD()时，才会进行上下文切换。\n任务永远不会被抢占，而具有相同优先级的任务也不会自动共享处理器时间。\n协作式调度的这作工作方式虽然比较简单，但可能会导致系统响应不够快。\n混合调度方案 需要在中断服务例程中显式地进行上下文切换；从而允许同步事件产生抢占行为，但时间事件却不行。这样做的结果是得到了一个没有时间片机制的抢占式系统。或许这正是所期望的，因为获得了效率，并且这也是一种常用的调度器配置；\n二、队列管理 FreeRTOS 中所有的通信与同步机制都是基于队列实现的。\n队列的特性: 数据存储 队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。 通常情况下，队列被作为 FIFO(先进先出)使用，即数据由队列尾写入，从队列首读出。当然，由队列首写入也是可能的。 往队列写入数据是通过字节拷贝把数据复制存储到队列中； 从队列读出数据使得把队列中的数据拷贝删除。\n可以被多任务存取 队列是具有自己独立权限的内核对象，并不属于或赋予任何任务。 所有任务都可以向同一队列写入和读出。一个队列由多方写入是经常的事，但由多方读出倒是很少遇到。\n读队列时阻塞 当某个任务试图读一个队列时，其可以指定一个阻塞超时时间。在这段时间中，如果队列为空，该任务将保持阻塞状态以等待队列数据有效。 当其它任务或中断服务例程往其等待的队列中写入了数据，该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间，即使队列中尚无有效数据，任务也会自动从阻塞态转移为就绪态。 由于队列可以被多个任务读取，所以对单个队列而言，也可能有多个任务处于阻塞状态以等待队列数据有效。 这种情况下，一旦队列数据有效，只会有一个任务会被解除阻塞，这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同，那么被解除阻塞的任务将是等待最久的任务。\n写队列时阻塞 同读队列一样，任务也可以在写队列时指定一个阻塞超时时间。这个时间是当被写队列已满时，任务进入阻塞态以等待队列空间有效的最长时间 ; 由于队列可以被多个任务写入，所以对单个队列而言，也可能有多个任务处于阻塞状态以等待队列空间有效。这种情况下，一旦队列空间有效，只会有一个任务会被解除阻塞，这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同，那么被解除阻塞的任务将是等待最久的任务。\n队列的使用： 创建队列： 队列在使用前必须先被创建。队列由声明为 xQueueHandle 的变量进行引用xQueueCreate()用于创建一个队列，并返回一个 xQueueHandle 句柄以便于对其创建的队列进行引用。当创建队列时， FreeRTOS 从堆空间中分配内存空间。分配的空间用于存储队列数据结构本身以及队列中包含的数据单元。如果内存堆中没有足够的空间来创建队列，xQueueCreate()将返回 NULL ;\nxQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,unsigned portBASE_TYPE uxItemSize ); uxQueueLength 队列能够存储的最大单元数目，即队列深度。\nuxItemSize 队列中数据单元的长度，以字节为单位。\n返回值 NULL 表示没有足够的堆空间分配给队列而导致创建失败。 非 NULL 值表示队列创建成功。此返回值应当保存下来，以作为 操作此队列的句柄。\n修改队列数据： xQueueSendToBack() 与 xQueueSendToFront() API 函数如同函数名字面意思所期望的一样，xQueueSendToBack()用于将数据发送到队列尾；而 xQueueSendToFront()用于将数据发送到队列首。xQueueSend()完全等同于 xQueueSendToBack()。但切记不要 在中断服务例程中调用 xQueueSendToFront() 或xQueueSendToBack()。系统提供中断安全版本的xQueueSendToFrontFromISR()与xQueueSendToBackFromISR()用于在中断服务中实现相同的功能。这些将在第三章中详述。\nportBASE_TYPE xQueueSendToFront( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait ); portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait ); xQueue 目标队列的句柄。这个句柄即是调用 xQueueCreate()创建该队 列时的返回值。\npvItemToQueue 发送数据的指针。其指向将要复制到目标队列中的数据单元。 由于在创建队列时设置了队列中数据单元的长度，所以会从该指 针指向的空间复制对应长度的数据到队列的存储区域。\nxTicksToWait 阻塞超时时间。如果在发送时队列已满，这个时间即是任务处于 阻塞态等待队列空间有效的最长等待时间。如 果 xTicksToWait 设 为 0 ， 并 且 队 列 已 满 ， 则 xQueueSendToFront()与 xQueueSendToBack()均会立即返回。 阻塞时间是以系统心跳周期为单位的，所以绝对时间取决于系统 心跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位 转换为毫秒时间单位。 如 果 把 xTicksToWait 设 置 为 portMAX_DELAY ，并 且 在 FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1，那 么阻塞等待将没有超时限制。\n返回值 有两个可能的返回值: 1. pdPASS 返回 pdPASS 只会有一种情况，那就是数据被成功发送到队列 中。 如果设定了阻塞超时时间(xTicksToWait 非 0)，在函数返回之前任务将被转移到阻塞态以等待队列空间有效—在超时到来前能够将数据成功写入到队列，函数则会返回 pdPASS。 2. errQUEUE_FULL 如果设定了阻塞超时时间（ xTicksToWait 非 0），在函数返回之 前任务将被转移到阻塞态以等待队列空间有效。但直到超时也没有其它任务或是中断服务例程读取队列而腾出空间，函数则会返 回 errQUEUE_FULL。如 果 由 于 队 列 已 满 而 无 法 将 数 据 写 入 ， 则 将 返 回errQUEUE_FULL。\n读取队列数据： xQueueReceive()与 xQueuePeek() API 函数xQueueReceive()用于从队列中接收(读取）数据单元。接收到的单元同时会从队列中删除。xQueuePeek()也是从从队列中接收数据单元，不同的是并不从队列中删出接收到的单元。 xQueuePeek()从队列首接收到数据后，不会修改队列中的数据，也不会改变数据在队列中的存储序顺。切记不要在中断服务例程中调用 xQueueRceive()和 xQueuePeek()。中断安全版本的替代 API 函数 xQueueReceiveFromISR()将会在第三章中讲述。\nportBASE_TYPE xQueueReceive( xQueueHandle xQueue, const void * pvBuffer, portTickType xTicksToWait ); portBASE_TYPE xQueuePeek( xQueueHandle xQueue, const void * pvBuffer, portTickType xTicksToWait ); xQueue 被读队列的句柄。这个句柄即是调用 xQueueCreate()创建该队列 时的返回值。\npvBuffer 接收缓存指针。其指向一段内存区域，用于接收从队列中拷贝来 的数据。 数据单元的长度在创建队列时就已经被设定，所以该指针指向的 内存区域大小应当足够保存一个数据单元。\nxTicksToWait 阻塞超时时间。如果在接收时队列为空，则这个时间是任务处于阻塞状态以等待队列数据有效的最长等待时间。 如果 xTicksToWait 设为 0，并且队列为空，则 xQueueRecieve() 与 xQueuePeek()均会立即返回。 阻塞时间是以系统心跳周期为单位的，所以绝对时间取决于系统 心跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转 换为毫秒时间单位。 如 果 把 xTicksToWait 设 置 为 portMAX_DELAY ， 并 且 在 FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1，那么 阻塞等待将没有超时限制.\n返回值 有两个可能的返回值: 1. pdPASS 只有一种情况会返回 pdPASS，那就是成功地从队列中读到数据。 如果设定了阻塞超时时间(xTicksToWait 非 0)，在函数返回之前任 务将被转移到阻塞态以等待队列数据有效—在超时到来前能够从 队列中成功读取数据，函数则会返回 pdPASS。 2. errQUEUE_FULL 如果在读取时由于队列已空而没有读到任何数据，则将返回 errQUEUE_FULL。 如果设定了阻塞超时时间（ xTicksToWait 非 0），在函数返回之前 任务将被转移到阻塞态以等待队列数据有效。但直到超时也没有 其它任务或是中断服务例程往队列中写入数据，函数则会返回 errQUEUE_FULL。\n查询队列数据个数： uxQueueMessagesWaiting() API 函数uxQueueMessagesWaiting()用于查询队列中当前有效数据单元个数。切记不要在中断服务例程中调用 uxQueueMessagesWaiting()。应当在中断服务中使用其中断安全版本 uxQueueMessagesWaitingFromISR()。\nunsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue ); xQueue 被查询队列的句柄。这个句柄即是调用 xQueueCreate()创建该队列时 的返回值。\n返回值 当前队列中保存的数据单元个数。返回 0 表明队列为空。\n//例子： //发送： static void vSenderTask( void *pvParameters ) { long lValueToSend; portBASE_TYPE xStatus; /* 该任务会被创建两个实例，所以写入队列的值通过任务入口参数传递 – 这种方式使得每个实例使用不同的 值。队列创建时指定其数据单元为long型，所以把入口参数强制转换为数据单元要求的类型 */ lValueToSend = ( long ) pvParameters; /* 和大多数任务一样，本任务也处于一个死循环中 */ for( ;; ) { /* 往队列发送数据 第一个参数是要写入的队列。队列在调度器启动之前就被创建了，所以先于此任务执行。 第二个参数是被发送数据的地址，本例中即变量lValueToSend的地址。 第三个参数是阻塞超时时间 – 当队列满时，任务转入阻塞状态以等待队列空间有效。本例中没有设定超 时时间，因为此队列决不会保持有超过一个数据单元的机会，所以也决不会满。 */ xStatus = xQueueSendToBack( xQueue, \u0026amp;lValueToSend, 0 ); if( xStatus != pdPASS ) { /* 发送操作由于队列满而无法完成 – 这必然存在错误，因为本例中的队列不可能满。 */ vPrintString( \u0026#34;Could not send to the queue.\\r\\n\u0026#34; ); } /* 允许其它发送任务执行。 taskYIELD()通知调度器现在就切换到其它任务，而不必等到本任务的时 间片耗尽 */ taskYIELD(); } } //读取： static void vReceiverTask( void *pvParameters ) { /* 声明变量，用于保存从队列中接收到的数据。 */ long lReceivedValue; portBASE_TYPE xStatus; const portTickType xTicksToWait = 100 / portTICK_RATE_MS; /* 本任务依然处于死循环中。 */ for( ;; ) { /* 此调用会发现队列一直为空，因为本任务将立即删除刚写入队列的数据单元。 */ if( uxQueueMessagesWaiting( xQueue ) != 0 ) { vPrintString( \u0026#34;Queue should have been empty!\\r\\n\u0026#34; ); } /* 从队列中接收数据 第一个参数是被读取的队列。队列在调度器启动之前就被创建了，所以先于此任务执行。 第二个参数是保存接收到的数据的缓冲区地址，本例中即变量lReceivedValue的地址。此变量类型与 队列数据单元类型相同，所以有足够的大小来存储接收到的数据。 第三个参数是阻塞超时时间 – 当队列空时，任务转入阻塞状态以等待队列数据有效。本例中常量 portTICK_RATE_MS用来将100毫秒绝对时间转换为以系统心跳为单位的时间值。 */ xStatus = xQueueReceive( xQueue, \u0026amp;lReceivedValue, xTicksToWait ); if( xStatus == pdPASS ) { /* 成功读出数据，打印出来。 */ vPrintStringAndNumber( \u0026#34;Received = \u0026#34;, lReceivedValue ); } else { /* 等待100ms也没有收到任何数据。 必然存在错误，因为发送任务在不停地往队列中写入数据 */ vPrintString( \u0026#34;Could not receive from the queue.\\r\\n\u0026#34; ); } } } //main函数 static void vReceiverTask( void *pvParameters ) { /* 声明变量，用于保存从队列中接收到的数据。 */ long lReceivedValue; portBASE_TYPE xStatus; const portTickType xTicksToWait = 100 / portTICK_RATE_MS; /* 本任务依然处于死循环中。 */ for( ;; ) { /* 此调用会发现队列一直为空，因为本任务将立即删除刚写入队列的数据单元。 */ if( uxQueueMessagesWaiting( xQueue ) != 0 ) { vPrintString( \u0026#34;Queue should have been empty!\\r\\n\u0026#34; ); } /* 从队列中接收数据 第一个参数是被读取的队列。队列在调度器启动之前就被创建了，所以先于此任务执行。 第二个参数是保存接收到的数据的缓冲区地址，本例中即变量lReceivedValue的地址。此变量类型与 队列数据单元类型相同，所以有足够的大小来存储接收到的数据。 第三个参数是阻塞超时时间 – 当队列空时，任务转入阻塞状态以等待队列数据有效。本例中常量 portTICK_RATE_MS用来将100毫秒绝对时间转换为以系统心跳为单位的时间值。 */ xStatus = xQueueReceive( xQueue, \u0026amp;lReceivedValue, xTicksToWait ); if( xStatus == pdPASS ) { /* 成功读出数据，打印出来。 */ vPrintStringAndNumber( \u0026#34;Received = \u0026#34;, lReceivedValue ); } else { /* 等待100ms也没有收到任何数据。 必然存在错误，因为发送任务在不停地往队列中写入数据 */ vPrintString( \u0026#34;Could not receive from the queue.\\r\\n\u0026#34; ); } } } 队列传递复合信息： 一个简单的方式就是利用队列传递结构体，结构体成员中就包含了数据信息和来源信息。\n//例子 /* 定义队列传递的结构类型。 */ typedef struct { unsigned char ucValue; unsigned char ucSource; } xData; /* 声明两个xData类型的变量，通过队列进行传递。 */ static const xData xStructsToSend[ 2 ] = { { 100, mainSENDER_1 }, /* Used by Sender1. */ { 200, mainSENDER_2 } /* Used by Sender2. */ }; //读队列 static void vReceiverTask( void *pvParameters ) { /* 声明结构体变量以保存从队列中读出的数据单元 */ xData xReceivedStructure; portBASE_TYPE xStatus; /* This task is also defined within an infinite loop. */ for( ;; ) { /* 读队列任务的优先级最低，所以其只可能在写队列任务阻塞时得到执行。而写队列任务只会在队列写 满时才会进入阻塞态，所以读队列任务执行时队列肯定已满。所以队列中数据单元的个数应当等于队列的 深度 – 本例中队列深度为3 */ if( uxQueueMessagesWaiting( xQueue ) != 3 ) { vPrintString( \u0026#34;Queue should have been full!\\r\\n\u0026#34; ); } /* Receive from the queue. 第二个参数是存放接收数据的缓存空间。本例简单地采用一个具有足够空间大小的变量的地址。 第三个参数是阻塞超时时间 – 本例不需要指定超时时间，因为读队列任会只会在队列满时才会得到执行， 故而不会因队列空而阻塞 */ xStatus = xQueueReceive( xQueue, \u0026amp;xReceivedStructure, 0 ); if( xStatus == pdPASS ) { /* 数据成功读出，打印输出数值及数据来源。 */ if( xReceivedStructure.ucSource == mainSENDER_1 ) { vPrintStringAndNumber( \u0026#34;From Sender 1 = \u0026#34;, xReceivedStructure.ucValue ); } else { vPrintStringAndNumber( \u0026#34;From Sender 2 = \u0026#34;, xReceivedStructure.ucValue ); } } else { /* 没有读到任何数据。这一定是发生了错误，因为此任务只支在队列满时才会得到执行 */ vPrintString( \u0026#34;Could not receive from the queue.\\r\\n\u0026#34; ); } } } int main( void ) { /* 创建队列用于保存最多3个xData类型的数据单元。 */ xQueue = xQueueCreate( 3, sizeof( xData ) ); if( xQueue != NULL ) { /* 为写队列任务创建2个实例。 The 任务入口参数用于传递发送到队列中的数据。因此其中一个任务往队列中一直写入 xStructsToSend[0]，而另一个则往队列中一直写入xStructsToSend[1]。这两个任务的优先级都 设为2，高于读队列任务的优先级 */ xTaskCreate( vSenderTask, \u0026#34;Sender1\u0026#34;, 1000, \u0026amp;( xStructsToSend[ 0 ] ), 2, NULL ); xTaskCreate( vSenderTask, \u0026#34;Sender2\u0026#34;, 1000, \u0026amp;( xStructsToSend[ 1 ] ), 2, NULL ); /* 创建读队列任务。 读队列任务优先级设为1，低于写队列任务的优先级。 */ xTaskCreate( vReceiverTask, \u0026#34;Receiver\u0026#34;, 1000, NULL, 1, NULL ); /* 启动调度器，创建的任务得到执行。 */ vTaskStartScheduler(); } else { /* 创建队列失败。 */ } /* 如果一切正常， main()函数不应该会执行到这里。但如果执行到这里，很可能是内存堆空间不足导致空闲 任务无法创建。第五章将提供更多关于内存管理方面的信息 */ for( ;; ); } 队列用于大型数据单元： 如果队列存储的数据单元尺寸较大，那最好是利用队列来传递数据的指针而不是对数据本身在队列上一字节一字节地拷贝进或拷贝出。传递指针无论是在处理速度上还是内存空间利用上都更有效。\n注意：\n指针指向的内存空间的所有权必须明确 ； 当任务间通过指针共享内存时，应该从根本上保证所不会有任意两个任务同时修改共享内存中的数据，或是以其它行为方式使得共享内存数据无效或产生一致性问题。原则上，共享内存在其指针发送到队列之前，其内容只允许被发送任务访问；共享内存指针从队列中被读出之后，其内容亦只允许被接收任务访问；\n指针指向的内存空间必须有效 ； 如果指针指向的内存空间是动态分配的，只应该有一个任务负责对其进行内存释放。当这段内存空间被释放之后，就不应该有任何一个任务再访问这段空间。\n切忌用指针访问任务栈上分配的空间。因为当栈帧发生改变后，栈上的数据将不再有效。\n三、中断管理 延迟中断处理： 二值信号量可以在某个特殊的中断发生时，让任务解除阻塞，相当于让任务与中断同步。这样就可以让中断事件处理量大的工作在同步任务中完成，中断服务例程(ISR)中只是快速处理少部份工作。 如此，中断处理可以说是被”推迟(deferred)”到一个”处理(handler)”任务。 如果某个中断处理要求特别紧急，其延迟处理任务的优先级可以设为最高，以保证延迟处理任务随时都抢占系统中的其它任务。这样，延迟处理任务就成为其对应的 ISR退出后第一个执行的任务，在时间上紧接着 ISR 执行，相当于所有的处理都在 ISR 中完成一样。 延迟处理任务对一个信号量进行带阻塞性质的”take”调用，意思是进入阻塞态以等待事件发生。当事件发生后， ISR 对同一个信号量进行”give”操作，使得延迟处理任务解除阻塞，从而事件在延迟处理任务中得到相应的处理。 在经典的信号量术语中，获取信号量等同于一个 P()操作，而给出信号量等同于一个 V()操作。 在这种中断同步的情形下，信号量可以看作是一个深度为 1 的队列。这个队列由于最多只能保存一个数据单元，所以其不为空则为满(所谓”二值”)。延迟处理任务调用xSemaphoreTake()时，等效于带阻塞时间地读取队列，如果队列为空的话任务则进入阻塞态。当事件发生后， ISR 简单地通过调用xSemaphoreGiveFromISR()放置一个令牌(信号量)到队列中，使得队列成为满状态。这也使得延迟处理任务切出阻塞态，并移除令牌，使得队列再次成为空。当任务完成处理后，再次读取队列，发现队列为空，又进入阻塞态，等待下一次事件发生。\n二值信号量创建： vSemaphoreCreateBinary() API 函数\n​ FreeRTOS 中各种信号量的句柄都存储在xSemaphoreHandle 类型的变量中。在 使 用 信 号 量 之 前 ， 必 须 先 创 建 它 。 创 建二值信号量使用vSemaphoreCreateBinary()API 函数 ；\nvoid vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore ); xSemaphore 创建的信号量 需要说明的是 vSemaphoreCreateBinary()在实现上是一个宏，所以 信号量变量应当直接传入，而不是传址。本章中包含本函数调用的示 例可用于参考进行复制\nxSemaphoreTake() API 函数\n​ “带走(Taking)”一个信号量意为”获取(Obtain)”或”接收(Receive)”信号量。只有当信号量有效的时候才可以被获取。在经典信号量术中， xSemaphoreTake()等同于一次 P()操作。\n​ 除互斥信号量(Recursive Semaphore，直译为递归信号量，按通常的说法译为互斥信号量)外，所有类型的信号量都可以调用函数 xSemaphoreTake()来获取。但 xSemaphoreTake()不能在中断服务例程中调用。\nportBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait ); xSemaphore 获取得到的信号量 信号量由定义为 xSemaphoreHandle 类型的变量引用。信号量在使 用前必须先创建。\nxTicksToWait 阻塞超时时间。任务进入阻塞态以等待信号量有效的最长时间。 如果 xTicksToWait 为 0，则 xSemaphoreTake()在信号量无效时会 立即返回。 阻塞时间是以系统心跳周期为单位的，所以绝对时间取决于系统心 跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转换 为毫秒时间单位。 如 果 把 xTicksToWait 设 置 为 portMAX_DELAY ， 并 且 在 FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1，那么阻 塞等待将没有超时限制。\n返回值 有两个可能的返回值: 1. pdPASS 只有一种情况会返回 pdPASS，那就是成功获得信号量。 如果设定了阻塞超时时间(xTicksToWait 非 0)，在函数返回之前任务 将被转移到阻塞态以等待信号量有效。如果在超时到来前信号量变 为有效，亦可被成功获取，返回 pdPASS。 2. pdFALSE 未能获得信号量。 如果设定了阻塞超时时间（ xTicksToWait 非 0），在函数返回之前任 务将被转移到阻塞态以等待信号量有效。但直到超时信号量也没有 变为有效，所以不会获得信号量，返回 pdFALSE。\nxSemaphoreGiveFromISR() API 函数除 互 斥 信 号 量 外 ， FreeRTOS 支 持 的 其 它 类 型 的 信 号 量 都 可 以 通 过 调 用xSemaphoreGiveFromISR()给出。xSemaphoreGiveFromISR()是 xSemaphoreGive()的特殊形式， 专门用于中断服务例程中。\nportBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken ); xSemaphore 给出的信号量 信号量由定义为 xSemaphoreHandle 类型的变量引用。 信号量在使用前必须先创建。\npxHigherPriorityTaskWoken 对某个信号量而言，可能有不止一个任务处于阻塞态在 等待其有效。调用 xSemaphoreGiveFromISR()会让信 号量变为有效，所以会让其中一个等待任务切出阻塞 态。如果调用 xSemaphoreGiveFromISR()使得一个任 务解除阻塞，并且这个任务的优先级高于当前任务(也就 是被中断的任务)，那么 xSemaphoreGiveFromISR()会 在 函 数 内 部 将 *pxHigherPriorityTaskWoken 设 为 pdTRUE。 如 果 xSemaphoreGiveFromISR() 将 此 值 设 为 pdTRUE，则在中断退出前应当进行一次上下文切换。 这样才能保证中断直接返回到就绪态任务中优先级最 高的任务中。\n返回值 有两个可能的返回值: 1. pdPASS xSemaphoreGiveFromISR()调用成功。 2. pdFAIL 如果信号量已经有效，无法给出，则返回 pdFAIL。\n利用二值信号量对任务和中断同步： 本例在中断服务例程中使用一个二值信号量让任务从阻塞态中切换出来——从效果上等同于让任务与中断进行同步。\n//周期性产生软件中断函数 static void vPeriodicTask( void *pvParameters ) { for( ;; ) { /* 此任务通过每500毫秒产生一个软件中断来”模拟”中断事件 */ vTaskDelay( 500 / portTICK_RATE_MS ); /* 产生中断，并在产生之前和之后输出信息，以便在执行结果中直观直出执行流程 */ vPrintString( \u0026#34;Periodic task - About to generate an interrupt.\\r\\n\u0026#34; ); __asm{ int 0x82 } /* 这条语句产生中断 */ vPrintString( \u0026#34;Periodic task - Interrupt generated. \\r\\n\\r\\n\\r\\n\u0026#34; ); } } //延迟处理任务 static void vHandlerTask( void *pvParameters ) { /* As per most tasks, this task is implemented within an infinite loop. */ for( ;; ) { /* 使用信号量等待一个事件。信号量在调度器启动之前，也即此任务执行之前就已被创建。任务被无超 时阻塞，所以此函数调用也只会在成功获取信号量之后才会返回。此处也没有必要检测返回值 */ xSemaphoreTake( xBinarySemaphore, portMAX_DELAY ); /* 程序运行到这里时，事件必然已经发生。本例的事件处理只是简单地打印输出一个信息 */ vPrintString( \u0026#34;Handler task - Processing event.\\r\\n\u0026#34; ); } } //中断服务例程 static void __interrupt __far vExampleInterruptHandler( void ) { static portBASE_TYPE xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; /* \u0026#39;Give\u0026#39; the semaphore to unblock the task. */ xSemaphoreGiveFromISR( xBinarySemaphore, \u0026amp;xHigherPriorityTaskWoken ); if( xHigherPriorityTaskWoken == pdTRUE ) { /* 给出信号量以使得等待此信号量的任务解除阻塞。如果解出阻塞的任务的优先级高于当前任务的优先级 – 强制进行一次任务切\t换，以确保中断直接返回到解出阻塞的任务(优选级更高)。 说明：在实际使用中， ISR中强制上下文切换的宏依赖于具体移植。此处调用的是基于Open Watcom DOS移植的宏。其它平台下\t的移植可能有不同的语法要求。对于实际使用的平台，请参如数对应移植自带的示例程序，以决定正确的语法和符号。 */ portSWITCH_CONTEXT(); } } //main函数 int main( void ) { /* 信号量在使用前都必须先创建。本例中创建了一个二值信号量 */ vSemaphoreCreateBinary( xBinarySemaphore ); /* 安装中断服务例程 */ _dos_setvect( 0x82, vExampleInterruptHandler ); /* 检查信号量是否成功创建 */ if( xBinarySemaphore != NULL ) { /* 创建延迟处理任务。此任务将与中断同步。延迟处理任务在创建时使用了一个较高的优先级，以保证 中断退出后会被立即执行。在本例中，为延迟处理任务赋予优先级3 */ xTaskCreate( vHandlerTask, \u0026#34;Handler\u0026#34;, 1000, NULL, 3, NULL ); /* 创建一个任务用于周期性产生软件中断。此任务的优先级低于延迟处理任务。每当延迟处理任务切出，阻塞态，就会抢占周期任\t务*/ xTaskCreate( vPeriodicTask, \u0026#34;Periodic\u0026#34;, 1000, NULL, 1, NULL ); /* Start the scheduler so the created tasks start executing. */ vTaskStartScheduler(); } /* 如果一切正常， main()函数不会执行到这里，因为调度器已经开始运行任务。但如果程序运行到了这里， 很可能是由于系统内存不足而无法创建空闲任务。第五章会提供更多关于内存管理的信息 */ for( ;; ); } 中断产生。\n中断服务例程启动，给出信号量以使延迟处理任务解除阻塞。\n当中断服务例程退出时，延迟处理任务得到执行。延迟处理任务做的第一件事便是获取信号量。\n延迟处理任务完成中断事件处理后，试图再次获取信号量——如果此时信号量无效，任务将切入阻塞待等待事件发生。\n计数信号量： 一个二值信号量最多只可以锁存一个中断事件。在锁存的事件还未被处理之前，如果还有中断事件发生，那么后续发生的中断事件将会丢失。如果用计数信号量代替二值信号量，那么，这种丢中断的情形将可以避免。就如同我们可以把二值信号量看作是只有一个数据单元的队列一样，计数信号量可以看作是深度大于 1 的队列。任务其实对队列中存储的具体数据并不感兴趣——其只关心队列是空还是非空。计数信号量每次被给出(Given)，其队列中的另一个空间将会被使用。队列中的有效数据单元个数就是信号量的”计数(Count)”值。\n两种典型用法：\n事件计数； 在这种用法中，每次事件发生时，中断服务例程都会“给出(Give)”信号量——信号量在每次被给出时其计数值加 1。延迟处理任务每处理一个任务都会”获取(Take)”一次 信号量——信号量在每次被获取时其计数值减 1。信号量的计数值其实就是已发生事件的数目与已处理事件的数目之间的差值。\n用于事件计数的计数信号量，在被创建时其计数值被初始化为 0。\n资源管理； 在这种用法中，信号量的计数值用于表示可用资源的数目。一个任务要获取资源的控制权，其必须先获得信号量——使信号量的计数值减 1。当计数值减至 0，则表示没有可用资源。当任务利用资源完成工作后，将给出(归还)信号量——使信号量的计数值加 1。\n用于资源管理的信号量，在创建时其计数值被初始化为可用资源总数。\nxSemaphoreCreateCounting() API 函数FreeRTOS 中所有种类的信号量句柄都由声明为 xSemaphoreHandle 类型的变量保存。信号量在使用前必须先被创建。使用xSemaphoreCreateCounting() API 函数来创建一个计数信号量。\nxSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount );//函数原型 uxMaxCount 最大计数值。如果把计数信号量类比于队列的话， uxMaxCount 值 就是队列的最大深度。 当此信号量用于对事件计数或锁存事件的话， uxMaxCount 就是可 锁存事件的最大数目。 当此信号量用于对一组资源的访问进行管理的话， uxMaxCount 应 当设置为所有可用资源的总数。\nuxInitialCount 信号量的初始计数值。 当此信号量用于事件计数的话， uxInitialCount 应当设置为 0——因 为当信号量被创建时，还没有事件发生。 当 此 信 号 量 用 于 资 源 管 理 的 话 ， uxInitialCount 应 当 等 于 uxMaxCount——因为当信号量被创建时，所有的资源都是可用的。\n返回值 如果返回 NULL 值，表示堆上内存空间不足，所以 FreeRTOS 无法 为信号量结构分配内存导致信号量创建失败。第五章有提供更多的 内存管理方面的信息。 如果返回非 NULL 值，则表示信号量创建成功。此值应当被保存起 来作为这个的信号量的句柄。\n利用计数信号量对任务和中断进行同步： /* 在信号量使用之前必须先创建。本例中创建了一个计数信号量。此信号量的最大计数值为10，初始计数值为0 */ xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 ); /*为了模拟多个事件以高频率发生，修改了中断服务例程，在每次中断多次”给出 (Give)”信号量。每个事件都锁存到信号量的计数值中。修改后的中断服务例程如程序清 单 50 所示。 其它函数都复用之前的代码，保持不变*/ static void __interrupt __far vExampleInterruptHandler( void ) { static portBASE_TYPE xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; /* 多次给出信号量。第一次给出时使得延迟处理任务解除阻塞。后续给出用于演示利用被信号量锁存事件， 以便延迟处理任何依序对这些中断事件进行处理而不会丢中断。用这种方式来模拟处理器产生多个中断，尽管 这些事件只是在单次中断中模拟出来的 */ xSemaphoreGiveFromISR( xCountingSemaphore, \u0026amp;xHigherPriorityTaskWoken ); xSemaphoreGiveFromISR( xCountingSemaphore, \u0026amp;xHigherPriorityTaskWoken ); xSemaphoreGiveFromISR( xCountingSemaphore, \u0026amp;xHigherPriorityTaskWoken ); if( xHigherPriorityTaskWoken == pdTRUE ) { /* 给出信号量以使得等待此信号量的任务解除阻塞。如果解出阻塞的任务的优先级高于当前任务的优先 级 – 强制进行一次任务切换，以确保中断直接返回到解出阻塞的任务(优选级更高)。 说明：在实际使用中， ISR中强制上下文切换的宏依赖于具体移植。此处调用的是基于Open Watcom DOS 移植的宏。其它平台下的移植可能有不同的语法要求。对于实际使用的平台，请参如数对应移植自带的示 例程序，以决定正确的语法和符号。 */ portSWITCH_CONTEXT(); } } //每次中断发生后，延迟处理任务处理了中断生成的全部三个事件[模拟出来的]。 这些事件被锁存到信号量的计数值中，以使得延迟处理任务可以对它们依序进行处理。 中断服务中使用队列： xQueueSendToFrontFromISR()，xQueueSendToBackFromISR()与 xQueueReceiveFromISR()分别是 xQueueSendToFront()， xQueueSendToBack()与 xQueueReceive()的中断安全版本，专门用于中断服务例程中。信号量用于事件通信。而队列不仅可以用于事件通信，还可以用来传递数据。\nportBASE_TYPE xQueueSendToFrontFromISR( xQueueHandle xQueue, void *pvItemToQueue portBASE_TYPE *pxHigherPriorityTaskWoken ); portBASE_TYPE xQueueSendToBackFromISR( xQueueHandle xQueue, void *pvItemToQueue portBASE_TYPE *pxHigherPriorityTaskWoken); xQueue 目标队列的句柄。这个句柄即是调用 xQueueCreate() 创建该队列时的返回值。\npvItemToQueue 发送数据的指针。其指向将要复制到目标队列中的数据 单元。 由于在创建队列时设置了队列中数据单元的长度，所以 会从该指针指向的空间复制对应长度的数据到队列的 存储区域。\npxHigherPriorityTaskWoken 对某个队列而言，可能有不止一个任务处于阻塞态在等 待其数据有效。调用 xQueueSendToFrontFromISR() 或 xQueueSendToBackFromISR()会使得队列数据变 为有效，所以会让其中一个等待任务切出阻塞态。如果 调用这两个 API 函数使得一个任务解除阻塞，并且这个 任务的优先级高于当前任务(也就是被中断的任务)，那 么 API 会在函数内部将*pxHigherPriorityTaskWoken 设 为 pdTRUE。 如果这两个 API 函数将此值设为 pdTRUE，则在中断退 出前应当进行一次上下文切换。这样才能保证中断直接 返回到就绪态任务中优先级最高的任务中。\n返回值 有两个可能的返回值: 1. pdPASS 返回 pdPASS 只会有一种情况， 那就是数据被成功发送 到队列中。 2. errQUEUE_FULL 如 果 由 于 队 列 已 满 而 无 法 将 数 据 写 入 ， 则 将 返 回 errQUEUE_FULL。\n利用队列在中断服务中发送或接收数据 ：\n/*本 例 演 示 在 同 一 个 中 断 服 务 中 使 用 xQueueSendToBackFromISR() 和 xQueueReceiveFromISR()。和之前一样，采用软件中断以方便实现。 创建一个周期任务用于每 200 毫秒往队列中发送五个数值，五个数值都发送完后 便产生一个软件中断。周期任务的实现代码参见程序清单 53。*/ static void vIntegerGenerator( void *pvParameters ) { portTickType xLastExecutionTime; unsigned portLONG ulValueToSend = 0; int i; /* 初始化变量，用于调用 vTaskDelayUntil(). */ xLastExecutionTime = xTaskGetTickCount(); for( ;; ) { /* 这是个周期性任务。进入阻塞态，直到该再次运行的时刻。此任务每200毫秒执行一次 */ vTaskDelayUntil( \u0026amp;xLastExecutionTime, 200 / portTICK_RATE_MS ); /* 连续五次发送递增数值到队列。这此数值将在中断服务例程中读出。中断服务例程会将队列读空，所 以此任务可以确保将所有的数值都发送到队列。因此不需要指定阻塞超时时间 */ for( i = 0; i configKERNEL_INTERRUPT_PRIORITY 设置系统心跳时钟的中断优先级。 如 果 在 移 植 中 没 有 使 用 常 量 configMAX_SYSCALL_INTERRUPT_PRIORITY，那 么需要调用中断安全版本 FreeRTOS API 的中断都必须运行在此优先级上。 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置中断安全版本 FreeRTOS API 可以运 行的最高中断优先级。 \u0026gt; 建立一个全面的中断嵌套模型需要设置configMAX_SYSCALL_INTERRUPT_PRIRITY为比 configKERNEL_INTERRUPT_PRIORITY更高的优先级。 ==在任务优先级和中断优先级之间常常会产生一些混淆。中断优先级是由微控制器架构体系所定义的。中断优先级是硬件控制的优先级，中断服务例程的执行会与之关联。任务并非运行在中断服务中，所以赋予任务的软件优先级与赋予中断源的硬件优先级之间没有任何关系;== ![f1bc90e982af100396b5aabb12d69dd](img-3.png) - 处于中断优先级 1 到 3(含)的中断会被内核或处于临界区的应用程序阻塞执行， 但是它们可以调用中断安全版本的 FreeRTOS API 函数； - 处于中断优先级 4 及以上的中断不受临界区影响，所以其不会被内核的任何行为阻塞，可以立即得到执行——这是由微控制器本身对中断优先级的限定所决定的。通常 需 要 严 格 时 间 精 度 的 功 能 ( 如 电 机 控 制 ) 会 使 用 高 于 configMAX_SYSCALL_INTERRUPT_PRIRITY 的优先级，以保证调度器不会对其中断响应时间造成抖动； - 不需要调用任何 FreeRTOS API 函数的中断，可以自由地使用任意优先级； **注意：** \u0026gt; Cortex M3 使用低优先级号数值表示逻辑上的高优先级中断。这显得不是那么直观，所以很容易被忘记。如果你想对某个中断赋予低优先级，则必须使用一个高优先级号数值。千万不要给它指定优先级号 0(或是其它低优先级号数值)，因为这将会使得这个 中 断 在 系 统 中 拥 有 最 高 优 先 级 — — 如 果 这 个 优 先 级 高 于configMAX_SYSCALL_INTERRUPT_PRIRITY，将很可能导致系统崩溃。Cortex M3 内核的最低优先级为 255，但是不同的 Cortex M3 处理器厂商实现的优先级位数不尽相同，而各自的配套库函数也使用了不同的方式来支持中断优先级。比如STM32， ST 的驱动库中将最低优先级指定为 15，而最高优先级指定为 0。 ### 四、资源管理 #### 非原子操作： \u0026gt; 这是一个”非原子”操作，因为完成整个操作需要不止一条指令，所以操作过程可能被中断。 #### 多任务系统中的风险： - 访问外设； - 读-改-写操作； - 变量的非原子访问（更新16位机上的32位变量）； - 函数重入（如果一个函数可以安全地被多个任务调用，或是在任务与中断中均可调用，则这个函数是可重入的 。） 每个任务都单独维护自己的栈空间及其自身在的内存寄存器组中的值。 如果一个函数除了访问自己栈空间上分配的数据或是内核寄存器中的数据外，不会访问其它任何数据，则这个函数就是可重入的。 ```c //可重入函数示例 long lAddOneHundered( long lVar1 ) { long lVar2; lVar2 = lVar1 + 100; return lVar2; } //不可重入函数示例 long lVar1; long lNonsenseFunction( void ) { static long lState = 0; long lReturn; switch( lState ) { case 0 : lReturn = lVar1 + 10; lState = 1; break; case 1 : lReturn = lVar1 + 20; lState = 0; break; } } 可重入和不可重入区别：\n一、不可重入函数1、概念 不可重入函数，即不能重复进入的函数，不能被中断的函数。在多个任务调用这个函数时可能修改其他任务调用这个函数的数据，从而导致不可预料的后果。不可重入函数在实时系统设计中被视为不安全函数。 2、特点 有以下条件都属于不可重入函数： 函数体内使用了静态的数据结构；（static） 函数体内调用了malloc()或者free()函数； 函数体内调用了标准I/O函数。 函数体内访问了全局变量 3、其他 在许多的处理器/编译器中，浮点一般都是不可重入的。 printf()经常有重入和性能上的问题。 二、可重入函数1、概念 可重入函数，即可以重复进入的函数，能被中断的函数。可以在这个函数执行的任何时候中断他的运行，在任务调度下去执行另外一段代码而不会出现什么错误。 2、实现 在函数体内不访问那些全局变量，不使用静态局部变量，坚持只使用缺省态（auto）局部变量。 如果使用全局变量（包括static），则应通过关中断、信号量（即P、V操作）等互斥方法对其加以保护。否则此函数就不具有可重入性，即当多个进程调用此函数时，很有可能使有关全局变量变为不可知状态。 在和硬件发生交互的时候，关闭硬件中断。完成交互打开中断。 不能调用其它不可重入的函数。\n互斥： 访问一个被多任务共享，或是被任务与中断共享的资源时，需要采用”互斥”技术以保证数据在任何时候都保持一致性。这样做的目的是要确保任务从开始访问资源就具有排它性，直至这个资源又恢复到完整状态。FreeRTOS 提供了多种特性用以实现互斥，但是最好的互斥方法（如果可能的话，任何时候都当如此）还是通过精心设计应用程序，尽量不要共享资源，或者是每个资源都通过单任务访问。\n临界区与挂起调度器 : 基本临界区 基本临界区是指宏 taskENTER_CRITICAL()与taskEXIT_CRITICAL()之间的代码区间;\n示例代码：\n/* 为了保证对PORTA寄存器的访问不被中断，将访问操作放入临界区。进入临界区 */ taskENTER_CRITICAL(); /* 在taskENTER_CRITICAL() 与 taskEXIT_CRITICAL()之间不会切换到其它任务。 中断可以执行，也允许嵌套，但只是针对优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断 – 而且这些中断不允许访问FreeRTOS API 函数. */ PORTA |= 0x01; /* 我们已经完成了对PORTA的访问，因此可以安全地离开临界区了。 */ taskEXIT_CRITICAL(); void vPrintString( const portCHAR *pcString ) { /* 往stdout中写字符串，使用临界区这种原始的方法实现互斥。 */ taskENTER_CRITICAL(); { printf( \u0026#34;%s\u0026#34;, pcString ); fflush( stdout ); } taskEXIT_CRITICAL(); /* 允许按任意键停止应用程序运行。实际的应用程序如果有使用到键值，还需要对键盘输入进行保护。 */ if( kbhit() ) { vTaskEndScheduler(); } } 临界区是提供互斥功能的一种非常原始的实现方法。临界区的工作仅仅是简单地把中断全部关掉，或是关掉优先级在 configMAX_SYSCAL_INTERRUPT_PRIORITY 及以下的中断——依赖于具体使用的 FreeRTOS 移植。抢占式上下文切换只可能在某个中断中完成，所以调用taskENTER_CRITICAL()的任务可以在中断关闭的时段一直保持运行态，直到退出临界区 ;\nPS：\n临界区必须只能具有很短的时间；\n临界区嵌套是安全的；\n另一种实现临界区的方法： 挂起(锁定)调度器 也可以通过挂起调度器来创建临界区。挂起调度器有些时候也被称为锁定调度器。基本临界区保护一段代码区间不被其它任务或中断打断。由挂起调度器实现的临界区只可以保护一段代码区间不被其它任务打断，因为这种方式下，中断是使能的。如果一个临界区太长而不适合简单地关中断来实现，可以考虑采用挂起调度器的方式。但是唤醒(resuming, or un-suspending)调度器却是一个相对较长的操作。所以评估哪种是最佳方式需要结合实际情况。\n调度器挂起函数：\nportBASE_TYPE xTaskResumeAll( void ); 在调度器处于挂起状态时，不能调用 FreeRTOS API 函数;\n调度器唤醒函数：\nportBASE_TYPE xTaskResumeAll( void ); 返回值 在调度器挂起过程中，上下文切换请求也会被挂起，直到调度器 被唤醒后才会得到执行。如果一个挂起的上下文切换请求在 xTaskResumeAll()返回前得到执行，则函数返回 pdTRUE。在其 它情况下， xTaskResumeAll()返回 pdFALSE。\n嵌套调用 vTaskSuspendAll()和 xTaskResumeAll()是安全的，因为内核有维护一个嵌套深度计数。调度器只会在嵌套深度计数为 0 时才会被唤醒——即在为每个之前调用的 vTaskSuspendAll()都配套调用了 xTaskResumAll()之后。\n例子：\nvoid vPrintString( const portCHAR *pcString ) { /* Write the string to stdout, suspending the scheduler as a method of mutual exclusion. */ vTaskSuspendScheduler(); { printf( \u0026#34;%s\u0026#34;, pcString ); fflush( stdout ); } xTaskResumeScheduler(); /* Allow any key to stop the application running. A real application that actually used the key value should protect access to the keyboard input too. */ if( kbhit() ) { vTaskEndScheduler(); } } 互斥量（及二值信号量）： 互斥量是一种特殊的二值信号量，用于控制在两个或多个任务间访问共享资源。单词MUTEX(互斥量)源于”MUTual EXclusion”。在用于互斥的场合，互斥量从概念上可看作是与共享资源关联的令牌。一个任务想要合法地访问资源，其必须先成功地得到(Take)该资源对应的令牌(成为令牌持有者)。当令牌持有者完成资源使用，其必须马上归还(Give)令牌。只有归还了令牌，其它任务才可能成功持有，也才可能安全地访问该共享资源。一个任务除非持有了令牌，否则不允许访问共享资源。\n互斥量和二值信号量区别：\n用于互斥的信号量必须归还；\n用于同步的信号量通常是完成同步之后便丢弃，不再归还；\nxSemaphoreCreateMutex() API 函数 互斥量是一种信号量。 FreeRTOS 中所有种类的信号量句柄都保存在类型为xSemaphoreHandle 的变量中。 互 斥 量 在 使 用 前 必 须 先 创 建 。创 建一个互斥 量 类 型 的 信 号 量 需 要 使 用xSemaphoreCreateMutex() API 函数。\nxSemaphoreHandle xSemaphoreCreateMutex( void ); 返回值 如果返回 NULL 表示互斥量创建失败。原因是内存堆空间不足导致 FreeRTOS 无法为互斥量分配结构数据空间。第五章提供更多关于内存 管理方面的信息。 返回非 NULL 值表示互斥量创建成功。返回值应当保存起来作为该互斥 量的句柄。\n例子（使用信号量）：\n/*本例创建了一个新版本的 vPrintString()，称为 prvNewPrintString()，然后在多任务 中调用这个新版函数。 prvNewPrintString()具有与 vPrintString()完全相同的功能，只是 在实现上使用互斥量代替基本临界区来实现对标准输出的控制。*/ static void prvNewPrintString( const portCHAR *pcString ) { /* 互斥量在调度器启动之前就已创建，所以在此任务运行时信号量就已经存在了。 试图获得互斥量。如果互斥量无效，则将阻塞，进入无超时等待。 xSemaphoreTake()只可能在成功获得互 斥量后返回，所以无需检测返回值。如果指定了等待超时时间，则代码必须检测到xSemaphoreTake()返回 pdTRUE后，才能访问共享资源(此处是指标准输出)。 */ xSemaphoreTake( xMutex, portMAX_DELAY ); { /* 程序执行到这里表示已经成功持有互斥量。现在可以自由访问标准输出，因为任意时刻只会有一个任 务能持有互斥量。 */ printf( \u0026#34;%s\u0026#34;, pcString ); fflush( stdout ); /* 互斥量必须归还! */ } xSemaphoreGive( xMutex ); /* Allow any key to stop the application running. A real application that actually used the key value should protect access to the keyboard too. A real application is very unlikely to have more than one task processing key presses though! */ if( kbhit() ) { vTaskEndScheduler(); } } /*prvNewPrintString()被一个任务的两个实例重复调用。在每次调用之间采用了一个 随机延迟时间。任务的入口参数用于向任务的每个实例传递各自的输出字符串。*/ int main( void ) { /* 信号量使用前必须先创建。本例创建了一个互斥量类型的信号量。 */ xMutex = xSemaphoreCreateMutex(); /* 本例中的任务会使用一个随机延迟时间，这里给随机数发生器生成种子。 */ srand( 567 ); /* Check the semaphore was created successfully before creating the tasks. */ if( xMutex != NULL ) { /* Create two instances of the tasks that write to stdout. The string they write is passed in as the task parameter. The tasks are created at different priorities so some pre-emption will occur. */ xTaskCreate( prvPrintTask, \u0026#34;Print1\u0026#34;, 1000, \u0026#34;Task 1 ******************************************\\r\\n\u0026#34;, 1, NULL ); xTaskCreate( prvPrintTask, \u0026#34;Print2\u0026#34;, 1000, \u0026#34;Task 2 ------------------------------------------\\r\\n\u0026#34;, 2, NULL ); /* Start the scheduler so the created tasks start executing. */ vTaskStartScheduler(); } /* 如果一切正常， main()函数不会执行到这里，因为调度器已经开始运行任务。但如果程序运行到了这里， 很可能是由于系统内存不足而无法创建空闲任务。第五章会提供更多关于内存管理的信息 */ for( ;; ); } 存在的问题：优先级反转； 在这种可能的执行流程描述中，高优先级的任务 2 竟然必须等待低优先级的任务 1 放弃对互斥量的持有权。高优先级任务被低优先级任务阻塞推迟的行为被称为”优先级反转”。这是一种不合理的行为方式，如果把这种行为再进一步放大，当高优先级任务正等待信号量的时候，一个介于两个任务优先之间的中等优先级任务开始执行——这就会导致一个高优先级任务在等待一个低优先级任务，而低优先级任务却无法执行！\n例如：\n解决方法：优先级继承（使用互斥量）； 互斥量自动提供了一个基本的”优先级继承”机制。优先级继承是最小化优先级反转负面影响的一种方案——其并不能修正优先级反转带来的问题，仅仅是减小优先级反转的影响。优先级继承使得系统行为的数学分析更为复杂，所以如果可以避免的话，并不建议系统实现对优先级继承有所依赖。 优先级继承暂时地将互斥量持有者的优先级提升至所有等待此互斥量的任务所具有的最高优先级。持有互斥量的低优先级任务”继承”了等待互斥量的任务的优先级。互斥量持有者在归还互斥量时，优先级会自动设置为其原来的优先级。\n由于最好是优先考虑避免优先级反转，并且因为 FreeRTOS 本身是面向内存有限的微控制器，所以只实现了最基本的互斥量的优先级继承机制，这种实现假定一个任务在任意时刻只会持有一个互斥量 ;\n死锁（互斥量提供互斥功能的另一个缺陷）： 当两个任务都在等待被对方持有的资源时，两个任务都无法再继续执行，这种情况就被称为死锁。考虑如下情形，任务 A 与任务 B 都需要获得互斥量 X 与互斥量 Y 以完成各自的工作：\n任务 A 执行，并成功获得了互斥量 X。\n任务 A 被任务 B 抢占。\n任务 B 成功获得了互斥量 Y，之后又试图获取互斥量 X——但互斥量 X 已经被任务 A 持有，所以对任务 B 无效。任务 B 选择进入阻塞态以等待互斥量 X 被释放。\n任务 A 得以继续执行。其试图获取互斥量 Y——但互斥量 Y 已经被任务 B持有而对任务 A 无效。任务 A 也选择进入阻塞态以等待互斥量 Y 被释放。\n​ 这种情形的最终结局是，任务 A 在等待一个被任务 B 持有的互斥量，而任务 B 也在等待一个被任务 A 持有的互斥量。死锁于是发生，因为两个任务都不可能再执行下去了。\n​ 和优先级反转一样，避免死锁的最好方法就是在设计阶段就考虑到这种潜在风险，这样设计出来的系统就不应该会出现死锁的情况。于实践经验而言，对于一个小型嵌入式系统，死锁并不是一个大问题，因为系统设计者对整个应用程序都非常清楚，所以能够找出发生死锁的代码区域，并消除死锁问题。\n守护任务： 守护任务提供了一种干净利落的方法来实现互斥功能，而不用担心会发生优先级反转和死锁。守护任务是对某个资源具有唯一所有权的任务。只有守护任务才可以直接访问其守护的资源——其它任务要访问该资源只能间接地通过守护任务提供的服务。\n例子：\n​ 下例提供了 vPrintString()的另一种实现方法，这里采用了一个守护任务来管理对标准输出的访问。当一个任务想要往终端写信息的时候，其不能直接调用打印函数，而是将消息发送到守护任务。 守护任务使用了一个 FreeRTOS 队列来对终端实现串行化访问。该任务内部实现；不必考虑互斥，因为它是唯一能够直接访问终端的任务。 ​ 守护任务大部份时间都在阻塞态等待队列中有信息到来。当一个信息到达时，守护任务仅仅简单地将收到的信息写到标准输出上，然后又返回阻塞态，继续等待下一条信息地到来。守护任务的具体实现参见程序。 中断中可以写队列，所以中断服务例程也可以安全地使用守护任务提供的服务，从而把信息输出到终端。在本例中，一个心跳中断钩子函数用于每 200 心跳周期就输出一个消息。\n心跳钩子函数(或称回调函数)由内核在每次心跳中断时调用。要挂接一个心跳钩子函数，需要做以下配置： 设置 FreeRTOSConfig.h 中的常量 configUSE_TICK_HOOK 为 1。 提供钩子函数的具体实现，要使用固定的函数名和原型(void vApplicationTickHook( void ); )。 心跳钩子函数在系统心跳中断的上下文上执行，所以必须保证非常短小，适度占用栈空间，并且不要调用任何名字不带后缀 ”FromISR ”的 FreeRTOS API 函数。\nstatic void prvStdioGatekeeperTask( void *pvParameters ) { char *pcMessageToPrint; /* 这是唯一允许直接访问终端输出的任务。任何其它任务想要输出字符串，都不能直接访问终端，而是将要 输出的字符串发送到此任务。并且因为只有本任务才可以访问标准输出，所以本任务在实现上不需要考虑互斥 和串行化等问题。 */ for( ;; ) { /* 等待信息到达。指定了一个无限长阻塞超时时间，所以不需要检查返回值 – 此函数只会在成功收到 消息时才会返回。 */ xQueueReceive( xPrintQueue, \u0026amp;pcMessageToPrint, portMAX_DELAY ); /* 输出收到的字符串。 */ printf( \u0026#34;%s\u0026#34;, pcMessageToPrint ); fflush( stdout ); /* Now simply go back to wait for the next message. */ } } static void prvPrintTask( void *pvParameters ) { int iIndexToString; /* Two instances of this task are created. The task parameter is used to pass an index into an array of strings into the task. Cast this to the required type. */ iIndexToString = ( int ) pvParameters; for( ;; ) { /* 打印输出字符串，不能直接输出，通过队列将字符串指针发送到守护任务。队列在调度器启动之前就 创建了，所以任务执行时队列就已经存在了。并有指定超时等待时间，因为队列空间总是有效。 */ xQueueSendToBack( xPrintQueue, \u0026amp;( pcStringsToPrint[ iIndexToString ] ), 0 ); /* 等待一个伪随机时间。注意函数rand()不要求可重入，因为在本例中rand()的返回值并不重要。但 在安全性要求更高的应用程序中，需要用一个可重入版本的rand()函数 – 或是在临界区中调用rand() 函数。 */ vTaskDelay( ( rand() \u0026amp; 0x1FF ) ); } } 心跳钩子函数仅仅是简单地对其被调用次数进行计数，当计数至 200 时就向守护任务发送信息。为了具有更好的演示效果，心跳钩子函数将信息发送到队列首，而打印输出任务将信息发送到队列尾。心跳钩子函数的实现代码如下所示。\nvoid vApplicationTickHook( void ) { static int iCount = 0; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* Print out a message every 200 ticks. The message is not written out directly, but sent to the gatekeeper task. */ iCount++; if( iCount \u0026gt;= 200 ) { /* In this case the last parameter (xHigherPriorityTaskWoken) is not actually used but must still be supplied. */ xQueueSendToFrontFromISR( xPrintQueue,\u0026amp;( pcStringsToPrint[ 2 ] ),\u0026amp;xHigherPriorityTaskWoken ); /* Reset the count ready to print out the string again in 200 ticks time. */ iCount = 0; } } main函数：\n/* 定义任务和中断将会通过守护任务输出的字符串。 */ static char *pcStringsToPrint[] = { \u0026#34;Task 1 ****************************************************\\r\\n\u0026#34;, \u0026#34;Task 2 ----------------------------------------------------\\r\\n\u0026#34;, \u0026#34;Message printed from the tick hook interrupt ##############\\r\\n\u0026#34; }; /*-----------------------------------------------------------*/ /* 声明xQueueHandle变量。这个变量将会用于打印任务和中断往守护任务发送消息。 */ xQueueHandle xPrintQueue; /*-----------------------------------------------------------*/ int main( void ) { /* 创建队列，深度为5，数据单元类型为字符指针。 */ xPrintQueue = xQueueCreate( 5, sizeof( char * ) ); /* 为伪随机数发生器产生种子。 */ srand( 567 ); /* Check the queue was created successfully. */ if( xPrintQueue != NULL ) { /* 创建任务的两个实例，用于向守护任务发送信息。任务入口参数传入需要输出的字符串索引号。这两 个任务具有不同的优先级，所以高优先级任务有时会抢占低优先级任务。 */ xTaskCreate( prvPrintTask, \u0026#34;Print1\u0026#34;, 1000, ( void * ) 0, 1, NULL ); xTaskCreate( prvPrintTask, \u0026#34;Print2\u0026#34;, 1000, ( void * ) 1, 2, NULL ); /* 创建守护任务。这是唯一一个允许直接访问标准输出的任务。 */ xTaskCreate( prvStdioGatekeeperTask, \u0026#34;Gatekeeper\u0026#34;, 1000, NULL, 0, NULL ); /* Start the scheduler so the created tasks start executing. */ vTaskStartScheduler(); } /* 如果一切正常， main()函数不会执行到这里，因为调度器已经开始运行任务。但如果程序运行到了这里， 很可能是由于系统内存不足而无法创建空闲任务。第五章会提供更多关于内存管理的信息 */ for( ;; ); } 守护任务的优先级低于打印任务——所以发送到守护任务的消息会一直保持在队列中，直到两个打印任务都进入阻塞态。在一些情况下，需要给守护任务赋予一个较高的优先级，消息就可以得到更快的处理——但这样做会由于守护任务的开销使得低优先级任务被推迟，直到守护任务完成对受其保护的资源的访问。\n五、内存管理 标准malloc()和free()库函数缺点： 这两个函数在小型嵌入式系统中可能不可用。\n这两个函数的具体实现可能会相对较大，会占用较多宝贵的代码空间。\n这两个函数通常不具备线程安全特性。\n这两个函数具有不确定性。每次调用时的时间开销都可能不同。\n这两个函数会产生内存碎片。\n这两个函数会使得链接器配置得复杂\n当内核请求内存时，其调用 pvPortMalloc()而不是直接调用 malloc()；当释放内存时，调用 vPortFree()而不是直接调用 free()。 pvPortMalloc()具有与 malloc()相同的函数原型；vPortFree()也具有与 free()相同的函数原型。 在小型嵌入式系统中，通常是在启动调度器之前创建任务，队列和信号量。这种情况表明，动态分配内存只会出现在应用程序真正开始执行实时功能之前，而且内存一旦分配就不会再释放。这就意味着选择内存分配方案时不必考虑一些复杂的因素，比如确定性与内存碎片等，而只需要从性能上考虑，比如代码大小和简易性。\nHeap_1.c : Heap_1.c 实现了一个非常基本的 pvPortMalloc()版本，而且没有实现 vPortFree()。如果应用程序不需要删除任务，队列或者信号量，则具有使用 heap_1 的潜质。 Heap_1总是具有确定性。这种分配方案是将 FreeRTOS 的内存堆空间看作一个简单的数组。当调用pvPortMalloc()时，则将数组又简单地细分为更小的内存块 。\n​ 数组的总大小(字节为单位)在 FreeRTOSConfig.h 中由 configTOTAL_HEAP_SIZE定义。以这种方式定义一个巨型数组会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前 ；\n​ 需要为每个创建的任务在堆空间上分配一个任务控制块(TCB)和一个栈空间。\nHeap_2.c： Heap_2.c 也是使用了一个由 configTOTAL_HEAP_SIZE 定义大小的简单数组。不同于 heap_1 的是， heap_2 采用了一个最佳匹配算法来分配内存，并且支持内存释放。由于声明了一个静态数组，所以会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前。最佳匹配算法保证 pvPortMalloc()会使用最接近请求大小的空闲内存块。比如，考虑以下情形： 堆空间中包含了三个空闲内存块，分别为 5 字节， 25 字节和 100 字节大小。 pvPortMalloc()被调用以请求分配 20 字节大小的内存空间。 匹配请求字节数的最小空闲内存块是具有25字节大小的内存块——所以pvPortMalloc()会将这个 25 字节块再分为一个 20 字节块和一个 5 字节块 ，然后返回一个指向 20 字节块的指针。剩下的 5 字节块则保留下来，留待以后调用 pvPortMalloc()时使用。\nHeap_3.c： Heap_3.c 简单地调用了标准库函数 malloc()和 free()，但是通过暂时挂起调度器使得函数调用备线程安全特性。 此时的内存堆空间大小不受 configTOTAL_HEAP_SIZE 影响，而是由链接器配置决定。\n六、错误排查 效率更高的sprintf： printf-stdarg.c当调用标准 C 库函数时，栈空间使用量可能会急剧上升，特别是 IO 与字符串处理函数，比如 sprintf()。在 FreeRTOS 下载包中有一个名为 printf-stdarg.c 的文件。这个文件实现了一个栈效率优化版的小型 sprintf()，可以用来代替标准 C 库函数版本。在大多数情况下，这样做可以使得调用sprintf()及相关函数的任务对栈空间的需求量小很多。\n栈溢出问题： uxTaskGetStackHighWaterMark() API 函数、 每个任务都独立维护自己的栈空间， 栈空间总量在任务创建时进行设定。uxTaskGetStackHighWaterMark()主要用来查询指定任务的运行历史中， 其栈空间还差多少就要溢出。这个值被称为栈空间的”高水线(High Water Mark)” ;\nunsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask ); | xTask | 被查询任务的句柄——欲知如何获得任务句柄，详情请参见 API 函数 xTaskCreate()的参数 pxCreatedTask。 如果传入 NULL 句柄，则任务查询的是自身栈空间的高水线。 | | :——: | :—————————————————————————————- | | 返回值 | 任务栈空间的实际使用量会随着任务执行和中断处理过程上下浮动。 uxTaskGetStackHighWaterMark()返回从任务启动执行开始的运行 历史中，栈空间具有的最小剩余量。这个值即是栈空间使用达到最深 时的剩下的未使用的栈空间。这个值越是接近 0，则这个任务就越是 离栈溢出不远了。 |\n运行时栈侦测 FreeRTOS 包含两种运行时栈侦测机制，由 FreeRTOSConfig.h 中的配置常量configCHECK_FOR_STACK_OVERFLOW 进行控制。这两种方式都会增加上下切换开销。\n栈溢出钩子函数(或称回调函数)由内核在侦测到栈溢出时调用。要使用栈溢出钩子函数，需要进行以下配置： 在 FreeRTOSConfig.h 中把configCHECK_FOR_STACK_OVERFLOW 设为 1 或 2；\n提供钩子函数的具体实现； void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName ); 运行时栈侦测：方法 1\n​ 当 configCHECK_FOR_STACK_OVERFLOW 设置为 1 时选用方法 1。\n​ 任务被交换出去的时候，该任务的整个上下文被保存到它自己的栈空间中。这时任务栈的使用应当达到了一个峰值。当 configCHECK_FOR_STACK_OVERFLOW 设为1 时，内核会在任务上下文保存后检查栈指针是否还指向有效栈空间。一旦检测到栈指针的指向已经超出任务栈的有效范围，栈溢出钩子函数就会被调用。\n方法 1 具有较快的执行速度，但栈溢出有可能发生在两次上下文保存之间，这种情况不会被侦测到。\n运行时栈侦测：方法 2\n​ 将 configCHECK_FOR_STACK_OVERFLOW 设为 2 就可以选用方法 2。\n​ 方法 2在方法 1 的基础上进行了一些补充。当创建任务时，任务栈空间中就预置了一个标记。方法 2 会检查任务栈的最后 20个字节，查看预置在这里的标记数据是否被覆盖。如果最后 20 个字节的标记数据与预设值不同，则栈溢出钩子函数就会被调用。\n方法 2 没有方法 1 的执行速度快，但测试仅仅 20 个字节相对来说也是很快的。这种方法应该可以侦测到任何时候发生的栈溢出，虽然理论上还是有可能漏掉一些情况，但这些情况几乎是不可能发生的。\n其他错误： 问题现象：在一个 Demo 应用程序中增加了一个简单的任务，导致应用程序崩溃：\n​ 任务创建时需要在内存堆中分配空间。许多 Demo 应用程序定义的堆空间大小只够用于创建 Demo 任务——所以当任务创建完成后，就没有足够的剩余空间来增加其它的任务，队列或信号量。空闲任务是在 vTaskStartScheduler()调用中自动创建的。如果由于内存不足而无法创建空闲任务， vTaskStartScheduler()会直接返回。在调用 vTaskStartScheduler()后加上一条空循环[for(;;)]可以使这种错误更加容易调试。如果要添加更多的任务，可以增加内存堆空间大小，或是删掉一些已存在的 Demo任务。\n问题现象：在中断中调用一个 API 函数，导致应用程序崩溃:\n除了具有后缀为”FromISR”函数名的 API 函数，千万不要在中断服务例程中调用其它 API 函数。\n问题现象：有时候应用程序会在中断服务例程中崩溃：\n​ 需要做的第一件事是检查中断是否导致了栈溢出。在不同的移植平台和不同的编译器上，中断的定义和使用方法是不尽相同的——所 以，需要做的第二件事是检查在中断服务例程中使用的语法，宏和调用约定是否符合Demo 程序的文档描述，以及是否和 Demp 程序中提供的中断服务例程范例相同。\n​ 如果应用程序工作在 Cotex M3 上，需要确定给中断指派优先级时，使用低优先级号数值表示逻辑上的高优先级中断，因为这种方式不太直观，所以很容易被忘记。\n​ 一个比较常见的错误就是，在优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断中调用了 FreeRTOS API 函数。\n问题现象：在启动第一个任务时，调度器就崩溃了：\n​ 如果使用的是 ARM7，那么请确定调用 vTaskStartScheduler()时处理器处于管理模式(Supervisor mode)。最简单的方式就是在 main() 之前的 C 启动态码中将处理器设置为管理模式。 ARM7 的 Demo 应用程序就是这么做的。如果处理器不在管理模式下，调度器是无法启动的。\n问题现象：临界区无法正确嵌套：\n​ 除了 taskENTER_CRITICA()和 taskEXIT_CRITICAL()，千万不要在其它地方修改控制器的中断使能位或优先级标志。这两个宏维护了一个嵌套深度计数，所以只有当所有的嵌套调用都退出后计数值才会为 0，也才会使能中断。\n问题现象：在调度器启动前应用程序就崩溃了：\n​ 如果一个中断会产生上下文切换，则这个中断不能在调度器启动之前使能。这同样适用于那些需要读写队列或信号量的中断。在调度器启动之前，不能进行上下文切换。还有一些 API 函数不能在调度器启动之前调用。在调用 vTaskStartScheduler()之前，最好是限定只使用创建任务，队列和信号量的 API 函数。\n问题现象：在调度器挂起时调用 API 函数，导致应用程序崩溃；\n​ 调用 vTaskSuspendAll()使得调度器挂起，而唤醒调度器调用 xTaskResumeAll()。 千万不要在调度器挂起时调用其它 API 函数。\n问题现象：函数原型 pxPortInitialiseStack()导致编译失败；\n​ 每种移植都需要定义一个对应的宏，以把正确的内核头文件加入到工程中。如果编译函数原型 pxPortInitialiseStack()时出错，这种现象基本上可以确定是因为没有正确定义相应的宏。请参见附录 4 以获得更多信息。可以基本相应平台的 Demo 工程建立新的应用程序。这种方式就不用担心没有包含正确的文件，也不必担心没有正确地配置编译器选项。\n","permalink":"https://fan-pengfei.top/posts/freertos%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/","summary":"\u003cblockquote\u003e\n\u003cp\u003eFreeRTOS学习记录；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"freertos学习记录\"\u003eFreeRTOS学习记录\u003c/h1\u003e\n\u003ch3 id=\"一任务管理\"\u003e一、任务管理\u003c/h3\u003e\n\u003ch4 id=\"任务函数\"\u003e任务函数：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eATaskFunction\u003c/span\u003e( \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003epvParameters );\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e每一个任务函数都有自己的栈空间、自动变量；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e函数里通常是一个死循环；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e一般函数有可能跳出死循环，则必须删除函数：\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eATaskFunction\u003c/span\u003e( \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003epvParameters )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e/* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的iVarialbleExample变\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\t量。但如果iVariableExample被定义为static，这一点则不成立 – 这种情况下只存在一个变量，所有的任务实\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\t例将会共享这个变量。 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e iVariableExample \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e/* 任务通常实现在一个死循环中。 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e( ;; )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e/* 完成任务功能的代码将放在这里。 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e/* 如果任务的具体实现会跳出上面的死循环，则此任务必须在函数运行完之前删除。传入NULL参数表示删除\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\t的是当前任务 */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003evTaskDelete\u003c/span\u003e( NULL );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"任务创建函数\"\u003e任务创建函数：\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eportBASE_TYPE \u003cspan style=\"color:#a6e22e\"\u003exTaskCreate\u003c/span\u003e( pdTASK_CODE pvTaskCode,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003esigned\u003c/span\u003e portCHAR \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e pcName,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e portSHORT usStackDepth,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003epvParameters,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e portBASE_TYPE uxPriority,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            xTaskHandle \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003epxCreatedTask );\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003epvTaskCode\n任务只是永不退出的 C 函数，实现常通常是一个死循环。参数 pvTaskCode 是一个指向任务的实现函数的指针(效果上仅仅是函数 名)。\u003c/p\u003e","title":"FreeRTOS基础知识"},{"content":" union、struct所占的空间大小；\nunion: ​ 当多个数据需要共享内存或者多个数据每次只取其一时，可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的：\n联合体是一个结构； 它的所有成员相对于基地址的偏移量都为0； 此结构空间要大到足够容纳最”宽”的成员； 其对齐方式要适合其中所有的成员；\n​ 下面解释这四条描述：\n由于联合体中的所有成员是共享一段内存的，因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0，即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存，因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。\n例如：\n#include using namespace std; union A{ int a[5]; char b; double c; }; int main(){ cout **a** **b** **b** **b** **b** **c** **c** ```c++ //例如： struct B { char a; short b; int c; } //则sizeof(A)大小应该是4+4=8字节 a c c\nb b b b\n//例如： struct B { char a; char b[2]; char c[4]; } //则sizeof(A)大小应该是1+2+4=7字节 a b b c c c c\n结构体在内存中的存放也是按照单元存放的，所以其开辟的单元的最大长度取决于占字节数最大的数据类型，而且存储顺序对于空间利用率有一些影响。\n总结： 混合结构体的计算：\n#include typedef union{ long i; int k[5]; char c; }UDATE; struct data{ int cat; UDATE cow; double dog; }too; UDATE temp; int main() { printf(\u0026#34;%d,%d\u0026#34;,sizeof(temp),sizeof(too)); } 结果是：24，40；\n前一个结果，联合体，最大单元为8，8对齐，4*5=20，最接近20的8的倍数为24；\n后一个结果，8+24+8=40；\ncat占用4个，cow占用24个，cow对齐方式为8字节，所以cat后边要填充4个字节，即cat和cow共占用8+24=32个，再加上dog占的8个字节，所以一共是40个字节。\n__packed ​ 像union和struct这样的数据结构，总有一些字节是浪费掉的，这样做的目的很简单，就是因为在大多数计算机体系结构中，对内存操作时按整字存取才能达到最高效率，相当于是以空间换取时间。\n​ 当然在某些计算机体系结构中，比如ARM，是支持非对齐字传输的，也就是说变量并不一定要按照字长对齐，尽管这样可能会降低效率，但换来的是存储空间上的节约。在mdk中加上__packed关键字，可以得到非对齐字的紧凑型结构体，则会强制编译器将结构体成员按1字节对齐，则以下结构体占用空间仍为15字节。\ntypedef __packed struct { char a; //1byte int b; //4byte char c[2] //2byte double d; //8byte }Test; 如果编译器不支持__packed关键字，将其定义为空宏即可 #define __packed.\n#pragma pack (1) /*指定按1字节对齐*/ #pragma pack () /*取消指定对齐，恢复缺省对齐*/ 位域： C语言标准规定，位域的宽度不能超过它所依附的数据类型的长度。通俗地讲，成员变量都是有类型的，这个类型限制了成员变量的最大长度，:后面的数字不能超过这个长度。\nC语言标准还规定，只有有限的几种数据类型可以用于位域。在 ANSI C 中，这几种数据类型是 int、signed int 和 unsigned int（int 默认就是 signed int）；到了 C99，_Bool 也被支持了。\n但编译器在具体实现时都进行了扩展，额外支持了 char、signed char、unsigned char 以及 enum 类型，所以上面的代码虽然不符合C语言标准，但它依然能够被编译器支持。\n位域的内存分配： 位域的具体存储规则如下：\n1、当相邻成员的类型相同时，如果它们的位宽之和小于类型的 sizeof 大小，那么后面的成员紧邻前一个成员存储，直到不能容纳为止；如果它们的位宽之和大于类型的 sizeof 大小，那么后面的成员将从新的存储单元开始，其偏移量为类型大小的整数倍。\n#include int main(){ struct bs{ unsigned m: 6; unsigned n: 12; unsigned p: 4; }; printf(\u0026#34;%d\\n\u0026#34;, sizeof(struct bs)); return 0; } 运行结果：4\nm、n、p 的类型都是 unsigned int，sizeof 的结果为 4 个字节（Byte），也即 32 个位（Bit）。m、n、p 的位宽之和为 6+12+4 = 22，小于 32，所以它们会挨着存储，中间没有缝隙。\nsizeof(struct bs) 的大小之所以为 4，而不是 3，是因为要将内存对齐到 4 个字节，以便提高存取效率；\n如果将成员 m 的位宽改为 22，那么输出结果将会是 8，因为 22+12 = 34，大于 32，n 会从新的位置开始存储，相对 m 的偏移量是 sizeof(unsigned int)，也即 4 个字节。\n如果再将成员 p 的位宽也改为 22，那么输出结果将会是 12，三个成员都不会挨着存储。\n2、当相邻成员的类型不同时，不同的编译器有不同的实现方案，GCC会压缩存储，而 VC/VS 不会。\n#include int main(){ struct bs{ unsigned m: 12; unsigned char ch: 4; unsigned p: 4; }; printf(\u0026#34;%d\\n\u0026#34;, sizeof(struct bs)); return 0; } 在 GCC 下的运行结果为 4，三个成员挨着存储；在 VC/VS 下的运行结果为 12，三个成员按照各自的类型存储（与不指定位宽时的存储方式相同）。\n3、如果成员之间穿插着非位域成员，那么不会进行压缩。例如以下：\nstruct bs{ unsigned m: 12; unsigned ch; unsigned p: 4; }; 在各个编译器下 sizeof 的结果都是 12。\n4、位域成员可以没有名称，只给出数据类型和位宽，如下所示：\nstruct bs{ int m: 12; int : 20; //该位域成员不能使用 int n: 4; }; 无名位域一般用来作填充或者调整成员位置。\n","permalink":"https://fan-pengfei.top/posts/union%E5%92%8Cstruct%E5%AD%98%E6%94%BE%E6%A0%BC%E5%BC%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003eunion、struct所占的空间大小；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"union\"\u003eunion:\u003c/h2\u003e\n\u003cp\u003e​        \u003cstrong\u003e当多个数据需要共享内存或者多个数据每次只取其一时，可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的：\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e联合体是一个结构；\u003c/strong\u003e\n\u003cstrong\u003e它的所有成员相对于基地址的偏移量都为0；\u003c/strong\u003e\n\u003cstrong\u003e此结构空间要大到足够容纳最”宽”的成员；\u003c/strong\u003e\n\u003cstrong\u003e其对齐方式要适合其中所有的成员；\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e​       下面解释这四条描述：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e由于联合体中的所有成员是共享一段内存的，因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0，即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存，因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eusing\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enamespace\u003c/span\u003e std;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eunion\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eA\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e a[\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e b;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e c;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(){\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   cout\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ea\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e```\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//例如：\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eB\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e a;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eshort\u003c/span\u003e b;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e c;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//则sizeof(A)大小应该是4+4=8字节\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003ea\u003c/strong\u003e\n\u003cstrong\u003ec\u003c/strong\u003e\n\u003cstrong\u003ec\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eb\u003c/strong\u003e\n\u003cstrong\u003eb\u003c/strong\u003e\n\u003cstrong\u003eb\u003c/strong\u003e\n\u003cstrong\u003eb\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//例如：\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eB\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e a;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e b[\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003echar\u003c/span\u003e c[\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//则sizeof(A)大小应该是1+2+4=7字节\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ea\nb\nb\nc\nc\nc\nc\u003c/p\u003e","title":"union和struct存放格式"},{"content":" 图片PNG文件转JPG文件；\n使用方法：将该脚本放置于png文件所在的文件夹中，然后运行该脚本，即可将png文件转化为jpg文件，并放置于JPG文件夹下；\nimport os from PIL import Image dirname_read = \u0026#34;./\u0026#34; # 源文件文件夹 dirname_write = \u0026#34;./JPG/\u0026#34; # 目标文件文件夹 names = os.listdir(dirname_read) count = 0 for name in names: portion = os.path.splitext(name) # 分离文件名和扩展名 if portion[1] == \u0026#34;.png\u0026#34;: # 判断扩展名是否为png img = Image.open(dirname_read+name) # 打开该文件 name = portion[0] + \u0026#34;.jpg\u0026#34; # 重命名文件 to_save_path = dirname_write + name # 设置保存路径 img = img.convert(\u0026#39;RGB\u0026#39;) # RGBA意思是红色，绿色，蓝色,Alpha指透明度。而JPG不支持透明度，所以要么丢弃Alpha,要么保存为.png文件 img.save(to_save_path, quality=95) # 保存 count += 1 # 计数加一 print(to_save_path, \u0026#34;------conut:\u0026#34;, count) # 输出信息 else: continue print(\u0026#34;Count_Sum:\u0026#34;, count) # 输出总数 ","permalink":"https://fan-pengfei.top/posts/%E5%B8%B8%E7%94%A8%E8%84%9A%E6%9C%AC%E6%B1%87%E6%80%BB/","summary":"\u003cblockquote\u003e\n\u003cp\u003e图片PNG文件转JPG文件；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e使用方法：将该脚本放置于png文件所在的文件夹中，然后运行该脚本，即可将png文件转化为jpg文件，并放置于JPG文件夹下；\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e os\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e PIL \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Image\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edirname_read \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;./\u0026#34;\u003c/span\u003e  \t\t\t\t\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 源文件文件夹\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edirname_write \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;./JPG/\u0026#34;\u003c/span\u003e  \t\t\t\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 目标文件文件夹\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enames \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elistdir(dirname_read)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecount \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e name \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e names:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    portion \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplitext(name)  \t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 分离文件名和扩展名\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e portion[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.png\u0026#34;\u003c/span\u003e:          \t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 判断扩展名是否为png\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        img \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Image\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eopen(dirname_read\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003ename) \t\t\u003cspan style=\"color:#75715e\"\u003e# 打开该文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        name \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e portion[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.jpg\u0026#34;\u003c/span\u003e \t\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 重命名文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        to_save_path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e dirname_write \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e name \t\t\u003cspan style=\"color:#75715e\"\u003e# 设置保存路径\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        img \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e img\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003econvert(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;RGB\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# RGBA意思是红色，绿色，蓝色,Alpha指透明度。而JPG不支持透明度，所以要么丢弃Alpha,要么保存为.png文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        img\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esave(to_save_path, quality\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e95\u003c/span\u003e)  \t\t\u003cspan style=\"color:#75715e\"\u003e# 保存\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        count \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e  \t\t\t\t\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 计数加一\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        print(to_save_path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;------conut:\u0026#34;\u003c/span\u003e, count)  \u003cspan style=\"color:#75715e\"\u003e# 输出信息\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Count_Sum:\u0026#34;\u003c/span\u003e, count)  \t\t\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 输出总数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"常用脚本汇总"},{"content":" PID控制中常出现的积分饱和概念及其解决方案；\n积分饱和的概念： 这种现象往往发生在误差有大幅变化（例如大幅增加），积分器因为误差的大幅增加有很大的累计量，因为积分器的输出满足下式：\n离散化形式表示为：\n所以随着时间的增加，每次累积较大的误差，很容易造成积分饱和并产生较大的过冲，而且当误差变为负时，其过冲仍维持一段时间之后才恢复正常的情形。\n通常会产生的输出如下图所示：\n从图中我们不难发现，这里有三个过程：\n因为这个过程存在 较大幅度变化的误差，因此积分器累积了较大的值，从图中可以看到，积分器的面积比较大（阴影部分）；\n此时积分已经饱和，产生了较大的过冲，并且在较长的一段时间内，一直处于过冲的状态；\n积分脱离饱和状态，产生了积极的调节作用，消除静差，系统输出达到设定值；\n如何防止积分饱和： 为了防止PID控制器出现积分饱和，需要在算法加入抗积分饱和（anti-integral windup）的算法；通常有以下几种措施；\n积分分离或者称为去积分算法；\n在饱和的时候将积分器的累计值初始化到一个比较理想的值；\n若积分饱和因为目标值突然变化而产生，将目标值以适当斜率的斜坡变化可避免此情形；\n将积分累计量限制上下限，避免积分累计量超过限制值；\n如果 PID输出已经饱和，则重新计算积分累计量，使输出恰好为合理的范围；\n参考： 1、一文详细解析到底什么是积分饱和；\n","permalink":"https://fan-pengfei.top/posts/pid%E4%B8%AD%E7%A7%AF%E5%88%86%E9%A5%B1%E5%92%8C%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003ePID控制中常出现的积分饱和概念及其解决方案；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"积分饱和的概念\"\u003e积分饱和的概念：\u003c/h2\u003e\n\u003cp\u003e这种现象往往发生在误差有大幅变化（例如大幅增加），积分器因为误差的大幅增加有很大的累计量，因为积分器的输出满足下式：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"pIYBAGBYOGKAMGJyAAAKa3yzFcw010.png\" loading=\"lazy\" src=\"/posts/pid%E4%B8%AD%E7%A7%AF%E5%88%86%E9%A5%B1%E5%92%8C%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e离散化形式表示为：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"pIYBAGBYOJqATzZgAAALkxL3jOw485.png\" loading=\"lazy\" src=\"/posts/pid%E4%B8%AD%E7%A7%AF%E5%88%86%E9%A5%B1%E5%92%8C%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e所以随着时间的增加，每次累积较大的误差，很容易造成积分饱和并产生较大的过冲，而且当误差变为负时，其过冲仍维持一段时间之后才恢复正常的情形。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e通常会产生的输出如下图所示：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"o4YBAGBYOR-APSCrAAIJXwZqBuo640.png\" loading=\"lazy\" src=\"/posts/pid%E4%B8%AD%E7%A7%AF%E5%88%86%E9%A5%B1%E5%92%8C%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e从图中我们不难发现，这里有三个过程：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e因为这个过程存在 较大幅度变化的误差，因此积分器累积了较大的值，从图中可以看到，积分器的面积比较大（阴影部分）；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e此时积分已经饱和，产生了较大的过冲，并且在较长的一段时间内，一直处于过冲的状态；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e积分脱离饱和状态，产生了积极的调节作用，消除静差，系统输出达到设定值；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"如何防止积分饱和\"\u003e如何防止积分饱和：\u003c/h2\u003e\n\u003cp\u003e为了防止PID控制器出现积分饱和，需要在算法加入抗积分饱和（\u003ccode\u003eanti-integral windup\u003c/code\u003e）的算法；通常有以下几种措施；\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e积分分离或者称为去积分算法；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e在饱和的时候将积分器的累计值初始化到一个比较理想的值；\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e若积分饱和因为目标值突然变化而产生，将目标值以适当斜率的斜坡变化可避免此情形；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e将积分累计量限制上下限，避免积分累计量超过限制值；\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果 PID输出已经饱和，则重新计算积分累计量，使输出恰好为合理的范围；\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"参考\"\u003e参考：\u003c/h2\u003e\n\u003cp\u003e1、\u003ca href=\"https://www.elecfans.com/d/1544295.html\"\u003e一文详细解析到底什么是积分饱和\u003c/a\u003e；\u003c/p\u003e","title":"PID中积分饱和及其解决办法"},{"content":" Jlink RTT调试技巧；\n使用Jlink的 RTT功能 : 这个功能是不需要另外接其他引脚的，如果使用SW连接方式，仅仅两根线就可以。\nRTT 是Jlink的一种实时终端的方式连接输出调试信息，网上有很多说明之间按照做就可以，我仅仅是记录一下自己的步骤.\n就是下载RTT软件包，下载RTT文件： http://download.segger.com/J-Link/RTT/RTT_Implementation_140925.zip ；\n添加RTT文件到自己的工程： 添加必要的头文件：\n输出函数打印：\n这个时候RTT在程序中就添加成功了，我们可以使用使用Jlink带的工具进行查看数据；\n如打开RTT Viewer 提升连接，点击OK 不出意外的话，你就可以看到调试信息了；\n","permalink":"https://fan-pengfei.top/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/","summary":"\u003cblockquote\u003e\n\u003cp\u003eJlink RTT调试技巧；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"使用jlink的-rtt功能-\"\u003e使用Jlink的 RTT功能 :\u003c/h2\u003e\n\u003cp\u003e这个功能是不需要另外接其他引脚的，如果使用SW连接方式，仅仅两根线就可以。\u003c/p\u003e\n\u003cp\u003eRTT 是Jlink的一种实时终端的方式连接输出调试信息，网上有很多说明之间按照做就可以，我仅仅是记录一下自己的步骤.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e就是下载RTT软件包，下载RTT文件： \u003ca href=\"http://download.segger.com/J-Link/RTT/RTT_Implementation_140925.zip\"\u003ehttp://download.segger.com/J-Link/RTT/RTT_Implementation_140925.zip\u003c/a\u003e  ；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/img-1.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e添加RTT文件到自己的工程：\n添加必要的头文件：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/img-2.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e输出函数打印：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这个时候RTT在程序中就添加成功了，我们可以使用使用Jlink带的工具进行查看数据；\u003c/p\u003e\n\u003cp\u003e如打开RTT Viewer 提升连接，点击OK 不出意外的话，你就可以看到调试信息了；\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/jlink%E4%BD%BF%E7%94%A8rtt%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E4%BB%A3%E6%9B%BF%E4%B8%B2%E5%8F%A3%E6%89%93%E5%8D%B0/img-5.png\"\u003e\u003c/p\u003e","title":"Jlink使用RTT输出调试信息(代替串口打印)"},{"content":" Markdown绘制简单流程图；\nTypora流程图： mermaid效果（这个可以在Typora编辑器中显示，博客中渲染失败） 输入```，然后输入mermaid即可 ·mermaid graph TD B((开始)) --\u0026gt;C{判断} C -- a=1 --\u0026gt;D[执行语句1] C -- a=2 --\u0026gt;E[执行语句2] C -- a=3 --\u0026gt;F[执行语句3] C -- a=4 --\u0026gt;G[执行语句4] D--\u0026gt; AA((结束)) E--\u0026gt; AA F--\u0026gt; AA G--\u0026gt; AA 如下图：\n语法： 官网说明文档 Mermaid .\nflowchar效果（只有这个可以在博客中显示流程图） ···flow st=\u0026gt;start: 开始节点 in=\u0026gt;inputoutput: 输入 e=\u0026gt;end: 结束节点 op=\u0026gt;operation: 操作节点 cond=\u0026gt;condition: 条件节点 sub=\u0026gt;subroutine: 子例程 out=\u0026gt;inputoutput: 输出 st(right)-\u0026gt;in-\u0026gt;op-\u0026gt;cond cond(yes,right)-\u0026gt;out-\u0026gt;e cond(no)-\u0026gt;sub 实际例子1：\n···flow st=\u0026gt;start: Start i=\u0026gt;inputoutput: 输入年份n cond1=\u0026gt;condition: n能否被4整除？ cond2=\u0026gt;condition: n能否被100整除？ cond3=\u0026gt;condition: n能否被400整除？ o1=\u0026gt;inputoutput: 输出非闰年 o2=\u0026gt;inputoutput: 输出非闰年 o3=\u0026gt;inputoutput: 输出闰年 o4=\u0026gt;inputoutput: 输出闰年 e=\u0026gt;end st-\u0026gt;i-\u0026gt;cond1 cond1(no)-\u0026gt;o1-\u0026gt;e cond1(yes)-\u0026gt;cond2 cond2(no)-\u0026gt;o3-\u0026gt;e cond2(yes)-\u0026gt;cond3 cond3(yes)-\u0026gt;o2-\u0026gt;e cond3(no)-\u0026gt;o4-\u0026gt;e 实际例子2：\n···flow st=\u0026gt;start: start:\u0026gt;http://www.baidu.com op1=\u0026gt;operation: 操作1 cond1=\u0026gt;condition: YES or NO? sub=\u0026gt;subroutine: 子程序 e=\u0026gt;end st-\u0026gt;op1-\u0026gt;cond1 cond1(yes)-\u0026gt;e cond1(no)-\u0026gt;sub(right)-\u0026gt;op1 综合例子：\n···flow st=\u0026gt;start: Improve your l10n process! e=\u0026gt;end: Continue to have fun!:\u0026gt;https://youtu.be/YQryHo1iHb8[blank] op1=\u0026gt;operation: Go to locize.com:\u0026gt;https://locize.com[blank] sub1=\u0026gt;subroutine: Read the awesomeness cond(align-next=no)=\u0026gt;condition: Interested to getting started? io=\u0026gt;inputoutput: Register:\u0026gt;https://www.locize.io/register[blank] sub2=\u0026gt;subroutine: Read about improving your localization workflow or another source:\u0026gt;https://medium.com/@adrai/8-signs-you-should-improve-your-localization-process-3dc075d53998[blank] op2=\u0026gt;operation: Login:\u0026gt;https://www.locize.io/login[blank] cond2=\u0026gt;condition: valid password? cond3=\u0026gt;condition: reset password? op3=\u0026gt;operation: send email sub3=\u0026gt;subroutine: Create a demo project sub4=\u0026gt;subroutine: Start your real project io2=\u0026gt;inputoutput: Subscribe st-\u0026gt;op1-\u0026gt;sub1-\u0026gt;cond cond(yes)-\u0026gt;io-\u0026gt;op2-\u0026gt;cond2 cond2(no)-\u0026gt;cond3 cond3(no,bottom)-\u0026gt;op2 cond3(yes)-\u0026gt;op3 op3(right)-\u0026gt;op2 cond2(yes)-\u0026gt;sub3 sub3-\u0026gt;sub4-\u0026gt;io2-\u0026gt;e cond(no)-\u0026gt;sub2(right)-\u0026gt;op1 st@\u0026gt;op1({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;sub1({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;cond({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;io({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;op2({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;cond2({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;sub3({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;sub4({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;io2({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;})@\u0026gt;e({\u0026#34;stroke\u0026#34;:\u0026#34;Red\u0026#34;,\u0026#34;stroke-width\u0026#34;:6,\u0026#34;arrow-end\u0026#34;:\u0026#34;classic-wide-long\u0026#34;}) 语法： 官网说明文档 FlowChar .\nst=\u0026gt;start: Start i=\u0026gt;inputoutput: 输入年份n cond1=\u0026gt;condition: n能否被4整除？ cond2=\u0026gt;condition: n能否被100整除？ cond3=\u0026gt;condition: n能否被400整除？ o1=\u0026gt;inputoutput: 输出非闰年 o2=\u0026gt;inputoutput: 输出非闰年 o3=\u0026gt;inputoutput: 输出闰年 o4=\u0026gt;inputoutput: 输出闰年 e=\u0026gt;end st-\u0026gt;i-\u0026gt;cond1 cond1(no)-\u0026gt;o1-\u0026gt;e cond1(yes)-\u0026gt;cond2 cond2(no)-\u0026gt;o3-\u0026gt;e cond2(yes)-\u0026gt;cond3 cond3(yes)-\u0026gt;o2-\u0026gt;e cond3(no)-\u0026gt;o4-\u0026gt;e{\u0026ldquo;scale\u0026rdquo;:1,\u0026ldquo;line-width\u0026rdquo;:2,\u0026ldquo;line-length\u0026rdquo;:50,\u0026ldquo;text-margin\u0026rdquo;:10,\u0026ldquo;font-size\u0026rdquo;:12} var code = document.getElementById(\u0026ldquo;flowchart-0-code\u0026rdquo;).value; var options = JSON.parse(decodeURIComponent(document.getElementById(\u0026ldquo;flowchart-0-options\u0026rdquo;).value)); var diagram = flowchart.parse(code); diagram.drawSVG(\u0026ldquo;flowchart-0\u0026rdquo;, options); st=\u0026gt;start: start:\u0026gt;http://www.baidu.com op1=\u0026gt;operation: 操作1 cond1=\u0026gt;condition: YES or NO? sub=\u0026gt;subroutine: 子程序 e=\u0026gt;end\nst-\u0026gt;op1-\u0026gt;cond1 cond1(yes)-\u0026gt;e cond1(no)-\u0026gt;sub(right)-\u0026gt;op1{\u0026ldquo;scale\u0026rdquo;:1,\u0026ldquo;line-width\u0026rdquo;:2,\u0026ldquo;line-length\u0026rdquo;:50,\u0026ldquo;text-margin\u0026rdquo;:10,\u0026ldquo;font-size\u0026rdquo;:12} var code = document.getElementById(\u0026ldquo;flowchart-1-code\u0026rdquo;).value; var options = JSON.parse(decodeURIComponent(document.getElementById(\u0026ldquo;flowchart-1-options\u0026rdquo;).value)); var diagram = flowchart.parse(code); diagram.drawSVG(\u0026ldquo;flowchart-1\u0026rdquo;, options);st=\u0026gt;start: Improve your l10n process! e=\u0026gt;end: Continue to have fun!:\u0026gt;https://youtu.be/YQryHo1iHb8[blank] op1=\u0026gt;operation: Go to locize.com:\u0026gt;https://locize.com[blank] sub1=\u0026gt;subroutine: Read the awesomeness cond(align-next=no)=\u0026gt;condition: Interested to getting started? io=\u0026gt;inputoutput: Register:\u0026gt;https://www.locize.io/register[blank] sub2=\u0026gt;subroutine: Read about improving your localization workflow or another source:\u0026gt;https://medium.com/@adrai/8-signs-you-should-improve-your-localization-process-3dc075d53998[blank] op2=\u0026gt;operation: Login:\u0026gt;https://www.locize.io/login[blank] cond2=\u0026gt;condition: valid password? cond3=\u0026gt;condition: reset password? op3=\u0026gt;operation: send email sub3=\u0026gt;subroutine: Create a demo project sub4=\u0026gt;subroutine: Start your real project io2=\u0026gt;inputoutput: Subscribe\nst-\u0026gt;op1-\u0026gt;sub1-\u0026gt;cond cond(yes)-\u0026gt;io-\u0026gt;op2-\u0026gt;cond2 cond2(no)-\u0026gt;cond3 cond3(no,bottom)-\u0026gt;op2 cond3(yes)-\u0026gt;op3 op3(right)-\u0026gt;op2 cond2(yes)-\u0026gt;sub3 sub3-\u0026gt;sub4-\u0026gt;io2-\u0026gt;e cond(no)-\u0026gt;sub2(right)-\u0026gt;op1\nst@\u0026gt;op1({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;sub1({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;cond({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;io({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;op2({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;cond2({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;sub3({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;sub4({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;io2({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;})@\u0026gt;e({\u0026ldquo;stroke\u0026rdquo;:\u0026ldquo;Red\u0026rdquo;,\u0026ldquo;stroke-width\u0026rdquo;:6,\u0026ldquo;arrow-end\u0026rdquo;:\u0026ldquo;classic-wide-long\u0026rdquo;}){\u0026ldquo;scale\u0026rdquo;:1,\u0026ldquo;line-width\u0026rdquo;:2,\u0026ldquo;line-length\u0026rdquo;:50,\u0026ldquo;text-margin\u0026rdquo;:10,\u0026ldquo;font-size\u0026rdquo;:12} var code = document.getElementById(\u0026ldquo;flowchart-2-code\u0026rdquo;).value; var options = JSON.parse(decodeURIComponent(document.getElementById(\u0026ldquo;flowchart-2-options\u0026rdquo;).value)); var diagram = flowchart.parse(code); diagram.drawSVG(\u0026ldquo;flowchart-2\u0026rdquo;, options);\n","permalink":"https://fan-pengfei.top/posts/markdown%E7%BB%98%E5%88%B6%E7%AE%80%E5%8D%95%E6%B5%81%E7%A8%8B%E5%9B%BE/","summary":"\u003cblockquote\u003e\n\u003cp\u003eMarkdown绘制简单流程图；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"typora流程图\"\u003eTypora流程图：\u003c/h1\u003e\n\u003ch2 id=\"mermaid效果这个可以在typora编辑器中显示博客中渲染失败\"\u003emermaid效果（这个可以在Typora编辑器中显示，博客中渲染失败）\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e输入```，然后输入mermaid即可\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e·mermaid\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egraph TD\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   B((开始)) --\u0026gt;C{判断}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   C --  a=1 --\u0026gt;D[执行语句1]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   C --  a=2  --\u0026gt;E[执行语句2]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   C --  a=3 --\u0026gt;F[执行语句3]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   C -- a=4  --\u0026gt;G[执行语句4]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   D--\u0026gt; AA((结束))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   E--\u0026gt; AA\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   F--\u0026gt; AA\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   G--\u0026gt; AA\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e如下图：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"a4e80fed35165401f57d392375ac726\" loading=\"lazy\" src=\"/posts/markdown%E7%BB%98%E5%88%B6%E7%AE%80%E5%8D%95%E6%B5%81%E7%A8%8B%E5%9B%BE/img-1.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"语法\"\u003e语法：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e官网说明文档 \u003ca href=\"https://mermaid-js.github.io/mermaid/#/\"\u003eMermaid\u003c/a\u003e .\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"flowchar效果只有这个可以在博客中显示流程图\"\u003eflowchar效果（只有这个可以在博客中显示流程图）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e···flow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003est=\u0026gt;start: 开始节点\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ein=\u0026gt;inputoutput: 输入\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ee=\u0026gt;end: 结束节点\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eop=\u0026gt;operation: 操作节点\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd=\u0026gt;condition: 条件节点\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esub=\u0026gt;subroutine: 子例程\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eout=\u0026gt;inputoutput: 输出\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003est(right)-\u0026gt;in-\u0026gt;op-\u0026gt;cond\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd(yes,right)-\u0026gt;out-\u0026gt;e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd(no)-\u0026gt;sub\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg alt=\"image-20220507220027459\" loading=\"lazy\" src=\"/posts/markdown%E7%BB%98%E5%88%B6%E7%AE%80%E5%8D%95%E6%B5%81%E7%A8%8B%E5%9B%BE/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e实际例子1：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plaintext\" data-lang=\"plaintext\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e···flow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003est=\u0026gt;start: Start\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ei=\u0026gt;inputoutput: 输入年份n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd1=\u0026gt;condition: n能否被4整除？\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd2=\u0026gt;condition: n能否被100整除？\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd3=\u0026gt;condition: n能否被400整除？\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eo1=\u0026gt;inputoutput: 输出非闰年\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eo2=\u0026gt;inputoutput: 输出非闰年\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eo3=\u0026gt;inputoutput: 输出闰年\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eo4=\u0026gt;inputoutput: 输出闰年\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ee=\u0026gt;end\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003est-\u0026gt;i-\u0026gt;cond1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd1(no)-\u0026gt;o1-\u0026gt;e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd1(yes)-\u0026gt;cond2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd2(no)-\u0026gt;o3-\u0026gt;e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd2(yes)-\u0026gt;cond3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd3(yes)-\u0026gt;o2-\u0026gt;e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econd3(no)-\u0026gt;o4-\u0026gt;e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e实际例子2：\u003c/strong\u003e\u003c/p\u003e","title":"markdown绘制简单流程图"},{"content":" LCD 显示太空人动画；\n这个比较简单，就是把连续的画面播放起来就行；\n","permalink":"https://fan-pengfei.top/posts/lcd%E6%98%BE%E7%A4%BA%E5%A4%AA%E7%A9%BA%E4%BA%BA%E7%94%BB%E9%9D%A2/","summary":"\u003cblockquote\u003e\n\u003cp\u003eLCD 显示太空人动画；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e这个比较简单，就是把连续的画面播放起来就行；\u003c/p\u003e","title":"LCD显示太空人画面"},{"content":" 自制Jlink-ob(带串口)；\n","permalink":"https://fan-pengfei.top/posts/%E8%87%AA%E5%88%B6jlink-ob%E5%B8%A6%E4%B8%B2%E5%8F%A3/","summary":"\u003cblockquote\u003e\n\u003cp\u003e自制Jlink-ob(带串口)；\u003c/p\u003e\n\u003c/blockquote\u003e","title":"自制Jlink-OB(带串口)"},{"content":" 之前经常用CRC校验，今天解释一下其原理；\n简单介绍： Cyclic Redundancy Check循环冗余检验，是基于数据计算一组效验码，用于核对数据传输过程中是否被更改或传输错误。\n循环冗余校验检错方案（CRC）：\nCRC校验原理看起来比较复杂，好难懂，因为大多数书上基本上是以二进制的多项式形式来说明的。其实很简单的问题，其根本思想就是先在要发送的帧后面附加一个数（这个就是用来校验的校验码，但要注意，这里的数也是二进制序列的，下同），生成一个新帧发送给接收端。当然，这个附加的数不是随意的，它 要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除（注意，这里不是直接采用二进制除法，而是采用一种称之为“模2除法”）。到达接收端后，再把接收到的新帧除以（同样采用“模2除法”）这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数，做了“去余”处理（也就已经能整除了），所以结果应该是没有余数。如果有余数，则表明该帧在传输过程中出现了差错。\n【说明】：“模2除法”与“算术除法”类似，但它既不向上位借位，也不比较除数和被除数的相同位数值的大小，只要以相同位数进行相除即可。模2加法运算为：1+1=0，0+1=1，0+0=0，无进位，也无借位；模2减法6运算为：1-1=0，0-1=1，1-0=1，0-0=0，也无进位，无借位。相当于二进制中的逻辑异或运算。也就是比较后，两者对应位相同则结果为“0”，不同则结果为“1”。如100101除以1110，结果得到商为11，余数为1，如图5-9左图所示。如11×11=101，如右图所示。\n“模2除法”和“模2乘法”示例\n具体来说，CRC校验原理就是以下几个步骤：\n先选择（可以随机选择，也可按标准选择，具体在后面介绍）一个用于在接收端进行校验时，对接收的帧进行除法运算的除数（是二进制比较特串，通常是以多项方式表示，所以CRC又称多项式编码方法，这个多项式也称之为“生成多项式”）。\n看所选定的除数二进制位数（假设为k位），然后在要发送的数据帧（假设为m位）后面加上k-1位“0”，然后以这个加了k-1个“0“的新帧（一共是m+k-1位）以“模2除法”方式除以上面这个除数，所得到的余数（也是二进制的比特串）就是该帧的CRC校验码，也称之为FCS（帧校验序列）。但要注意的是，余数的位数一定要是比除数位数只能少一位，哪怕前面位是0，甚至是全为0（附带好整除时）也都不能省略。\n再把这个校验码附加在原数据帧（就是m位的帧，注意不是在后面形成的m+k-1位的帧）后面，构建一个新帧发送到接收端；最后在接收端再把这个新帧以“模2除法”方式除以前面选择的除数，如果没有余数，则表明该帧在传输过程中没出错，否则出现了差错。\n通过以上介绍，大家一定可以理解CRC校验的原理，并且不再认为很复杂吧。 从上面可以看出，CRC校验中有两个关键点：一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串（或多项式）；二是把原始帧与上面选定的除进行二进制除法运算，计算出FCS。前者可以随机选择，也可按国际上通行的标准选择，但最高位和最低位必须均为“1”，如在IBM的SDLC（同步数据链路控制）规程中使用的CRC-16（也就是这个除数一共是17位）生成多项式g（x）= x^16 + x^15 + x^2 +1（对应二进制比特串为：11000000000000101）；而在ISO HDLC（高级数据链路控制）规程、ITU的SDLC、X.25、V.34、V.41、V.42等中使用CCITT-16生成多项式g（x）= x^16+ x^15 + x^5 +1（对应二进制比特串为：11000000000100001）。\nCRC校验码的计算示例\n由以上分析可知，既然除数是随机，或者按标准选定的，所以CRC校验的关键是如何求出余数，也就是校验码（CRC校验码）。下面以一个例子来具体说明整个过程。现假设选择的CRC生成多项式为G（X） = X^4 + X^3 + 1，要求出二进制序列10110011的CRC校验码。下面是具体的计算过程：\n首先把生成多项式转换成二进制数，由G（X） = X^4+ X^3 + 1可以知道，它一共是5位（总位数等于最高位的幂次加1，即4+1=5），然后根据多项式各项的含义（多项式只列出二进制值为1的位，也就是这个二进制的第4位、第3位、第0位的二进制均为1，其它位均为0）很快就可得到它的二进制比特串为11001。\n因为生成多项式的位数为5，根据前面的介绍，得知CRC校验码的位数为4（校验码的位数比生成多项式的位数少1）。因为原数据帧10110011，在它后面再加4个0，得到101100110000，然后把这个数以“模2除法”方式除以生成多项式，得到的余数（即CRC码）为0100，如图所示。注意参考前面介绍的“模2除法”运算法则。 CRC校验码计算示例\n把上步计算得到的CRC校验0100替换原始帧101100110000后面的四个“0”，得到新帧101100110100。再把这个新帧发送到接收端。\n当以上新帧到达接收端后，接收端会把这个新帧再用上面选定的除数11001以“模2除法”方式去除，验证余数是否为0，如果为0，则证明该帧数据在传输过程中没有出现差错，否则出现了差错。\n通过以上CRC校验原理的剖析和CRC校验码的计算示例的介绍，大家应该对这种看似很复杂的CRC校验原理和计算方法应该比较清楚了。\n算法原理： 假设数据传输过程中需要发送15位的二进制信息g=101001110100001，这串二进制码可表示为代数多项式g(x) = x^14 + x^12 + x^9 + x^8 + x^7 + x^5+ 1，其中g中第k位的值，对应g(x)中x^k 的系数。将g(x)乘以x^m，既将g后加m个0，然后除以m阶多项式h(x)，得到的(m-1)阶余项r(x)对应的二进制码r就是CRC编码。\nh(x)可以自由选择或者使用国际通行标准，一般按照h(x)的阶数m，将CRC算法称为CRC-m，比如CRC-32、CRC-64等。国际通行标准可以参看标准；\ng(x)和h(x)的除运算，可以通过g和h做xor（异或）运算。比如将11001与10101做xor运算：\n明白了xor运算法则后，举一个例子使用CRC-8算法求101001110100001的效验码。CRC-8标准的h(x) = x^8 + x^7 + x^6 + x^4 + x^2 + 1，既h是9位的二进制串111010101。\n经过迭代运算后，最终得到的r是10001100，这就是CRC效验码。\n通过示例，可以发现一些规律，依据这些规律调整算法：\n每次迭代，根据gk的首位决定b，b是与gk进行运算的二进制码。若gk的首位是1，则b=h；若gk的首位是0，则b=0，或者跳过此次迭代，上面的例子中就是碰到0后直接跳到后面的非零位。 ​ 每次迭代，gk的首位将会被移出，所以只需考虑第2位后计算即可。这样就可以舍弃h的首位，将b取h的后m位。比如CRC-8的h是111010101，b只需是11010101。 每次迭代，受到影响的是gk的前m位，所以构建一个m位的寄存器S，此寄存器储存gk的前m位。每次迭代计算前先将S的首位抛弃，将寄存器左移一位，同时将g的后一位加入寄存器。若使用此种方法，计算步骤如下： 蓝色表示寄存器S的首位，是需要移出的，b根据S的首位选择0或者h。黄色是需要移入寄存器的位。S’是经过位移后的S。\n查表法： 同样是上面的那个例子，将数据按每4位组成1个block，这样g就被分成6个block。\n下面的表展示了4次迭代计算步骤，灰色背景的位是保存在寄存器中的。\n经4次迭代，B1被移出寄存器。被移出的部分，不是我们关心的，我们关心的是这4次迭代对B2和B3产生了什么影响。注意表中红色的部分，先作如下定义：\nB23 = 00111010 b1 = 00000000 b2 = 01010100 b3 = 10101010 b4 = 11010101 b’ = b1 xor b2 xor b3 xor b4\n4次迭代对B2和B3来说,实际上就是让它们与b1,b2,b3,b4做了xor计算，既：\nB23 xor b1 xor b2 xor b3 xor b4\n可以证明xor运算满足交换律和结合律，于是：\nB23 xor b1 xor b2 xor b3 xor b4 = B23 xor (b1 xor b2 xor b3 xor b4) = B23 xor b’\nb1是由B1的第1位决定的，b2是由B1迭代1次后的第2位决定（既是由B1的第1和第2位决定），同理，b3和b4都是由B1决定。通过B1就可以计算出b’。另外，B1由4位组成，其一共2^4有种可能值。于是我们就可以想到一种更快捷的算法，事先将b’所有可能的值，16个值可以看成一个表；这样就可以不必进行那4次迭代，而是用B1查表得到b’值，将B1移出，B3移入，与b’计算，然后是下一次迭代。\n可看到每次迭代，寄存器中的数据以4位为单位移入和移出，关键是通过寄存器前4位查表获得，这样的算法可以大大提高运算速度。\n上面的方法是半字节查表法，另外还有单字节和双字节查表法，原理都是一样的——事先计算出2^8或2^16个b’的可能值，迭代中使用寄存器前8位或16位查表获得b’。\n反向算法： 之前讨论的算法可以称为正向CRC算法，意思是将g左边的位看作是高位，右边的位看作低位。G的右边加m个0，然后迭代计算是从高位开始，逐步将低位加入到寄存器中。在实际的数据传送过程中，是一边接收数据，一边计算CRC码，正向算法将新接收的数据看作低位。\n逆向算法顾名思义就是将左边的数据看作低位，右边的数据看作高位。这样的话需要在g的左边加m个0，h也要逆向，例如正向CRC-16算法h=0x4c11db8，逆向CRC-16算法h=0xedb88320。b的选择0还是h，由寄存器中右边第1位决定，而不是左边第1位。寄存器仍旧是向左位移，就是说迭代变成从低位到高位。\n要搞清楚的是：\n模2除法到底是怎么除的。（只关心当前阶段的最高位，这里要特别注意。因此我们在模2除法中不存在“不够减”的情况。）\n查表法是啥意思。（我们不关心商，只需要余数。因为模2除法的特殊性，我们并不存在“不够减 ”的情况。所以根据前面几位我们就能知道后面几位该“减去”什么。）\n正向反向的那个可以看看，看不懂也暂时没关系。\n查表法示意\nCRC 校验的基本过程： 采用 CRC 校验时，发送方和接收方用同一个生成多项式 g(x) ， g(x) 是一个 GF(2) 多项式，并且 g(x) 的首位和最后一位的系数必须为 1 。\nCRC 的处理方法是：发送方用发送数据的二进制多项式 t(x) 除以 g(x) ，得到余数 y(x) 作为 CRC 校验码。校验时，以计算的校正结果是否为 0 为据，判断数据帧是否出错。设生成多项式是 r 阶的（最高位是 x^r ）具体步骤如下面的描述。\n发送方：\n在发送的 m 位数据的二进制多项式 t(x) 后添加 r 个 0 ，扩张到 m+ r 位，以容纳 r 位的校验码，追加 0 后的二进制多项式为 T(x) ；\n用 T(x) 除以生成多项式 g(x) ，得到 r 位的余数 y(x) ，它就是 CRC 校验码；\n把 y(x) 追加到 t(x) 后面，此时的数据 s(x) 就是包含了 CRC 校验码的待发送字符串；由于 s(x) = t(x) y(x) ，因此 s(x) 肯定能被 g(x) 除尽。\n接收方：\n接收数据 n(x) ，这个 n(x) 就是包含了 CRC 校验码的 m+r 位数据；\n计算 n(x) 除以 g(x) ，如果余数为 0 则表示传输过程没有错误，否则表示有错误。从 n(x) 去掉尾部的 r 位数据，得到的就是原始数据。\n软件计算： 如果你看了我之前第二部分提到的原理，那么下面这行代码，应该还是挺显然的（记作版本1）：\nr=0; while (len--) r = ((r \u0026gt; 24) \u0026amp; 0xFF];//版本1 （请注意，这个是4字节的版本，即余数长度为32比特，与之前我们原理部分提到的不一样。r的长度是32比特。p是char类型，自加的时候移动一个字节。）\n还是解释一下：r是存储我们结果的寄存器（叫做寄存器，其实这里不是。这么称呼只是因为实际做CRC的时候可能是用硬件 移位寄存器做的。这么称呼比较…自然？），初始值为0（注意现在还是正向计算）。每次迭代，我们将r左移8位，空出来的部分自动补0.然后将移位得到的数字与一个字节的数据做或运算（这一套复杂的操作，只是为了让下一个字节的数据进入寄存器。）。另外，取出r的高8位，通过它来查表获得应该异或的数字（也就是t[(r \u0026raquo;24)\u0026amp;0xFF]）。在这里，“异或”在模2除法里就是“做减法”。\n但是真正计算的时候，数据流是需要补0的（也就是Augmented message）。（不理解的话请看看第二部分的那张图，在第一部分我也略有提及。）所以上面那行代码还要加一段：\nfor (i=0; i\u0026gt; 24) \u0026amp; 0xFF];//版本1 其中W是生成多项式的长度。\n如何省掉这行代码呢？\n或者说，如何把上面这两行代码变成下面这个玩意（记作版本2）：\nr=0; while (len--) r = (r\u0026gt; 24) ^ *p++];//版本2 （这行代码与我开头提到的那个几乎是一个意思，只不过计算方向有所不同）;\n在解释之前，我们先看一下版本1的代码：\n在最开始的阶段，寄存器r向右移位的4次，相应的移出了4个字节的0.而取表的时候，也是拿最高位的0去取。也就是说最开始的4次只是在“装填”，将数据比特装填进寄存器。\n在最末尾的时候，由于我们补了0，所以最后4次移位，实际上相当于把装填进寄存器的数据流最后那4个字节全部移出。当它们全部移出时，寄存器r剩下的值就是我们需要的余数了。\n也就是说…正式的流程从第一个数据字节移出开始，到最后一个数据字节移出结束。当然，这个表述有些问题，移出的不是原来的数据字节，而是它与多次查表得到的值异或得到的东西。\n现在我们转头来看版本2的代码。如果比较难理解的话，我们先跟着代码迭代一次。版本2的第一次迭代：取出r的前8位（此时是0x00），与数据流的第一个字节异或（也就是得到数据流这个字节本身），根据异或结果去取表。\n注意到了吗，版本2的第一次迭代，实际上就是省去了版本1的4次装填，而直接将它拿出来异或。\n但好像现在只是第一次迭代看上去是对的。因为在版本2里，第一次迭代是数据流与0异或。那第二次迭代呢？第二次迭代异或的对象就不是0了呀。\n其实版本1和版本2的算法是有等价性的，具体证明请参考：https://zhuanlan.zhihu.com/p/61636624\n应用范围： CRC具有线性的性质。它适合用来“对抗自然”，即排除随机干扰，但并不适合防御恶意的人为修改。所以CRC被用于通信，而不是网络安全领域。\n常见的CRC算法（参考维基百科）：\n参考： 1、https://liuweiqiang.win/2019/08/06/CRC%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/\n2、https://blog.51cto.com/winda/1063951\n3、https://zhuanlan.zhihu.com/p/61636624\n4、https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html\n","permalink":"https://fan-pengfei.top/posts/crc%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/","summary":"\u003cblockquote\u003e\n\u003cp\u003e之前经常用CRC校验，今天解释一下其原理；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"简单介绍\"\u003e简单介绍：\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCyclic Redundancy Check循环冗余检验，是基于数据计算一组效验码，用于核对数据传输过程中是否被更改或传输错误。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e循环冗余校验检错方案（CRC）：\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eCRC校验原理看起来比较复杂，好难懂，因为大多数书上基本上是以二进制的多项式形式来说明的。其实很简单的问题，其根本思想就是先在要发送的帧后面附加一个数（这个就是用来校验的校验码，但要注意，这里的数也是二进制序列的，下同），生成一个新帧发送给接收端。当然，这个附加的数不是随意的，它  要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除（注意，这里不是直接采用二进制除法，而是采用一种称之为“模2除法”）。到达接收端后，再把接收到的新帧除以（同样采用“模2除法”）这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数，做了“去余”处理（也就已经能整除了），所以结果应该是没有余数。如果有余数，则表明该帧在传输过程中出现了差错。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e【说明】\u003c/strong\u003e：“模2除法”与“算术除法”类似，但它既不向上位借位，也不比较除数和被除数的相同位数值的大小，只要以相同位数进行相除即可。模2加法运算为：1+1=0，0+1=1，0+0=0，无进位，也无借位；模2减法6运算为：1-1=0，0-1=1，1-0=1，0-0=0，也无进位，无借位。相当于二进制中的逻辑异或运算。也就是比较后，两者对应位相同则结果为“0”，不同则结果为“1”。如100101除以1110，结果得到商为11，余数为1，如图5-9左图所示。如11×11=101，如右图所示。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"最通俗的CRC校验原理剖析_深入理解计算机网络\" loading=\"lazy\" src=\"/posts/crc%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/img-1.jpg\"\u003e\n\u003cimg alt=\"最通俗的CRC校验原理剖析_CRC_02\" loading=\"lazy\" src=\"/posts/crc%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/img-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e“模2除法”和“模2乘法”示例\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e具体来说，CRC校验原理就是以下几个步骤：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e先选择（可以随机选择，也可按标准选择，具体在后面介绍）一个用于在接收端进行校验时，对接收的帧进行除法运算的除数（是二进制比较特串，通常是以多项方式表示，所以CRC又称多项式编码方法，这个多项式也称之为“生成多项式”）。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e看所选定的除数二进制位数（假设为k位），然后在要发送的数据帧（假设为m位）后面加上k-1位“0”，然后以这个加了k-1个“0“的新帧（一共是m+k-1位）以“模2除法”方式除以上面这个除数，所得到的余数（也是二进制的比特串）就是该帧的CRC校验码，也称之为FCS（帧校验序列）。但要注意的是，余数的位数一定要是比除数位数只能少一位，哪怕前面位是0，甚至是全为0（附带好整除时）也都不能省略。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e再把这个校验码附加在原数据帧（就是m位的帧，注意不是在后面形成的m+k-1位的帧）后面，构建一个新帧发送到接收端；最后在接收端再把这个新帧以“模2除法”方式除以前面选择的除数，如果没有余数，则表明该帧在传输过程中没出错，否则出现了差错。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e通过以上介绍，大家一定可以理解CRC校验的原理，并且不再认为很复杂吧。  从上面可以看出，CRC校验中有两个关键点：一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串（或多项式）；二是把原始帧与上面选定的除进行二进制除法运算，计算出FCS。前者可以随机选择，也可按国际上通行的标准选择，但最高位和最低位必须均为“1”，如在IBM的SDLC（同步数据链路控制）规程中使用的CRC-16（也就是这个除数一共是17位）生成多项式g（x）= x^16 + x^15 + x^2 +1（对应二进制比特串为：11000000000000101）；而在ISO HDLC（高级数据链路控制）规程、ITU的SDLC、X.25、V.34、V.41、V.42等中使用CCITT-16生成多项式g（x）= x^16+ x^15 + x^5 +1（对应二进制比特串为：11000000000100001）。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003eCRC校验码的计算示例\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e由以上分析可知，既然除数是随机，或者按标准选定的，所以CRC校验的关键是如何求出余数，也就是校验码（CRC校验码）。下面以一个例子来具体说明整个过程。现假设选择的CRC生成多项式为G（X） = X^4 + X^3 + 1，要求出二进制序列10110011的CRC校验码。下面是具体的计算过程：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e首先把生成多项式转换成二进制数，由G（X） = X^4+ X^3 + 1可以知道，它一共是5位（总位数等于最高位的幂次加1，即4+1=5），然后根据多项式各项的含义（多项式只列出二进制值为1的位，也就是这个二进制的第4位、第3位、第0位的二进制均为1，其它位均为0）很快就可得到它的二进制比特串为11001。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e因为生成多项式的位数为5，根据前面的介绍，得知CRC校验码的位数为4（校验码的位数比生成多项式的位数少1）。因为原数据帧10110011，在它后面再加4个0，得到101100110000，然后把这个数以“模2除法”方式除以生成多项式，得到的余数（即CRC码）为0100，如图所示。注意参考前面介绍的“模2除法”运算法则。\n\u003cimg alt=\"最通俗的CRC校验原理剖析_王达_03\" loading=\"lazy\" src=\"/posts/crc%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/img-3.jpg\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eCRC校验码计算示例\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e把上步计算得到的CRC校验0100替换原始帧101100110000后面的四个“0”，得到新帧101100110100。再把这个新帧发送到接收端。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e当以上新帧到达接收端后，接收端会把这个新帧再用上面选定的除数11001以“模2除法”方式去除，验证余数是否为0，如果为0，则证明该帧数据在传输过程中没有出现差错，否则出现了差错。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e通过以上CRC校验原理的剖析和CRC校验码的计算示例的介绍，大家应该对这种看似很复杂的CRC校验原理和计算方法应该比较清楚了。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"算法原理\"\u003e算法原理：\u003c/h2\u003e\n\u003cp\u003e假设数据传输过程中需要发送15位的二进制信息g=101001110100001，这串二进制码可表示为代数多项式g(x) = x^14 + x^12 + x^9 + x^8 + x^7 + x^5+ 1，其中g中第k位的值，对应g(x)中x^k 的系数。将g(x)乘以x^m，既将g后加m个0，然后除以m阶多项式h(x)，得到的(m-1)阶余项r(x)对应的二进制码r就是CRC编码。\u003c/p\u003e\n\u003cp\u003eh(x)可以自由选择或者使用国际通行标准，一般按照h(x)的阶数m，将CRC算法称为CRC-m，比如CRC-32、CRC-64等。国际通行标准可以参看\u003ca href=\"http://en.wikipedia.org/wiki/Cyclic_redundancy_check\"\u003e标准\u003c/a\u003e；\u003c/p\u003e\n\u003cp\u003eg(x)和h(x)的除运算，可以通过g和h做xor（异或）运算。比如将11001与10101做xor运算：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/crc%E6%A0%A1%E9%AA%8C%E5%8E%9F%E7%90%86/img-4.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e明白了xor运算法则后，举一个例子使用CRC-8算法求101001110100001的效验码。CRC-8标准的h(x) = x^8 + x^7 + x^6 + x^4 + x^2 + 1，既h是9位的二进制串111010101。\u003c/p\u003e","title":"CRC校验原理"},{"content":" 最近要做一个GUI界面，自己开发各种功能是极其麻烦的(对的，我之前是做过的)，需要注意很多问题，而且开发起来很多功能基本上是实现不了的，所以只能想其他办法了，因为之前用LVGL做过一个很简单的GUI界面，所以这次也打算用LVGL来做，也算是再复习一下这个界面的使用。 以下是关于LVGL的简介以及特点： LVGL(Light and Versatile Graphics Library，轻巧而多功能的图形库)是一个免费的开放源代码图形库，它提供创建具有易于使用的图形元素，精美的视觉效果和低内存占用的嵌入式GUI所需的一切。\nLVGL主要特性 功能强大的构建块，例如按钮，图表，列表，滑块，图像等.\n带有动画，抗锯齿，不透明，平滑滚动的高级图形.\n各种输入设备，例如触摸板，鼠标，键盘，编码器等.\n支持UTF-8编码的多语言.\n多显示器支持，如TFT，单色显示器.\n完全可定制的图形元素.\n独立于任何微控制器或显示器使用的硬件.\n可扩展以使用很少的内存(64 kB闪存，16 kB RAM)进行操作.\n操作系统，支持外部存储器和GPU，但不是必需的.\n单帧缓冲区操作，即使具有高级图形效果.\n用C语言编写，以实现最大的兼容性(与C ++兼容).\n模拟器可在没有嵌入式硬件的PC上进行嵌入式GUI设计.\n可移植到MicroPython.\n可快速上手的教程、示例、主题.\n丰富的文档教程.\n在MIT许可下免费和开源.\nLVGL硬件要求 基本上，每个现代控制器(肯定必须要能够驱动显示器)都适合运行LVGL。LVGL的最低运行要求很低：\n16、32或64位微控制器或处理器.\n最低 16 MHz 时钟频率.\nFlash/ROM:：对于非常重要的组件要求 \u0026gt;64 kB(建议 \u0026gt; 180 kB).\nRAM 静态 RAM 使用量：~2 kB，取决于所使用的功能和对象类型.\n堆栈： \u0026gt; 2kB(建议 \u0026gt; 8 kB).\n动态数据(堆)：\u0026gt; 2 KB(如果使用多个对象，则建议 \u0026gt; 16 kB)。由 lv_conf.h 中的 LV_MEM_SIZE 宏进行设置.\n显示缓冲区：\u0026gt; “水平分辨率”像素(建议 \u0026gt; 10× “水平分辨率” ).\nMCU 或外部显示控制器中的一帧缓冲区.\nC99或更高版本的编译器.\n具备基本的C(或C ++)知识：指针，结构，回调…\n请注意，内存使用情况可能会因具体的体系结构、编译器和构建选项而异。 Bash\nLVGL源码布局 ./lvgl 库本身.\n./lv_drivers 显示和输入设备驱动程序.\n./lv_examples 示例和演示.\nlvgl官方文档网站(https://docs.lvgl.io).\nlvgl官方博客博客站点(https://blog.lvgl.io).\nsim在线模拟器网站(https://sim.lvgl.io).\nlv*sim *… 适用于各种 IDE 和平台的模拟器项目.\nlv*port *… 移植到其他开发板.\nlv*binding *… 绑定到其他语言.\nlv _…移植到其他平台.\n其中，lvgl，lv_examples和lv_drivers是最受维护、关注的核心存储库。 LVGL更新发行规则 lvgl核心存储库遵循语义版本控制规则： 不兼容的API的主要版本更改。例如。 v5.0.0，v6.0.0\n次要版本，用于新的但向后兼容的功能。例如。 v6.1.0，v6.2.0\n修补程序版本，用于向后兼容的错误修复。例如。 v6.1.1，v6.1.2\nLVGL仓库分支说明 核心存储库至少具有以下分支：\nmaster 分支，最新版本，补丁直接在这里合并。\ndev 分支，开发人员在此处合并新功能，直到将它们合并到 master 分支为止。\nrelease/vX 分支，主要版本的稳定版本\nLVGL发布周期 LVGL有2周的发布周期。在每月的第一个和第三个 星期二 ：\n(基于新功能)从 master 分支创建 主要、次要或错误修复 的版本\n将 master 分支合并到 release/vX 中\n发布后立即将 dev 分支合并到 master 分支\n在接下来的2周内，测试 master 分支的新功能\n错误修复直接合并到 master 中\n2周后，再从第一步重新开始迭代\nLVGL版本标签 每个版本都会创建 vx.Y.Z 之类的标签，如： v7.9.0 。\nLVGL变更日志 版本更改记录在 ./lvgl/CHANGELOG.md 中。\nLVGL版本兼容 在核心存储库中，每个主要版本都有一个分支(例如 release/v6 )。该主要版本的所有次要版本和修补程序版本都在此处合并\n这样就可以添加稳定的较旧版本，而无需打扰较新的版本\n所有主要版本的官方支持周期为1年。\n关于项目 关于我所使用的： MCU用的是STM32F401CCU6， 256Kbytes ROM，64Kbytes RAM 屏幕为SPI接口的1.54寸IPS屏幕，16位色彩，分辨率为240*240 LVGL版本为7.10.1\n","permalink":"https://fan-pengfei.top/posts/lvgl%E5%BC%80%E5%8F%91/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近要做一个GUI界面，自己开发各种功能是极其麻烦的(对的，我之前是做过的)，需要注意很多问题，而且开发起来很多功能基本上是实现不了的，所以只能想其他办法了，因为之前用LVGL做过一个很简单的GUI界面，所以这次也打算用LVGL来做，也算是再复习一下这个界面的使用。\n以下是关于LVGL的简介以及特点：\nLVGL(Light and Versatile Graphics Library，轻巧而多功能的图形库)是一个免费的开放源代码图形库，它提供创建具有易于使用的图形元素，精美的视觉效果和低内存占用的嵌入式GUI所需的一切。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"lvgl主要特性\"\u003eLVGL主要特性\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e功能强大的构建块，例如按钮，图表，列表，滑块，图像等.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e带有动画，抗锯齿，不透明，平滑滚动的高级图形.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e各种输入设备，例如触摸板，鼠标，键盘，编码器等.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e支持UTF-8编码的多语言.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e多显示器支持，如TFT，单色显示器.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e完全可定制的图形元素.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e独立于任何微控制器或显示器使用的硬件.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e可扩展以使用很少的内存(64 kB闪存，16 kB RAM)进行操作.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e操作系统，支持外部存储器和GPU，但不是必需的.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e单帧缓冲区操作，即使具有高级图形效果.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e用C语言编写，以实现最大的兼容性(与C ++兼容).\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e模拟器可在没有嵌入式硬件的PC上进行嵌入式GUI设计.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e可移植到MicroPython.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e可快速上手的教程、示例、主题.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e丰富的文档教程.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e在MIT许可下免费和开源.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"lvgl硬件要求\"\u003eLVGL硬件要求\u003c/h2\u003e\n\u003cp\u003e基本上，每个现代控制器(肯定必须要能够驱动显示器)都适合运行LVGL。LVGL的最低运行要求很低：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e16、32或64位微控制器或处理器.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e最低 16 MHz 时钟频率.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFlash/ROM:：对于非常重要的组件要求 \u0026gt;64 kB(建议 \u0026gt; 180 kB).\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eRAM\n静态 RAM 使用量：~2 kB，取决于所使用的功能和对象类型.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e堆栈： \u0026gt; 2kB(建议 \u0026gt; 8 kB).\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e动态数据(堆)：\u0026gt; 2 KB(如果使用多个对象，则建议 \u0026gt; 16 kB)。由 lv_conf.h 中的 LV_MEM_SIZE 宏进行设置.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e显示缓冲区：\u0026gt; “水平分辨率”像素(建议 \u0026gt; 10× “水平分辨率” ).\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eMCU 或外部显示控制器中的一帧缓冲区.\u003c/p\u003e","title":"LVGL开发"},{"content":" 小一升级版；\n","permalink":"https://fan-pengfei.top/posts/%E5%B0%8F%E4%B8%80%E5%8D%87%E7%BA%A7%E7%89%88%E6%9D%A5%E4%BA%86/","summary":"\u003cblockquote\u003e\n\u003cp\u003e小一升级版；\u003c/p\u003e\n\u003c/blockquote\u003e","title":"小一升级版来了！"},{"content":" ​ 最近接了一个项目，是做一个绕线机，一般用来绕变压器线圈，已经基本做好了，原理并不难，就是根据你输入的一些参数，比如漆包线的直径以及每一层的圈数还有就是一共要绕多少圈，然后由控制器控制步进电机根据光电传感器的信号来回运动，已达到均匀绕线的目的。\n其实这个项目拖了很长时间，因为自己拖延症太严重，而且中间又发生了很多事情，导致项目中间耽搁了很久，还好甲方没有在意，也没有太催我，上周终于下定决心要结束了这个项目，于是就用了一个下午还有晚上的时间完成了这个项目，因为手头没有合适的漆包线，于是就用了直径较细的焊锡丝代替，经过测试效果很好，因为步进电机的转动是通过细分的，所以可以做到很精细的控制，经过测试，精度可以达到0.0035mm，而甲方的要求是0.01mm，也算是完全符合要求了，于是我就把测试视频发给了甲方，甲方首先给予了肯定（那是必然呀，哈哈哈哈），然后给我发了一个红包，我一个疑惑，然后他就提出了一个新的需求，就是希望能够做到反转退线的功能，并且这种情况下能做到减计数。\n我思考了一下，觉得是可以做到的，用一个光电传感器是可以判断转动的次数，那用两个光电传感器就可以判断次数和转动方向，原理是根据两个光电传感器被遮挡的先后来判断转动方向，例如光电1先被遮挡，然后光电2又被遮挡，则认为是正转，反之则认为是反转，理论可行，就差一个实践了，于是就修改了一下硬件（因为第一版硬件只留出了一个光电接口，而且少画了一个二极管），又发去打了一次板，板子还没到，所以也还没开始验证，应该是可以的，所以就等之后板子到了再验证下。\n板子已经到了，经过验证，完全没问题，算是满足了甲方所有的要求，很开心，也收到了剩下的款项，基本这个项目是告一段落了； 下边是关于这个算法的程序：\nvoid HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { uint32_t i = 0; if (GPIO_Pin == guangdian_Pin) { i = 65535; while (i--) ; i = 6553; while (i--) ; if (HAL_GPIO_ReadPin(guangdian_GPIO_Port, guangdian_Pin) == 0) { if (flag_stop == 0) { if (cnt_sum == 0) { flag_dir = N0; } if (cnt_sum % N2 == 0) { flag_dir = !flag_dir; } if (time_k1_temp ","permalink":"https://fan-pengfei.top/posts/%E5%85%89%E7%94%B5%E4%BC%A0%E6%84%9F%E5%99%A8%E5%88%A4%E6%96%AD%E8%BD%AC%E5%8A%A8%E6%96%B9%E5%90%91/","summary":"\u003cblockquote\u003e\n\u003cp\u003e​        最近接了一个项目，是做一个绕线机，一般用来绕变压器线圈，已经基本做好了，原理并不难，就是根据你输入的一些参数，比如漆包线的直径以及每一层的圈数还有就是一共要绕多少圈，然后由控制器控制步进电机根据光电传感器的信号来回运动，已达到均匀绕线的目的。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e其实这个项目拖了很长时间，因为自己拖延症太严重，而且中间又发生了很多事情，导致项目中间耽搁了很久，还好甲方没有在意，也没有太催我，上周终于下定决心要结束了这个项目，于是就用了一个下午还有晚上的时间完成了这个项目，因为手头没有合适的漆包线，于是就用了直径较细的焊锡丝代替，经过测试效果很好，因为步进电机的转动是通过细分的，所以可以做到很精细的控制，经过测试，精度可以达到0.0035mm，而甲方的要求是0.01mm，也算是完全符合要求了，于是我就把测试视频发给了甲方，甲方首先给予了肯定（那是必然呀，哈哈哈哈），然后给我发了一个红包，我一个疑惑，然后他就提出了一个新的需求，就是希望能够做到反转退线的功能，并且这种情况下能做到减计数。\u003c/p\u003e\n\u003cp\u003e我思考了一下，觉得是可以做到的，用一个光电传感器是可以判断转动的次数，那用两个光电传感器就可以判断次数和转动方向，原理是根据两个光电传感器被遮挡的先后来判断转动方向，例如光电1先被遮挡，然后光电2又被遮挡，则认为是正转，反之则认为是反转，理论可行，就差一个实践了，于是就修改了一下硬件（因为第一版硬件只留出了一个光电接口，而且少画了一个二极管），又发去打了一次板，板子还没到，所以也还没开始验证，应该是可以的，所以就等之后板子到了再验证下。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e板子已经到了，经过验证，完全没问题，算是满足了甲方所有的要求，很开心，也收到了剩下的款项，基本这个项目是告一段落了；\n下边是关于这个算法的程序：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_EXTI_Callback\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003euint16_t\u003c/span\u003e GPIO_Pin)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003euint32_t\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (GPIO_Pin \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e guangdian_Pin)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e65535\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (i\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e6553\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (i\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eHAL_GPIO_ReadPin\u003c/span\u003e(guangdian_GPIO_Port, guangdian_Pin) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (flag_stop \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (cnt_sum \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    flag_dir \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e N0;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (cnt_sum \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e N2 \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    flag_dir \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003eflag_dir;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (time_k1_temp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"光电传感器判断转动方向"},{"content":" Windows 使用技巧;\n应用程序自启动 1、在Windows10桌面，右键点击桌面左下角的开始按钮，在弹出的菜单中选择”运行”菜单项. 2、这时就会打开Windows10的运行窗口，在窗口中输入命令shell:startup，然后点击确定按钮. 3、这时就可以打开Windows10系统的启动文件夹. 4、把需要开机启动的应用或是程序的快捷方式拖动到该文件夹中，这样以后电脑开机的时候，就会自动启动这些应用.\n添加右键新建Markdown文档 1、在电脑桌面右键一个.txt文本(文档名随便，文档内容如下)；\nWindows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\\.md] @=\u0026#34;MarkdownFile\u0026#34; \u0026#34;PerceivedType\u0026#34;=\u0026#34;text\u0026#34; \u0026#34;Content Type\u0026#34;=\u0026#34;text/plain\u0026#34; [HKEY_CLASSES_ROOT\\.md\\ShellNew] [HKEY_CLASSES_ROOT\\MarkdownFile] @=\u0026#34;Markdown File\u0026#34; [HKEY_CLASSES_ROOT\\MarkdownFile\\DefaultIcon] @=\u0026#34;%SystemRoot%\\system32\\imageres.dll,-102\u0026#34; [HKEY_CLASSES_ROOT\\MarkdownFile\\shell] [HKEY_CLASSES_ROOT\\MarkdownFile\\shell\\open] 2、修改文档的后缀为.reg； 3、双击打开这个.reg文档； 4、点击确定写进注册表； 5、在桌面右键刷新，然后新建就看到有markdown啦；\n取消右键新建文件(以visio文件为例) 1、WIN+R； 2、输入regedit，enter确认，打开注册表编辑器； 3、在第一项HKEY_CLASSES_ROOT中找到对应的后缀名(例如.vsdx)； 4、删去该项的子项-\u0026gt;ShellNew； 5、再次右键新建文件即可发现已经修改成功；\n","permalink":"https://fan-pengfei.top/posts/windows%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/","summary":"\u003cblockquote\u003e\n\u003cp\u003eWindows 使用技巧;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"应用程序自启动\"\u003e应用程序自启动\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、在Windows10桌面，右键点击桌面左下角的开始按钮，在弹出的菜单中选择”运行”菜单项.\n2、这时就会打开Windows10的运行窗口，在窗口中输入命令shell:startup，然后点击确定按钮.\n3、这时就可以打开Windows10系统的启动文件夹.\n4、把需要开机启动的应用或是程序的快捷方式拖动到该文件夹中，这样以后电脑开机的时候，就会自动启动这些应用.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"添加右键新建markdown文档\"\u003e添加右键新建Markdown文档\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、在电脑桌面右键一个.txt文本(文档名随便，文档内容如下)；\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWindows Registry Editor Version 5.00\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\.\u003c/span\u003emd\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;MarkdownFile\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;PerceivedType\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Content Type\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text/plain\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\.\u003c/span\u003emd\u003cspan style=\"color:#ae81ff\"\u003e\\S\u003c/span\u003ehellNew\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\M\u003c/span\u003earkdownFile\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Markdown File\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\M\u003c/span\u003earkdownFile\u003cspan style=\"color:#ae81ff\"\u003e\\D\u003c/span\u003eefaultIcon\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%SystemRoot%\\system32\\imageres.dll,-102\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\M\u003c/span\u003earkdownFile\u003cspan style=\"color:#ae81ff\"\u003e\\s\u003c/span\u003ehell\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eHKEY_CLASSES_ROOT\u003cspan style=\"color:#ae81ff\"\u003e\\M\u003c/span\u003earkdownFile\u003cspan style=\"color:#ae81ff\"\u003e\\s\u003c/span\u003ehell\u003cspan style=\"color:#ae81ff\"\u003e\\o\u003c/span\u003epen\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e2、修改文档的后缀为.reg；\n3、双击打开这个.reg文档；\n4、点击确定写进注册表；\n5、在桌面右键刷新，然后新建就看到有markdown啦；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"取消右键新建文件以visio文件为例\"\u003e取消右键新建文件(以visio文件为例)\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、WIN+R；\n2、输入regedit，enter确认，打开注册表编辑器；\n3、在第一项HKEY_CLASSES_ROOT中找到对应的后缀名(例如.vsdx)；\n4、删去该项的子项-\u0026gt;ShellNew；\n\u003cimg alt=\"8e1686b424d6d058e33837cd304b045\" loading=\"lazy\" src=\"/posts/windows%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/img-1.png\"\u003e\n\u003cimg alt=\"95bc7aa3a625bb0bed08dfa657c80af\" loading=\"lazy\" src=\"/posts/windows%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/img-2.png\"\u003e\n5、再次右键新建文件即可发现已经修改成功；\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Windows使用技巧"},{"content":" 最近做项目用到了Arduino的板子，在此记录一下开发过程中遇到的一些问题，其实我之前是很不情愿用Arduino，觉得底层都被封装起来了，而且那个IDE用起来真的是一言难尽。不过最近客户要求用Arduino进行开发，就硬着头皮上了，结果发现用VScode+Platformio开发Arduino还是顶好用的，而且有太多可以用的封装好的函数了，真香！\n一个IO引脚驱动两个不同颜色的LED 用一个IO引脚驱动两个led灯\n这个电路可以实现，一个IO引脚驱动两个不同颜色的LED，不过自己只测试过红色和绿色的LED，其他颜色的并没有验证，因为似乎红色和绿色对电压的要求较低。\n","permalink":"https://fan-pengfei.top/posts/arduino/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近做项目用到了Arduino的板子，在此记录一下开发过程中遇到的一些问题，其实我之前是很不情愿用Arduino，觉得底层都被封装起来了，而且那个IDE用起来真的是一言难尽。不过最近客户要求用Arduino进行开发，就硬着头皮上了，结果发现用VScode+Platformio开发Arduino还是顶好用的，而且有太多可以用的封装好的函数了，真香！\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ch3 id=\"一个io引脚驱动两个不同颜色的led\"\u003e一个IO引脚驱动两个不同颜色的LED\u003c/h3\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用一个IO引脚驱动两个led灯\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/arduino/img-1.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e这个电路可以实现，一个IO引脚驱动两个不同颜色的LED，不过自己只测试过红色和绿色的LED，其他颜色的并没有验证，因为似乎红色和绿色对电压的要求较低。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Arduino"},{"content":" 最近做项目经常用到STC的单片机，在使用过程中遇到了较多的问题，而且在后续的硬件搭建过程中也积累了不少的经验，因此打算记录一下，方便以后的查询。\n","permalink":"https://fan-pengfei.top/posts/stc%E5%8D%95%E7%89%87%E6%9C%BA%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近做项目经常用到STC的单片机，在使用过程中遇到了较多的问题，而且在后续的硬件搭建过程中也积累了不少的经验，因此打算记录一下，方便以后的查询。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"stc单片机使用技巧"},{"content":" 最近做项目用到了51单片机驱动蜂鸣器，但是一直无法驱动，后来以为是上拉电阻问题，结果发现加了上拉电阻会一直响，即使IO口输出为低电平。\n​ 后来发现还需要加一个下拉电阻以保证不受干扰。\n参考：https://blog.csdn.net/qq_25814297/article/details/118696321?spm=1001.2014.3001.5506\n下面就 3.3V NPN 三极管驱动有源蜂鸣器设计，从实际产品中分析电路设计存在的问题，提出电路的改进方案，使读者能从小小的蜂鸣器电路中学会分析和改进电路的方法，从而设计出更优秀的产品，达到抛砖引玉的效果。\n常见错误接法 图1 为典型的错误接法，当 BUZZER 端输入高电平时蜂鸣器不响或响声太小。当 I/O 口为高电平时，基极电压为 3.3/4.7*3.3V≈2.3V，由于三极管的压降 0.6~0.7V，则三极管射 极电压为 2.3-0.7=1.6V，驱动电压太低导致蜂鸣器无法驱动或者响声很小。\n图2 错误接法2\n图2 为第二种典型的错误接法，由于上拉电阻R2，BUZZER 端在输出低电平时，由于 电阻R1和R2的分压作用，三极管不能可靠关断。\n图3 为第三种错误接法，三极管的高电平门槛电压就只有 0.7V，即在 BUZZER 端输入 压只要超过0.7V就有可能使三极管导通，显然0.7V的门槛电压对于数字电路来说太低了， 电磁干扰的环境下，很容易造成蜂鸣器鸣叫。\n图 4 为第四种错误接法，当CPU的GPIO管脚存在内部下拉时，由于 I/O 口存在输入阻抗，也可能导致三极管不能可靠关断，而且和图3一样BUZZER端输入电压只要超过0.7V就有可能使三极管导通。\n以上几种用法我觉得也不能说是完全不行，对于器件的各种参数要求会比较局限，不利于器件选型，抗干扰性能也比较差。\nNPN 三极管控制有源蜂鸣器常规设计 图 5 为通用有源蜂鸣器的驱动电路。电阻R1为限流电阻，防止流过基极电流过大损坏三极管。电阻R2有着重要的作用，第一个作用：R2 相当于基极的下拉电阻。如果A端被悬空则由于R2的存在能够使三极管保持在可靠的关断状态，如果删除R2则当BUZZER输入端悬空时则易受到干扰而可能导致三极管状态发生意外翻转或进入不期望的放大状态，造成蜂鸣器意外发声。第二个作用：R2可提升高电平的门槛电压。如果删除R2，则三极管的高电平门槛电压就只有0.7V，即A端输入电压只要超过0.7V 就有可能导通，添加R2的情况就不同了，当从A端输入电压达到约2.2V 时三极管才会饱和导通，具体计算过程如下：\n假定β =120为晶体管参数的最小值，蜂鸣器导通电流是15mA。那么集电极电流IC=15mA。则三极管刚刚达到饱和导通时的基极电流是 IB=15mA/120=0.125mA。流经R2的电流是0.7V/3.3kΩ=0.212mA，流经R1的电流 IR1=0.212mA +0.125mA=0.337 mA。最后算出BUZZER端的门槛电压是0.7V+0.337mA× 4.7kΩ=2.2839V≈2.3V。\n图中的C2为电源滤波电容，滤除电源高频杂波。C1可以在有强干扰环境下，有效的滤除干扰信号，避免蜂鸣器变音和意外发声，在 RFID射频通讯、Mifare卡的应用时，这里初步选用0.1uF 的电容，具体可以根据实际情况选择。\n改进方案 蜂鸣器竟然有EMI 辐射？！在 NPN 3.3V 控制有源蜂鸣器时，在电路的 BUZZER 输入 高电平，让蜂鸣器鸣叫，检测蜂鸣器输入管脚（NPN 三极管的C极处信号，发现蜂鸣器在发声时，向外发生1.87KHz，-2.91V 的脉冲信号，如图 6 所示。\n图 6 蜂鸣器自身发放脉冲\n在电路的BUZZER 输入20Hz的脉冲信号，让蜂鸣器鸣叫，检测蜂鸣器输入管脚处信号，发现蜂鸣器在发声时，在控制电平上叠加了1.87KHz，-2.92V 的脉冲信号，并且在蜂鸣器关断时出现正向尖峰脉冲（≥10V），如图7所示。\n图7中1.87KHz，-2.92V 的脉冲信号应该是有源蜂鸣器内部震荡源释放出来的信号。常用有源蜂鸣器主要分为压电式、 电磁震荡式两种， iMX283 开发板上用的是压电式蜂鸣器，压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成，而多谐震荡器由晶体管或集成电路构成，我们所用的蜂鸣器内部含有晶体管震荡电路（有兴趣的朋友可以自己拆开看看）。\n有源蜂鸣器产生脉冲信号能量不是很强，可以考虑增加滤波电容将脉冲信号滤除。在有源蜂鸣器的两端添加一个104的滤波电容，脉冲信号削减到-110mV，如图 8 所示，但顶部信号由于电容充电过慢，有点延时。\n图 8 减少蜂鸣器自身发放脉冲\n消除蜂鸣器EMI辐射后改进电路图如图9所示：\n图 9 NPN 有源蜂鸣器控制电路改善后电路图\n兼容性设计 作为标准电路，需要考虑电路的兼容性问题，比如同样耐压不同功率的有源蜂鸣器，有 源蜂鸣器和无源蜂鸣器的兼容性问题。\n1兼容同样耐压不同功率的有源蜂鸣器电路设计 为了电路的兼容性和可扩展性，电路需要考虑兼容不同厂家和不同功率的蜂鸣器。同一 个耐压的蜂鸣器主要是蜂鸣器的内阻和工作电流不一样，一般 3V5V 耐压的蜂鸣器，不同功率的蜂鸣器导通电流是 10mA80mA。我们按照最大功率的蜂鸣器去设计电路即可，即三极管的推动电流按照 80 mA 设计。\n假定：β＝120 为晶体管参数的最小值，蜂鸣器导通电流是 80 mA。那么集电极电流 IC ＝80 mA。则三极管刚刚达到饱和导通时的基极电流 IB＝80mA／ 120＝0.667mA。流经 R2的电流是 0.7V／ 3.3kΩ＝ 0.212mA，所以流经 R1 的电流应该是 IR1=0.667mA +0.125mA=0.792mA。BUZZER 端的门槛电压是设定在 2.2V，那么 R1=(2.2V-0.7V)/ 0.792mA=1.89K。电阻取常规 2K 即可。\n如果电路更换功率稍大一点的有源蜂鸣器，可以按照上面的计算方法计算 R1 的大小。\n2兼容有源蜂鸣器和无源蜂鸣器电路设计 在电路的设计过程中，往往会碰到需求变更，比如项目前期，对蜂鸣器的发声频率没有 要求，但后期有要求，需要更换为无源蜂鸣器，这时就需要修改电路图，甚至修改 PCB， 这样就增加了改动成本、周期和风险。\n有源蜂鸣器和无源蜂鸣器的驱动电路区别主要在于无源蜂鸣器本质上是一个感性元件， 其电流不能瞬变，因此必须有一个续流二极管提供续流。否则，在蜂鸣器两端会有反向感应 电动势，产生几十伏的尖峰电压，可能损坏驱动三极管，并干扰整个电路系统的其它部分。而如果电路中工作电压较大，要使用耐压值较大的二极管，而如果电路工作频率高，则要选 用高速的二极管。这里选择的是 IN4148 的开关二极管。电路如图 10 所示。\n","permalink":"https://fan-pengfei.top/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近做项目用到了51单片机驱动蜂鸣器，但是一直无法驱动，后来以为是上拉电阻问题，结果发现加了上拉电阻会一直响，即使IO口输出为低电平。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e​        后来发现还需要加一个下拉电阻以保证不受干扰。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-1.png\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e参考：\u003ca href=\"https://blog.csdn.net/qq_25814297/article/details/118696321?spm=1001.2014.3001.5506\"\u003ehttps://blog.csdn.net/qq_25814297/article/details/118696321?spm=1001.2014.3001.5506\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e  下面就 3.3V NPN 三极管驱动有源蜂鸣器设计，从实际产品中分析电路设计存在的问题，提出电路的改进方案，使读者能从小小的蜂鸣器电路中学会分析和改进电路的方法，从而设计出更优秀的产品，达到抛砖引玉的效果。\u003c/p\u003e\n\u003ch1 id=\"常见错误接法\"\u003e常见错误接法\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e  图1 为典型的错误接法，当 BUZZER 端输入高电平时蜂鸣器不响或响声太小。当 I/O 口为高电平时，基极电压为 3.3/4.7*3.3V≈2.3V，由于三极管的压降 0.6~0.7V，则三极管射 极电压为 2.3-0.7=1.6V，驱动电压太低导致蜂鸣器无法驱动或者响声很小。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e图2 错误接法2\u003c/p\u003e\n\u003cp\u003e  图2 为第二种典型的错误接法，由于上拉电阻R2，BUZZER 端在输出低电平时，由于 电阻R1和R2的分压作用，三极管不能可靠关断。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e  图3 为第三种错误接法，三极管的高电平门槛电压就只有 0.7V，即在 BUZZER 端输入 压只要超过0.7V就有可能使三极管导通，显然0.7V的门槛电压对于数字电路来说太低了， 电磁干扰的环境下，很容易造成蜂鸣器鸣叫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e  图 4 为第四种错误接法，当CPU的GPIO管脚存在内部下拉时，由于 I/O 口存在输入阻抗，也可能导致三极管不能可靠关断，而且和图3一样BUZZER端输入电压只要超过0.7V就有可能使三极管导通。\u003c/p\u003e\n\u003cp\u003e  以上几种用法我觉得也不能说是完全不行，对于器件的各种参数要求会比较局限，不利于器件选型，抗干扰性能也比较差。\u003c/p\u003e\n\u003ch1 id=\"npn-三极管控制有源蜂鸣器常规设计\"\u003eNPN 三极管控制有源蜂鸣器常规设计\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8%E7%94%B5%E8%B7%AF/img-6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e  图 5 为通用有源蜂鸣器的驱动电路。电阻R1为限流电阻，防止流过基极电流过大损坏三极管。电阻R2有着重要的作用，第一个作用：R2 相当于基极的下拉电阻。如果A端被悬空则由于R2的存在能够使三极管保持在可靠的关断状态，如果删除R2则当BUZZER输入端悬空时则易受到干扰而可能导致三极管状态发生意外翻转或进入不期望的放大状态，造成蜂鸣器意外发声。第二个作用：R2可提升高电平的门槛电压。如果删除R2，则三极管的高电平门槛电压就只有0.7V，即A端输入电压只要超过0.7V 就有可能导通，添加R2的情况就不同了，当从A端输入电压达到约2.2V 时三极管才会饱和导通，具体计算过程如下：\u003c/p\u003e\n\u003cp\u003e  假定β =120为晶体管参数的最小值，蜂鸣器导通电流是15mA。那么集电极电流IC=15mA。则三极管刚刚达到饱和导通时的基极电流是 IB=15mA/120=0.125mA。流经R2的电流是0.7V/3.3kΩ=0.212mA，流经R1的电流 IR1=0.212mA +0.125mA=0.337 mA。最后算出BUZZER端的门槛电压是0.7V+0.337mA× 4.7kΩ=2.2839V≈2.3V。\u003c/p\u003e\n\u003cp\u003e  图中的C2为电源滤波电容，滤除电源高频杂波。C1可以在有强干扰环境下，有效的滤除干扰信号，避免蜂鸣器变音和意外发声，在 RFID射频通讯、Mifare卡的应用时，这里初步选用0.1uF 的电容，具体可以根据实际情况选择。\u003c/p\u003e\n\u003ch1 id=\"改进方案\"\u003e改进方案\u003c/h1\u003e\n\u003cp\u003e  蜂鸣器竟然有EMI 辐射？！在 NPN 3.3V 控制有源蜂鸣器时，在电路的 BUZZER 输入 高电平，让蜂鸣器鸣叫，检测蜂鸣器输入管脚（NPN 三极管的C极处信号，发现蜂鸣器在发声时，向外发生1.87KHz，-2.91V 的脉冲信号，如图 6 所示。\u003c/p\u003e","title":"蜂鸣器驱动电路"},{"content":" GCC常用命令；\n1. 什么是gcc gcc的全称是GNU Compiler Collection，它是一个能够编译多种语言的编译器。最开始gcc是作为C语言的编译器（GNU C Compiler），现在除了c语言，还支持C++、java、Pascal等语言。gcc支持多种硬件平台。\n2. gcc的特点 gcc是一个可移植的编译器，支持多种硬件平台。例如ARM、X86等等。\ngcc不仅是个本地编译器，它还能跨平台交叉编译。所谓的本地编译器，是指编译出来的程序只能够在本地环境进行运行。而gcc编译出来的程序能够在其他平台进行运行。例如嵌入式程序可在x86上编译，然后在arm上运行。\ngcc有多种语言前端，用于解析不同的语言。\ngcc是按模块化设计的，可以加入新语言和新CPU架构的支持。\ngcc是自由软件。任何人都可以使用或更改这个软件。\n3. gcc编译程序的过程 gcc编译程序主要经过四个过程：\n预处理（Pre-Processing）\n编译 （Compiling）\n汇编 （Assembling）\n链接 （Linking）\n预处理实际上是将头文件、宏进行展开。编译阶段，gcc调用不同语言的编译器，例如c语言调用编译器ccl。gcc实际上是个工具链，在编译程序的过程中调用不同的工具。汇编阶段，gcc调用汇编器进行汇编。链接过程会将程序所需要的目标文件进行链接成可执行文件。汇编器生成的是可重定位的目标文件，学过操作系统，我们知道，在源程序中地址是从0开始的，这是一个相对地址，而程序真正在内存中运行时的地址肯定不是从0开始的，而且在编写源代码的时候也不能知道程序的绝对地址，所以重定位能够将源代码的代码、变量等定位为内存具体地址。下面以一张图来表示这个过程，注意过程中文件的后缀变化，编译选项和这些后缀有关。\n这是GCC编译的四个步骤。\n4. gcc常用选项 来看一下gcc常用选项\n选项名 作用\n-o 产生目标（.i、.s、.o、可执行文件等）\n-E 只运行C预编译器\n-S 告诉编译器产生汇编程序文件后停止编译，产生的汇编语言文件拓展名为.s\n-c 通知gcc取消连接步骤，即编译源码，并在最后生成目标文件\n-Wall 使gcc对源文件的代码有问题的地方发出警告\n-Idir 将dir目录加入搜索头文件的目录路径\n-Ldir 将dir目录加入搜索库的目录路径\n-llib 连接lib库\n-g 在目标文件中嵌入调试信息，以便gdb之类的调试程序调试\n现在我们有源文件hello.c，下面是一些gcc的使用示例：\ngcc -E hello.c -o hello.i 对hello.c文件进行预处理，生成了hello.i 文件 gcc -S hello.i -o hello.s 对预处理文件进行编译，生成了汇编文件 gcc -c hello.s -o hello.o 对汇编文件进行编译，生成了目标文件 gcc hello.o -o hello 对目标文件进行链接，生成可执行文件 gcc hello.c -o hello 直接编译链接成可执行目标文件 gcc -c hello.c 或 gcc -c hello.c -o hello.o 编译生成可重定位目标文件 使用gcc时可以加上-Wall选项。下面这个例子如果不加上-Wall选项，编译器不会报出任何错误或警告，但是程序的结果却不是预期的：\n//bad.c #include int main() { printf(\u0026#34;the number is %f \u0026#34;,5); //程序输出了the number is 0.000000，结果错误 return 0; } 使用-Wall选项：\ngcc -Wall bad.c -o bad gcc将输出警告信息：\nwarning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=] printf(\u0026#34;the number is %f\\n\u0026#34;,5); 5. gcc编译多个文件 假设现在有三个文件：hello.c hello.h main.c ,三个文件的内容如下：\n// hello.c #include #include\u0026#34;hello.h\u0026#34; void printHello() { printf(\u0026#34;hello world!\\n\u0026#34;); } //main.c #include #include\u0026#34;hello.h\u0026#34; int main() { printHello(); return 0; } //hello.h //仅包含函数声明 #ifndef _HELLO_ #define _HELLO_ void printHello(); #endif 编译这三个文件，可以一次编译：\ngcc hello.c main.c -o main 生成可执行文件main\n也可以独立编译：\ngcc -Wall -c main.c -o main.o gcc -Wall -c hello.c -o hello.o gcc -Wall main.o hello.o -o main 独立编译的好处是，当其中某个模块发送改变时，只需要编译该模块就行，不必重新编译所有文件，这样可以节省编译时间。\n6. 使用外部库 在使用C语言和其他语言进行程序设计的时候，我们需要头文件来提供对常数的定义和对系统及库函数调用的声明。库文件是一些预先编译好的函数集合，那些函数都是按照可重用原则编写的。它们通常由一组互相关联的可重用原则编写的，它们通常由一组互相关联的用来完成某项常见工作的函数构成。使用库的优点在于：\n模块化的开发\n可重用性\n可维护性\n库又可以分为静态库与动态库：\n静态库（.a）：程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。静态库比较占用磁盘空间，而且程序不可以共享静态库。运行时也是比较占内存的，因为每个程序都包含了一份静态库。\n动态库（.so或.sa）：程序在运行的时候才去链接共享库的代码，多个程序共享使用库的代码，这样就减少了程序的体积。\n一般头文件或库文件的位置在：\n/usr/include及其子目录底下的include文件夹\n/usr/local/include及其子目录底下的include文件夹\n/usr/lib\n/usr/local/lib\n/lib\n7. 生成静态库 为了生成.a文件，我们需要先生成.o文件。下面这行命令将我们的hello.o打包成静态库libhello.a：\nar rcs libhello.a hello.o ar是gun归档工具，rcs表示replace and create，如果libhello之前存在，将创建新的libhello.a并将其替换。\n然后就可以这样来使用静态库libhello.a\ngcc -Wall main.c libhello.a -o main 还有另外一种使用方式：\ngcc -Wall -L. main.c -o main -lhello 【lhello 是 libhello的缩写】 其中 **-L.**表示库文件的位置在当前目录下，由于libhello.a是我们自己生成的，并存放在当前录下下，所以需要加上-L.选项。默认库文件是在系统的目录下进行搜索。同样的，-I.选项用于头文件的搜索。\n8. 生成共享库 生成一个共享库，名称的规则是libxxx.so。将刚才hello.o生成libhello.so的命令为：\ngcc -shared -fPIC hello.o -o libhello.so 生成了共享库之后，可以这样来使用共享库：\ngcc -Wall main.o -o main -L. -lhello 该命令与使用静态库的命令相同，但是在共享库与静态库共存的情况下，优先使用共享库。\n共享库有时候并不不在当前的目录下，为了让gcc能够找得到共享库，有下面几种方法：\n拷贝.so文件到系统共享库路径下，一般指/usr/lib\n在~/.bash_profile文件中，配置LD_LIBRARY_PATH变量\n配置/etc/ld.so.conf，配置完成后调用ldconfig更新ld.so.cache\n其中，shared选项表示生成共享库格式。fPIC表示产生位置无关码（position independent code），位置无关码表示它的运行、加载与内存位置无关，可以在任何内存地址进行加载。\n9. 库的搜索路径 库的搜索路径遵循几个搜索原则：从左到右搜索-I -l指定的目录，如果在这些目录中找不到，那么gcc会从由环境 变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH.如果还是找不到，那么会从系统指定指定的目录进行搜索。\n文章链接：http://www.cnblogs.com/QG-whz/p/5456720.html\n","permalink":"https://fan-pengfei.top/posts/gcc%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003eGCC常用命令；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1-什么是gcc\"\u003e1. 什么是gcc\u003c/h2\u003e\n\u003cp\u003egcc的全称是GNU Compiler Collection，它是一个能够编译多种语言的编译器。最开始gcc是作为C语言的编译器（GNU C Compiler），现在除了c语言，还支持C++、java、Pascal等语言。gcc支持多种硬件平台。\u003c/p\u003e\n\u003ch2 id=\"2-gcc的特点\"\u003e2. gcc的特点\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003egcc是一个可移植的编译器，支持多种硬件平台。例如ARM、X86等等。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003egcc不仅是个本地编译器，它还能跨平台交叉编译。所谓的本地编译器，是指编译出来的程序只能够在本地环境进行运行。而gcc编译出来的程序能够在其他平台进行运行。例如嵌入式程序可在x86上编译，然后在arm上运行。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003egcc有多种语言前端，用于解析不同的语言。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003egcc是按模块化设计的，可以加入新语言和新CPU架构的支持。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003egcc是自由软件。任何人都可以使用或更改这个软件。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"3-gcc编译程序的过程\"\u003e3. gcc编译程序的过程\u003c/h2\u003e\n\u003cp\u003egcc编译程序主要经过四个过程：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e预处理（Pre-Processing）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e编译 （Compiling）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e汇编 （Assembling）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e链接 （Linking）\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/gcc%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/img-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e预处理实际上是将头文件、宏进行展开。编译阶段，gcc调用不同语言的编译器，例如c语言调用编译器ccl。gcc实际上是个工具链，在编译程序的过程中调用不同的工具。汇编阶段，gcc调用汇编器进行汇编。链接过程会将程序所需要的目标文件进行链接成可执行文件。汇编器生成的是可重定位的目标文件，学过操作系统，我们知道，在源程序中地址是从0开始的，这是一个相对地址，而程序真正在内存中运行时的地址肯定不是从0开始的，而且在编写源代码的时候也不能知道程序的绝对地址，所以\u003cstrong\u003e重定位\u003c/strong\u003e能够将源代码的代码、变量等定位为内存具体地址。下面以一张图来表示这个过程，注意过程中文件的后缀变化，编译选项和这些后缀有关。\u003c/p\u003e\n\u003cp\u003e这是GCC编译的四个步骤。\u003c/p\u003e\n\u003ch2 id=\"4-gcc常用选项\"\u003e4. gcc常用选项\u003c/h2\u003e\n\u003cp\u003e来看一下gcc常用选项\u003c/p\u003e\n\u003cp\u003e选项名\n作用\u003c/p\u003e\n\u003cp\u003e-o\n产生目标（.i、.s、.o、可执行文件等）\u003c/p\u003e\n\u003cp\u003e-E\n只运行C预编译器\u003c/p\u003e\n\u003cp\u003e-S\n告诉编译器产生汇编程序文件后停止编译，产生的汇编语言文件拓展名为.s\u003c/p\u003e\n\u003cp\u003e-c\n通知gcc取消连接步骤，即编译源码，并在最后生成目标文件\u003c/p\u003e\n\u003cp\u003e-Wall\n使gcc对源文件的代码有问题的地方发出警告\u003c/p\u003e\n\u003cp\u003e-Idir\n将dir目录加入搜索头文件的目录路径\u003c/p\u003e\n\u003cp\u003e-Ldir\n将dir目录加入搜索库的目录路径\u003c/p\u003e\n\u003cp\u003e-llib\n连接lib库\u003c/p\u003e\n\u003cp\u003e-g\n在目标文件中嵌入调试信息，以便gdb之类的调试程序调试\u003c/p\u003e\n\u003cp\u003e现在我们有源文件hello.c，下面是一些gcc的使用示例：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc -E hello.c -o hello.i   对hello.c文件进行预处理，生成了hello.i 文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc -S hello.i -o hello.s    对预处理文件进行编译，生成了汇编文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc -c hello.s -o hello.o  对汇编文件进行编译，生成了目标文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc hello.o -o hello 对目标文件进行链接，生成可执行文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc hello.c -o hello 直接编译链接成可执行目标文件\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egcc -c hello.c 或 gcc -c hello.c -o hello.o 编译生成可重定位目标文件\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e使用gcc时可以加上-Wall选项。下面这个例子如果不加上-Wall选项，编译器不会报出任何错误或警告，但是程序的结果却不是预期的：\u003c/p\u003e","title":"GCC学习记录"},{"content":" vscode代码美化；\n1）文件 —\u0026gt; 首选项\n因为 VSCode 默认启用了根据文件类型自动设置tabsize的选项，在设置中添加：\n\u0026#34;editor.detectIndentation\u0026#34;: false 2）编辑器配置\n在项目文件中新建 .editorconfig 文件为特定类型文件指定缩进大小、缩进类型（空格，或tab），是否自动插入末行等等。\nroot = true [*] charset = utf-8 indent_style = tab indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true [*.md] max_line_length = off trim_trailing_whitespace = false 3）安装 VsCode 格式化代码插件\n搜索并安装 Beautify 格式化代码插件 打开要格式化的文件 —\u0026gt; F1 —\u0026gt; Beautify file —\u0026gt; 选择你要格式化的代码类型\n4）格式化对齐快捷键：\nWindows： Ctrl + K + F\nWindows：Shift + Alt + F\nMac： Shift + Option + F\nUbuntu： Ctrl + Shift + I\n","permalink":"https://fan-pengfei.top/posts/vscode%E6%A0%BC%E5%BC%8F%E5%8C%96%E7%BE%8E%E5%8C%96%E4%BB%A3%E7%A0%81/","summary":"\u003cblockquote\u003e\n\u003cp\u003evscode代码美化；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1）文件 —\u0026gt; 首选项\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e因为 VSCode 默认启用了根据文件类型自动设置tabsize的选项，在设置中添加：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;editor.detectIndentation\u0026#34;\u003c/span\u003e: false\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e2）编辑器配置\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e在项目文件中新建 .editorconfig 文件为特定类型文件指定缩进大小、缩进类型（空格，或tab），是否自动插入末行等等。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eroot \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e true\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e*\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echarset \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e utf-8\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eindent_style \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e tab\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eindent_size \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einsert_final_newline \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e true\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrim_trailing_whitespace \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e true\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e*.md\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emax_line_length \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e off\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrim_trailing_whitespace \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e false\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e3）安装 VsCode 格式化代码插件\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e搜索并安装 Beautify 格式化代码插件\n打开要格式化的文件 —\u0026gt; F1 —\u0026gt; Beautify file —\u0026gt; 选择你要格式化的代码类型\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e4）格式化对齐快捷键：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eWindows： \u003ccode\u003eCtrl\u003c/code\u003e + \u003ccode\u003eK\u003c/code\u003e + \u003ccode\u003eF\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eWindows：\u003ccode\u003eShift\u003c/code\u003e + \u003ccode\u003eAlt\u003c/code\u003e + \u003ccode\u003eF\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eMac： \u003ccode\u003eShift\u003c/code\u003e + \u003ccode\u003eOption\u003c/code\u003e + \u003ccode\u003eF\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUbuntu： \u003ccode\u003eCtrl\u003c/code\u003e + \u003ccode\u003eShift\u003c/code\u003e + \u003ccode\u003eI\u003c/code\u003e\u003c/p\u003e","title":"VSCode格式化美化代码"},{"content":" 程序已经写好了； Github地址:https://github.com/fan-pengfei/Bootloader_H750_SD/tree/master\n","permalink":"https://fan-pengfei.top/posts/h750%E6%9E%9A%E4%B8%BEu%E7%9B%98%E6%8B%96%E6%8B%BD%E5%BC%8Fbootloader+%E5%A4%96%E9%83%A8spi_flash/","summary":"\u003cblockquote\u003e\n\u003cp\u003e程序已经写好了；\nGithub地址:\u003ca href=\"https://github.com/fan-pengfei/Bootloader_H750_SD/tree/master\"\u003ehttps://github.com/fan-pengfei/Bootloader_H750_SD/tree/master\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","title":"H750枚举U盘拖拽式BootLoader+外部spi_flash"},{"content":" OLED 局部刷新提高帧率.\n待续\n","permalink":"https://fan-pengfei.top/posts/oled%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/","summary":"\u003cblockquote\u003e\n\u003cp\u003eOLED 局部刷新提高帧率.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e待续\u003c/p\u003e\n\u003ch2\u003e\u003c/h2\u003e","title":"oled使用技巧"},{"content":" 之前准备电赛的时候，需要用到无线调试器，先是上淘宝看了一下，基本都是大几百，所以想着自己自制一个，直接搞DAP无线调试器有点难，所以就用手头现有的蓝牙模块搞了一个串口无线下载器，调试的话就靠printf打印了；\n也做了好几个版本，最初的版本主要有两个大的问题：\n1、没有程序校验过程，不能保证单片机收到的APP程序是完整且正确的； 2、没有错误解决机制，一旦APP程序错误或者程序跑飞，就必须手动按下复位键进行复位；\n为解决以上两个问题，采取了以下措施\n1、对APP程序加入CRC校验； 2、加入看门狗，解决程序跑飞问题；\n以下是关于这个项目的具体介绍： 一、APP程序部分 首先要有一个APP程序，与一般的程序相比，APP程序有两个地方需要进行配置：\n分别如下图所示：\n1、在system_stm32f4xx.c中进行修改：\n2、在小锤子那里进行修改：\n3、修改完毕后，进行正常的编译即可；\n二、BIN文件的处理 最初的版本并没有加入crc校验，所以经常会出错，导致程序经常跑飞，所以加入了crc32校验；经检验，效果那是非常的好； 为什么是采用bin文件而不是使用更容易得到的hex文件呢？ 原因很简单：因为写入单片机flash的程序实际就是bin文件，hex文件需要转换才能写进flash中； 以上操作只是得到了hex文件，而我们需要的是bin文件，以下是对hex文件的处理过程：\n1、第一步呢，当然是得到最初的bin文件；而Keil默认情况下编译成功后生成的是Hex文件，而不是bin文件，所以要将Hex文件转换为bin文件，当然是有现成的Hex转bin的程序的，但是比较麻烦，可以用Keil自带的功能完成编译后自动生成bin文件的过程，具体的方法请自行百度；\n这是我的配置，仅供参考：\nD:\\Keil_v5\\ARM\\ARMCC\\bin\\fromelf.exe —bin -o fromelf —bin -o “$L@L.bin” “#L”\n2、然后是对bin文件进行处理，也就是crc32校验码的生成以及插入该校验码到bin文件中，我用的是python脚本的方式，代码如下：\n# -*- coding:utf-8 -*- import binascii import os import sys def crc2hex(crc): res=\u0026#39;\u0026#39; for i in range(4): t=crc \u0026amp; 0xFF crc \u0026gt;\u0026gt;= 8 res=\u0026#39;%02X%s\u0026#39; % (t, res) return res inputfile = \u0026#34;E:\\IAP\\APP\\Project\\OBJ\\STM32F407.bin\u0026#34;#实际存放的bin文件路径 isfile = os.path.isfile(inputfile); print(inputfile); fp = open(inputfile, \u0026#34;r+b\u0026#34;) #直接打开一个文件，如果文件不存在则创建文件 filesize = os.path.getsize(inputfile) print(\u0026#34;ZI app firmware size:\u0026#34;, filesize, \u0026#34;bytes.\u0026#34;) #计算bin文件的CRC，首先清空CRC32区域的4个byte fp.seek(0x1c, 0)#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32 clear4bytes = \u0026#39;00000000\u0026#39; c4 =binascii.unhexlify(clear4bytes) fp.write(c4) #将CRC32存放的区域4bytes清零 fp.seek(0, 0)#从0开始读取整个bin file_content = fp.read()#读整个文件内容到 file_content crc = binascii.crc32(file_content) print(\u0026#39;CRC32:\u0026#39;, hex(crc)) fp.seek(0x1c, 0)#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32 #存放计算CRC32四个字节 crcstr_2 = crc2hex(crc) r=binascii.unhexlify(crcstr_2) fp.write(r) fp.close() sys.exit(0)##正常退出 代码我就不多解释了，应该还是挺容易看懂的；\n3、然后是对Keil进行配置，实现编译，生成bin文件和加入CRC校验可以一步完成；\n具体配置结果的截图如下：\n配置成功后输出的结果：\n4、此时在工程文件目录下就可以找到.bin文件了：\nBin文件的发送我用的是正点原子的串口助手，配置好波特率什么的就可以用蓝牙发送该bin文件了;\n三、Bootloader部分 BootLoader就是一个引导程序，它的功能就是用串口接收蓝牙模块收到的信息，处理信息，校验信息，最后执行APP程序； 值得注意的是，Bootloader部分程序需要通过st-link或者DAP等调试器下载到单片机flash中；\nBootloader部分程序具体的工作流程是：\n#接收串口收到的信息; #判断是否接收完毕; #提取bin文件中储存的crc32校验码; #清除该校验码，还原初始bin文件; #对该bin文件进行CRC校验得到校验码; #比对两次得到的校验码; #将bin文件写入flash中，跳转到目标地址执行APP程序; 关键的代码如下： CRC校验函数以及校验过程：\ncrc32_temp=USART_RX_BUF[0x1c]*256*256*256 +USART_RX_BUF[0x1c+1]*256*256 +USART_RX_BUF[0x1c+2]*256 +USART_RX_BUF[0x1c+3];//读取crc32的值 for(i=0;i CRC校验函数： ```c uint32_t crc32(unsigned char *buf,uint32_t size) { uint32_t i, crc; crc = 0xFFFFFFFF; for (i = 0; i \u0026gt; 8); } return crc^0xFFFFFFFF; } static const uint32_t crc32tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; 程序加载以及程序跳转函数：\nif(((*(vu32*)(0X20001000+4))\u0026amp;0xFF000000)==0x08000000)//判断是否为0X08XXXXXX. { iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码 printf(\u0026#34;固件更新完成!\\r\\n\u0026#34;); printf(\u0026#34;开始执行FLASH用户代码!!\\r\\n\u0026#34;); if(((*(vu32*)(FLASH_APP1_ADDR+4))\u0026amp;0xFF000000)==0x08000000)//判断是否为0X08XXXXXX. { iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码 crc32_true=0; } else { printf(\u0026#34;非FLASH应用程序,无法执行!\\r\\n\u0026#34;); } } 完整的代码我放在GitHub上边了，欢迎访问。\n该项目的GitHub地址是：https://github.com/fan-pengfei/Stm32_IAP/tree/master\n","permalink":"https://fan-pengfei.top/posts/stm32%E8%93%9D%E7%89%99%E4%B8%B2%E5%8F%A3%E4%B8%8B%E8%BD%BD/","summary":"\u003cblockquote\u003e\n\u003cp\u003e之前准备电赛的时候，需要用到无线调试器，先是上淘宝看了一下，基本都是大几百，所以想着自己自制一个，直接搞DAP无线调试器有点难，所以就用手头现有的蓝牙模块搞了一个串口无线下载器，调试的话就靠printf打印了；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e也做了好几个版本，最初的版本主要有两个大的问题：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、没有程序校验过程，不能保证单片机收到的APP程序是完整且正确的；\n2、没有错误解决机制，一旦APP程序错误或者程序跑飞，就必须手动按下复位键进行复位；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e为解决以上两个问题，采取了以下措施\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、对APP程序加入CRC校验；\n2、加入看门狗，解决程序跑飞问题；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"以下是关于这个项目的具体介绍\"\u003e以下是关于这个项目的具体介绍：\u003c/h2\u003e\n\u003ch3 id=\"一app程序部分\"\u003e一、APP程序部分\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e首先要有一个APP程序，与一般的程序相比，APP程序有两个地方需要进行配置：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e分别如下图所示：\u003c/p\u003e\n\u003cp\u003e1、在system_stm32f4xx.c中进行修改：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/stm32%E8%93%9D%E7%89%99%E4%B8%B2%E5%8F%A3%E4%B8%8B%E8%BD%BD/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e2、在小锤子那里进行修改：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/stm32%E8%93%9D%E7%89%99%E4%B8%B2%E5%8F%A3%E4%B8%8B%E8%BD%BD/img-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e3、修改完毕后，进行正常的编译即可；\u003c/p\u003e\n\u003ch3 id=\"二bin文件的处理\"\u003e二、BIN文件的处理\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e最初的版本并没有加入crc校验，所以经常会出错，导致程序经常跑飞，所以加入了crc32校验；经检验，效果那是非常的好；\n为什么是采用bin文件而不是使用更容易得到的hex文件呢？\n原因很简单：因为写入单片机flash的程序实际就是bin文件，hex文件需要转换才能写进flash中；\n以上操作只是得到了hex文件，而我们需要的是bin文件，以下是对hex文件的处理过程：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1、第一步呢，当然是得到最初的bin文件；而Keil默认情况下编译成功后生成的是Hex文件，而不是bin文件，所以要将Hex文件转换为bin文件，当然是有现成的Hex转bin的程序的，但是比较麻烦，可以用Keil自带的功能完成编译后自动生成bin文件的过程，具体的方法请自行百度；\u003c/p\u003e\n\u003cp\u003e这是我的配置，仅供参考：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eD:\\Keil_v5\\ARM\\ARMCC\\bin\\fromelf.exe —bin -o fromelf —bin -o “$L@L.bin” “#L”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e2、然后是对bin文件进行处理，也就是crc32校验码的生成以及插入该校验码到bin文件中，我用的是python脚本的方式，代码如下：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# -*- coding:utf-8 -*-\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e binascii\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e os\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecrc2hex\u003c/span\u003e(crc):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    res\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        t\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecrc \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0xFF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        crc \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        res\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%02X%s\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e (t, res)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e res\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einputfile \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;E:\\IAP\\APP\\Project\\OBJ\\STM32F407.bin\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#实际存放的bin文件路径\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eisfile \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisfile(inputfile);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(inputfile);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e open(inputfile, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r+b\u0026#34;\u003c/span\u003e)  \u003cspan style=\"color:#75715e\"\u003e#直接打开一个文件，如果文件不存在则创建文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efilesize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egetsize(inputfile)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ZI app firmware size:\u0026#34;\u003c/span\u003e, filesize, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bytes.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#计算bin文件的CRC，首先清空CRC32区域的4个byte\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0x1c\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eclear4bytes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;00000000\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ec4 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ebinascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunhexlify(clear4bytes)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(c4)  \u003cspan style=\"color:#75715e\"\u003e#将CRC32存放的区域4bytes清零\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从0开始读取整个bin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efile_content \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e fp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\u003cspan style=\"color:#75715e\"\u003e#读整个文件内容到 file_content\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecrc \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e binascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecrc32(file_content)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;CRC32:\u0026#39;\u003c/span\u003e, hex(crc))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eseek(\u003cspan style=\"color:#ae81ff\"\u003e0x1c\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e#从bin文件开始，偏移地址为0x1c的地方存放bin的CRC32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#存放计算CRC32四个字节\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecrcstr_2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e crc2hex(crc)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003er\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ebinascii\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunhexlify(crcstr_2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(r)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclose()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esys\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexit(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\u003cspan style=\"color:#75715e\"\u003e##正常退出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e代码我就不多解释了，应该还是挺容易看懂的；\u003c/p\u003e","title":"Stm32蓝牙串口下载"},{"content":" Git相关的知识； 参考链接：https://zhuanlan.zhihu.com/p/94008510\nGit 常用命令 初次使用时，在命令行中配置本地仓库的账号和邮箱：\n$ git config --global user.name \u0026#34;username\u0026#34; $ git config --global user.email \u0026#34;useremail\u0026#34; 初始化Git,使用 cd 命令导航到要在终端中设置版本控制的目录，现在你可以像这样初始化 Git 存储库：\ngit init 要开始对现有文件进行版本控制，你应该先跟踪这些文件并进行初始提交。要做到这一点，你首先需要将文件添加到 Git 中，并将它们附加到 Git 项目中。\ngit add git commit -m \u0026#34;first commit\u0026#34; 还有一些更高级的方法可以将文件添加到 Git 中，从而使你的工作流程更高效。我们可以执行以下操作，而不是试图查找所有有更改的文件并逐个添加它们：\n# 逐个添加文件 git add filename # 添加当前目录中的所有文件 git add -A # 添加当前目录中的所有文件更改 git add . # 选择要添加的更改（你可以 Y 或 N 完成所有更改） git add -p 远程备份文件（Github）,因此，首先转到 http://github.com 并创建一个存储库。然后，使用存储库的链接将其添加为本地 git 项目的来源，即该代码的存储位置；\ngit remote add origin \\ https://github.com/fan-pengfei/bash_learning.git 远程备份代码：\ngit push origin master git status 命令用于确定哪些文件处于哪种状态，它使你可以查看哪些文件已提交，哪些文件尚未提交。如果在所有文件都已提交并推送后运行此命令，则应该看到类似以下内容：\n$ git status # On branch master nothing to commit (working directory clean) 我们可以使用 git commit -m '提交信息' 来将文件提交到 Git。对于提交简短消息来说，这一切都很好，但是如果你想做一些更精细的事情，你需要来学习更多的操作:\n### 提交暂存文件，通常用于较短的提交消息 git commit -m \u0026#39;commit message\u0026#39; ### 添加文件并提交一次 git commit filename -m \u0026#39;commit message\u0026#39; ### 添加文件并提交暂存文件 git commit -am \u0026#39;insert commit message\u0026#39; ### 更改你的最新提交消息 git commit --amend \u0026#39;new commit message\u0026#39; # 将一系列提交合并为一个提交，你可能会用它来组织混乱的提交历史记录 git rebase -i ### 这将为你提供核心编辑器上的界面： # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like \u0026#34;squash\u0026#34;, but discard this commit\u0026#39;s log message # x, exec = run command (the rest of the line) using shell GitHub存储库的master分支应始终包含有效且稳定的代码。但是，你可能还希望备份一些当前正在处理的代码，但这些代码并不完全稳定。也许你要添加一个新功能，你正在尝试和破坏很多代码，但是你仍然希望保留备份以保存进度！ 分支使你可以在不影响master分支的情况下处理代码的单独副本。首次创建分支时，将以新名称创建master分支的完整克隆。然后，你可以独立地在此新分支中修改代码，包括提交文件等。一旦你的新功能已完全集成并且代码稳定，就可以将其合并到master分支中！\n### 创建一个本地分支 git checkout -b branchname ### 在2个分支之间切换 git checkout prc/dev-wupx git checkout master ### 将新的本地分支作为备份 git push -u origin branch_2 ### 删除本地分支，这不会让你删除尚未合并的分支 git branch -d branch_2 ### 删除本地分支，即使尚未合并，这也会删除该分支！ git branch -D branch_2 ### Viewing all current branches for the repository, including both ### local and remote branches. Great to see if you already have a ### branch for a particular feature addition, especially on bigger ### projects ### 查看存储库的所有当前分支，包括本地和远程分支。 git branch -a ### 查看已合并到您当前分支中的所有分支，包括本地和远程。 非常适合查看所有代码的来源！ git branch -a --merged ### 查看尚未合并到当前分支中的所有分支，包括本地和远程 git branch -a --no-merged ### 查看所有本地分支 git branch ### 查看所有远程分支 git branch -r # 将主分支重新设置为本地分支 $ git rebase origin/master # 将分支推送到远程存储库源并对其进行跟踪 $ git push origin branchname 将新功能添加到分支中之后，你需要将其合并回master分支，以便您的master具有所有最新的代码功能。 方法如下：\n### 首先确保你正在查看 master 分支 git checkout master ### 现在将你的分支合并到 master git merge prc/dev-wupx 你可能必须修复分支与主服务器之间的任何代码冲突，但是 Git 将向你展示在键入该 merge 命令后如何执行所有这些操作。 当有错误发生时，Git 提供了你所需的一切，以防你在所推送的代码中犯错，改写某些内容或者只是想对所推送的内容进行更正。\n### 切换到最新提交的代码版本 git reset HEAD git reset HEAD -- filename # for a specific file ### 切换到最新提交之前的代码版本 git reset HEAD^ -- filename git reset HEAD^ -- filename # for a specific file ### 切换回3或5次提交 git reset HEAD~3 -- filename git reset HEAD~3 -- filename # for a specific file git reset HEAD~5 -- filename git reset HEAD~5 -- filename # for a specific file ### 切换回特定的提交，其中 0766c053 为提交 ID git reset 0766c053 -- filename git reset 0766c053 -- filename # for a specific file ### 先前的命令是所谓的软重置。 你的代码已重置，但是git仍会保留其他代码的副本，以备你需要时使用。 另一方面，--hard 标志告诉Git覆盖工作目录中的所有更改。 git reset --hard 0766c053 有用的技巧 搜索 ### 搜索目录中的字符串部分 git grep \u0026#39;project\u0026#39; ### 在目录中搜索部分字符串，-n 打印出 git 找到匹配项的行号 git grep -n \u0026#39;project\u0026#39; ### git grep -C \u0026#39;something\u0026#39; 搜索带有某些上下文的字符串部分（某些行在我们正在寻找的字符串之前和之后） git grep -C \u0026#39;project\u0026#39; ### 搜索字符串的一部分，并在字符串之前显示行 git grep -B \u0026#39;project\u0026#39; ### 搜索字符串的一部分，并在字符串之后显示行 git grep -A \u0026#39;something\u0026#39; 看谁写了什么 ### 显示带有作者姓名的文件的更改历史记录 git blame \u0026#39;filename\u0026#39; ### 显示带有作者姓名和 git commit ID 的文件的更改历史记录 git blame \u0026#39;filename\u0026#39; -l 日志 ### 显示存储库中所有提交的列表 该命令显示有关提交的所有信息，例如提交ID，作者，日期和提交消息 git log ### 提交列表仅显示提交消息和更改 git log -p ### 包含您要查找的特定字符串的提交列表 git log -S \u0026#39;project\u0026#39; ### 作者提交的清单 git log --author \u0026#39;wupx\u0026#39; ### 显示存储库中提交列表的摘要。显示提交ID和提交消息的较短版本。 git log --oneline ### 显示昨天以来仓库中的提交列表 git log --since=yesterday ### 显示作者日志，并在提交消息中搜索特定术语 git log --grep \u0026#34;project\u0026#34; --author \u0026#34;wupx\u0026#34; 每次提交不用重复输入账号和密码 参考链接：https://zhuanlan.zhihu.com/p/81334170\n1、验证是否真的使用的是https方式\n使用命令：\ngit remote -v 确定是https方式；\n2、修改https的方式为ssh方式\n移除当前关联的远程仓库\ngit remote rm origin 添加新的ssh地址\ngit remote add origin ssh地址 3、再次提交\ngit push origin master 这次没有再提示输入密码\n报错解决办法 当出错：! [rejected] master -\u0026gt; master (fetch first) error: failed to push some refs to ‘ 。。。’\n出现这个问题是因为github中的README.md文件不在本地代码目录中，可以通过如下命令进行代码合并\ngit pull --rebase origin master ","permalink":"https://fan-pengfei.top/posts/git%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","summary":"\u003cblockquote\u003e\n\u003cp\u003eGit相关的知识；\n参考链接：\u003ca href=\"https://zhuanlan.zhihu.com/p/94008510\"\u003ehttps://zhuanlan.zhihu.com/p/94008510\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"git-常用命令\"\u003eGit 常用命令\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e初次使用时，在命令行中配置本地仓库的账号和邮箱：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ git config --global user.name \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ git config --global user.email \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;useremail\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e初始化Git,使用 cd 命令导航到要在终端中设置版本控制的目录，现在你可以像这样初始化 Git 存储库：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit init\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e要开始对现有文件进行版本控制，你应该先跟踪这些文件并进行初始提交。要做到这一点，你首先需要将文件添加到 Git 中，并将它们附加到 Git 项目中。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit commit -m \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;first commit\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e还有一些更高级的方法可以将文件添加到 Git 中，从而使你的工作流程更高效。我们可以执行以下操作，而不是试图查找所有有更改的文件并逐个添加它们：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 逐个添加文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add filename\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 添加当前目录中的所有文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add -A\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 添加当前目录中的所有文件更改\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 选择要添加的更改（你可以 Y 或 N 完成所有更改）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add -p\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e远程备份文件（Github）,因此，首先转到 \u003ca href=\"https://link.zhihu.com/?target=http%3A//github.com\"\u003ehttp://github.com\u003c/a\u003e 并创建一个存储库。然后，使用存储库的链接将其添加为本地 git 项目的来源，即该代码的存储位置；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit remote add origin \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehttps://github.com/fan-pengfei/bash_learning.git\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e远程备份代码：\u003c/p\u003e","title":"Git学习记录"},{"content":" Typora问题记录；\n图片上传出错问题解决办法： 1、名字重复 2、服务器端口出错 3、图床owner\\repo填写出错\n","permalink":"https://fan-pengfei.top/posts/typora%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0%E5%87%BA%E9%94%99/","summary":"\u003cblockquote\u003e\n\u003cp\u003eTypora问题记录；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"图片上传出错问题解决办法\"\u003e图片上传出错问题解决办法：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e1、名字重复\n2、服务器端口出错\n3、图床owner\\repo填写出错\u003c/p\u003e\n\u003c/blockquote\u003e","title":"typora图片上传出错"},{"content":" 常用bat脚本；\nWindows bat脚本： 依次执行多条命令，并且执行完执行完毕并不退出：\n/*upload.bat:博客自动上传脚本*/ call hexo clean // call hexo g //博客生成 call hexo d //博客上传 pause //页面暂停 同时执行多条命令，并且执行完执行完毕并不退出（以下例子仅仅说明语法，并不代表可用）：\n/*upload.bat:博客自动上传脚本*/ start hexo clean // start hexo g //博客生成 start hexo d //博客上传 pause //页面暂停 脚本运行结果：\n","permalink":"https://fan-pengfei.top/posts/windows10-bat%E8%84%9A%E6%9C%AC/","summary":"\u003cblockquote\u003e\n\u003cp\u003e常用bat脚本；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"windows-bat脚本\"\u003eWindows bat脚本：\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e依次执行多条命令，并且执行完执行完毕并不退出：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/*upload.bat:博客自动上传脚本*/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecall hexo clean //\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecall hexo g //博客生成\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecall hexo d //博客上传\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epause //页面暂停\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e同时执行多条命令，并且执行完执行完毕并不退出（以下例子仅仅说明语法，并不代表可用）：\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/*upload.bat:博客自动上传脚本*/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estart hexo clean //\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estart hexo g //博客生成\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estart hexo d //博客上传\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epause //页面暂停\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e脚本运行结果：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/posts/windows10-bat%E8%84%9A%E6%9C%AC/img-1.png\"\u003e\u003c/p\u003e","title":"Windows10 Bat脚本"},{"content":" 搭建自己的博客；\n缘起 一直想着要搭建一个属于自己的博客，感觉这样子很酷很酷，也是正好记录一下自己学习中遇到的问题，就当做一个笔记本来用吧！\n原先想着用树莓派做服务器就可以搭建自己的的个人网站用来写博客，先在阿里云上购买了域名，其实最后是买了两个：\nfan-pengfei.top fan-pengfei.xyz 为啥买了两个呢？\n波折 其实是我自己太粗心大意了，在二月份的时候自己就买了.xyz这一个域名，后来忙其他的事就把这件事搁置了；注册新域名的时候发现这个已经被注册了（没想到是自己之前注册的），所以没办法，只能感慨与自己同名同姓的人真多，然后就注册了.top域名;\n注册完才发现，自己的域名控制台上竟然有两个域名，这才让我想起尘封已久的记忆，不过头一个快过期了，就用第二个搭建了这个个人网站；\nhttps://fan-pengfei.top\n前两天闲来无事，就又想折腾一下搭建自己博客的事；找了很多资料，终于还是将这个博客搭建起来了，挺简约的，自己很喜欢，毕竟博客就是用来记录自己学习到的知识，所以博客的内容应该更加重要。\n步骤 一、配置Github 首先注册、登录： https://github.com/\n记住自己的Username（很重要）；\n然后右上角选择 Create a new repository；\nRepository name -\u0026gt;填自己的名字, yourname.github.io-\u0026gt;这个就是你博客的域名了(yourname与你的注册用户名一致)；\n例如，我的域名是github.com/fan-pengfei，就填入fan-pengfei.github.io；\n二、配置环境 安装 Node.js： https://nodejs.org/en/\n安装 Git： https://github.com/waylau/git-for-win\n安装完成后，在开始菜单里找到Git-\u0026gt;Git Bash，打开，并依次执行以下命令：\ngit config --global user.name \u0026#34;username\u0026#34; git config --global user.email \u0026#34;useremail\u0026#34; 其中名称和邮箱都是Github注册时自己的名字和邮箱；\n安装 Hexo，所有必备的应用程序安装完成后，即可使用 npm 安装 Hexo:\nnpm install -g hexo-cli 至此环境安装完毕（推荐使用cmder，超级好用的）;\n三、电脑设置 在电脑E盘（自己随意）目录下新建文件夹my_blog，进入my_blog，按住Shift键点击鼠标右键，选择Cmder Here；因为我有安装Cmder，没有安装的点击“在此处打开命令窗口”，输入：\nhexo init blog 稍微等待下，速度有点慢，成功后将提示：\nINFO Start blogging with Hexo! 重新打开CMD，输入：\nssh-keygen -t rsa -C \u0026#34;Github的注册邮箱地址\u0026#34; 一路Enter过来就好，得到信息：\nYour public key has been saved in /c/Users/user/.ssh/id_rsa.pub. 找到该文件，打开（sublime text），Ctrl + a复制里面的所有内容，然后进入Sign in to GitHub：https://github.com/settings/ssh\nNew SSH key -\u0026gt;Title，blog -\u0026gt;Key：输入刚才复制的—— Add SSH key；\n四、配置博客 在blog目录下，用Notepad++或者电脑自带的记事本打开_config.yml文件（我的路径是E:\\my_blog\\blog），修改参数信息；\n特别注意的是，在每个参数之后的：之后都要加上一个空格，否则一定会报错；\n修改网站相关信息：\ntitle: 小飞的博客 subtitle: 副标题 description: 网页描述 author: 小飞 language: zh-CN timezone: Asia/Changsha 配置部署（我的是fan-pengfei，修改成自己的）：\ndeploy: type: git repo: https://github.com/fan-pengfei/fan-pengfei.github.io.git branch: main 五、发表文章 在命令行窗口中输入：\nhexo new \u0026#34;测试文章\u0026#34; INFO Created: E:\\my_blog\\blog\\source\\_posts\\测试文章.md 找到该文章，打开，使用Markdown语法编辑即可；\n保存，然后执行下列步骤：\nE:\\my_blog\\blog $ hexo clean INFO Deleted database. INFO Deleted public folder. E:\\my_blog\\blog $ hexo generate INFO Start processing INFO Files loaded in 1.48 s #省略 INFO 29 files generated in 4.27 s 最后一步，发布到网上，执行：\nF:\\test\\blog $ hexo deploy INFO Deploying: git INFO Clearing .deploy_git folder... INFO Copying files from public folder... #省略 其中会跳出Github登录，直接登录即可；\n六、总结 发布文章的步骤：\n1、hexo new 创建文章；\n2、Markdown语法编辑文章；\n3、部署（所有打开CMD都是在blog目录下）；\nhexo clean #清除缓存 网页正常情况下可以忽略此条命令 hexo generate #生成 hexo server #启动服务预览，非必要，可本地浏览网页 hexo deploy #部署发布 常用命令简写Tips：\nhexo n “我的博客” == hexo new “我的博客” #新建文章hexo g == hexo generate#生成hexo s == hexo server #启动服务预览hexo d == hexo deploy#部署\n如果在执行 hexo deploy 后,出现error deployer not found:github的错误，执行：\nnpm install hexo-deployer-git --save 七、其他 若想让让博客更美观，可以更换其他主题，我使用的Yilia主题具体安装步骤请参考：https://github.com/litten/hexo-theme-yilia、\n删除增加图片：\n是在E:\\my_blog\\blog\\themes\\yilia\\source\\assets文件夹下\n我的博客截图：\n","permalink":"https://fan-pengfei.top/posts/hexo+yilia%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e搭建自己的博客；\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"缘起\"\u003e缘起\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e一直想着要搭建一个属于自己的博客，感觉这样子很酷很酷，也是正好记录一下自己学习中遇到的问题，就当做一个笔记本来用吧！\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e原先想着用树莓派做服务器就可以搭建自己的的个人网站用来写博客，先在阿里云上购买了域名，其实最后是买了两个：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efan-pengfei.top\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efan-pengfei.xyz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e为啥买了两个呢？\u003c/p\u003e\n\u003ch1 id=\"波折\"\u003e波折\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e其实是我自己太粗心大意了，在二月份的时候自己就买了.xyz这一个域名，后来忙其他的事就把这件事搁置了；注册新域名的时候发现这个已经被注册了（没想到是自己之前注册的），所以没办法，只能感慨与自己同名同姓的人真多，然后就注册了.top域名;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e注册完才发现，自己的域名控制台上竟然有两个域名，这才让我想起尘封已久的记忆，不过头一个快过期了，就用第二个搭建了这个个人网站；\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://fan-pengfei.top\"\u003ehttps://fan-pengfei.top\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e前两天闲来无事，就又想折腾一下搭建自己博客的事；找了很多资料，终于还是将这个博客搭建起来了，挺简约的，自己很喜欢，毕竟博客就是用来记录自己学习到的知识，所以博客的内容应该更加重要。\u003c/p\u003e\n\u003ch1 id=\"步骤\"\u003e步骤\u003c/h1\u003e\n\u003ch2 id=\"一配置github\"\u003e一、配置Github\u003c/h2\u003e\n\u003cp\u003e首先注册、登录： \u003ca href=\"https://github.com/\"\u003ehttps://github.com/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e记住自己的Username（很重要）；\u003c/p\u003e\n\u003cp\u003e然后右上角选择 Create a new repository；\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eRepository name\u003c/code\u003e -\u0026gt;填自己的名字, \u003ccode\u003eyourname.github.io\u003c/code\u003e-\u0026gt;这个就是你博客的域名了(yourname与你的注册用户名一致)；\u003c/p\u003e\n\u003cp\u003e例如，我的域名是\u003ccode\u003egithub.com/fan-pengfei\u003c/code\u003e，就填入\u003ccode\u003efan-pengfei.github.io\u003c/code\u003e；\u003c/p\u003e\n\u003ch2 id=\"二配置环境\"\u003e二、配置环境\u003c/h2\u003e\n\u003cp\u003e安装 Node.js： \u003ca href=\"https://nodejs.org/en/\"\u003ehttps://nodejs.org/en/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e安装 Git： \u003ca href=\"https://github.com/waylau/git-for-win\"\u003ehttps://github.com/waylau/git-for-win\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e安装完成后，在开始菜单里找到Git-\u0026gt;Git Bash，打开，并依次执行以下命令：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global user.name \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global user.email \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;useremail\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e其中名称和邮箱都是Github注册时自己的名字和邮箱；\u003c/p\u003e\n\u003cp\u003e安装 Hexo，所有必备的应用程序安装完成后，即可使用 npm 安装 Hexo:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enpm install -g hexo-cli\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e至此环境安装完毕（推荐使用cmder，超级好用的）;\u003c/p\u003e\n\u003ch2 id=\"三电脑设置\"\u003e三、电脑设置\u003c/h2\u003e\n\u003cp\u003e在电脑E盘（自己随意）目录下新建文件夹my_blog，进入my_blog，按住Shift键点击鼠标右键，选择\u003ccode\u003eCmder Here\u003c/code\u003e；因为我有安装Cmder，没有安装的点击“在此处打开命令窗口”，输入：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehexo init blog\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e稍微等待下，速度有点慢，成功后将提示：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eINFO  Start blogging with Hexo!\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e重新打开CMD，输入：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh-keygen -t rsa -C \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Github的注册邮箱地址\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e一路Enter过来就好，得到信息：\u003c/p\u003e","title":"Hexo+Yilia搭建自己的博客"},{"content":" 荔枝派使用记录；\n","permalink":"https://fan-pengfei.top/posts/%E6%9E%84%E5%BB%BA%E8%8D%94%E6%9E%9D%E6%B4%BEzero%E5%AE%8C%E6%95%B4%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8A%E8%BF%90%E8%A1%8Cqt%E7%A8%8B%E5%BA%8F/","summary":"\u003cblockquote\u003e\n\u003cp\u003e荔枝派使用记录；\u003c/p\u003e\n\u003c/blockquote\u003e","title":"构建荔枝派zero完整系统以及运行QT程序"},{"content":" 自从前几个月学了51单片机和stm32，我就一直想着做一个自己的作品，后来思来想去，就产生了做一个小型钟表的想法。既然是自己的作品，那么从设计电路板，到焊接电子元件，再到程序的编写，再到最后的调试和Debug，都应该是自己独立完成。想法有了，然后就开始动手制作。\n因为之前在焊洞洞板的时候，发现精细的电路是很难用洞洞板焊接的，所以就开始在网上找资料学习画PCB电路板，刚开始是找的凡亿教育的视频来看，虽然讲的很好，但是好像不太适合我这种刚入门的小白，在我苦苦盯着视频研究了大半天却一脸懵之后，我最后决定还是自己摸索，不懂的，直接问度娘，不得不说，度娘真的帮了我好多。\n然后又花了半天时间发现问题，解决问题，然后就突然什么都明白了，也理解了元件库，封装库，原理图和pcb文件之间的关系，也学会了自己画封装库和元件库，一切好像都是水到渠成。最后就又花了几天的时间绘制并打样了我的第一块电路板（不得不说，嘉立创的五元包邮的板子真的太香了）。\n这个板子很简单，却为小一的诞生奠定了基础。\n画了这个小核心板之后，就开始画小一了，因为需要先确定下所使用的芯片，所以在画板子之前，我开始选择所需要的芯片，最后决定主控芯片选择宏晶公司的stc15w408as，因为这个芯片小体积价格便宜却有着强大的功能，而且外围电路也极其简单。\n考虑到我想要实现的功能，又选择了Ds1302时钟芯片，LM75a温度测量芯片，微型蜂鸣器，纽扣电池，以及实现程序下载的CH340G芯片。\n画了很久才终于画出了pcb电路图，主要是布线和器件布局太麻烦，一动不动画了好几个下午，最后终于成功了，便把pcb文件发给嘉立创，几天后，元件和电路板几乎同时到了，让我很不开心的是，等我收到板子之后，才发现板子有很多的错误，比如开关选择的型号不对；电路板上丝印很不清晰，甚至都重合在一起，无法辨识；有些线还连接有错误，我很是沮丧，最后我只好用来练习焊接贴片元件，物尽其用。\n然后就是修改原理图和pcb文件，然后把pcb文件又一次发个嘉立创，又过了几天，板子才到。我迫不及待的开始焊接，好难啊，即使我已经买了936焊台，但是因为我贴片元件焊接的经验实在是太少了，所以还是焊了好长的时间，最后终于焊好了，我在内心祈祷着，很怕电源一接通，就会短路，板子会烧掉。结果还好，电源指示灯成功点亮，然后就开始尝试下载程序，结果却是失败，最后又找了好久才发现问题所在。原因是我没有考虑到单片机下载程序时需要冷启动，最后又飞了好几根线，才终于下载成功。开心爆了！！！\n然后又依次下载了一些测试程序，结果又发现了一些问题，比如走时不太准，纽扣电池掉电太快，又想了好久才解决这些问题。走时不准是因为我的手上有静电，会使走时受到干扰；纽扣电池掉电太快，是因为有一根线连错了，然后又飞了一根线，加了两个电容，然后终于可以了，可以实现所有的功能了。这些事情说着简单，其实真的很难，那些问题都太奇怪了，网上根本找不到资料，都是一点点试出来的，太难了。然后就是漫长的软件开发过程了，这可是比硬件更难的啊。\n软件开发又花了好长的时间，写了好久，然后终于可以写好了，现在当然还是有一些bug，不过都是小问题，大概的已经OK了。程序加起来有好几千行，一个模块是一个C文件和H文件，模块化程序，可以让以后移植程序变得很方便。最后大概实现以下功能：\n实时时钟（可以实现掉电不掉时） 温度测量（分辨率0.125℃，测量范围:-55℃～+125℃） 可设置闹钟（也可作为定时器，可设置的时间范 围:1s～24小时，到设定时间有蜂鸣器提醒） 秒表（最小精度1ms，可中途暂停，可清零） 电压表（范围:0～5V,精确度:10位精度AD转换）（改程序ing，因为有bug） 基本不可能有其他功能了，因为STC15W408AS只有8kflash,而现在的程序文件已经7.56k了，只能等以后换用更强大的芯片，才能有更多的功能了。演示视频我放在下面了，也算是比较成功吧，我还是超级满意的呀。\n因为飞线很不美观，所以我打算升级小一，PCB电路图已经画好了，应该是不会再有前两版的那些错误了，而且加入了新的功能，比如可以用锂电池供电，给锂电池充电等功能。\n哈哈，超开心，小一是我真正意义上的第一件作品，希望我以后会有更多的作品，小一会有更多的兄弟姐妹，也会升级小一，给他更强大的大脑，拥有更多的功能。\n有兴趣的兄弟姐妹，可以跟我交流，我可以帮你们解决一些我力所能及的问题。\n","permalink":"https://fan-pengfei.top/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/","summary":"\u003cblockquote\u003e\n\u003cp\u003e自从前几个月学了51单片机和stm32，我就一直想着做一个自己的作品，后来思来想去，就产生了做一个小型钟表的想法。既然是自己的作品，那么从设计电路板，到焊接电子元件，再到程序的编写，再到最后的调试和Debug，都应该是自己独立完成。想法有了，然后就开始动手制作。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e因为之前在焊洞洞板的时候，发现精细的电路是很难用洞洞板焊接的，所以就开始在网上找资料学习画PCB电路板，刚开始是找的凡亿教育的视频来看，虽然讲的很好，但是好像不太适合我这种刚入门的小白，在我苦苦盯着视频研究了大半天却一脸懵之后，我最后决定还是自己摸索，不懂的，直接问度娘，不得不说，度娘真的帮了我好多。\u003c/p\u003e\n\u003cp\u003e然后又花了半天时间发现问题，解决问题，然后就突然什么都明白了，也理解了元件库，封装库，原理图和pcb文件之间的关系，也学会了自己画封装库和元件库，一切好像都是水到渠成。最后就又花了几天的时间绘制并打样了我的第一块电路板（不得不说，嘉立创的五元包邮的板子真的太香了）。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-2.jpg\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e这个板子很简单，却为小一的诞生奠定了基础。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e画了这个小核心板之后，就开始画小一了，因为需要先确定下所使用的芯片，所以在画板子之前，我开始选择所需要的芯片，最后决定主控芯片选择宏晶公司的stc15w408as，因为这个芯片小体积价格便宜却有着强大的功能，而且外围电路也极其简单。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e考虑到我想要实现的功能，又选择了Ds1302时钟芯片，LM75a温度测量芯片，微型蜂鸣器，纽扣电池，以及实现程序下载的CH340G芯片。\u003c/p\u003e\n\u003cp\u003e画了很久才终于画出了pcb电路图，主要是布线和器件布局太麻烦，一动不动画了好几个下午，最后终于成功了，便把pcb文件发给嘉立创，几天后，元件和电路板几乎同时到了，让我很不开心的是，等我收到板子之后，才发现板子有很多的错误，比如开关选择的型号不对；电路板上丝印很不清晰，甚至都重合在一起，无法辨识；有些线还连接有错误，我很是沮丧，最后我只好用来练习焊接贴片元件，物尽其用。\u003c/p\u003e\n\u003cp\u003e然后就是修改原理图和pcb文件，然后把pcb文件又一次发个嘉立创，又过了几天，板子才到。我迫不及待的开始焊接，好难啊，即使我已经买了936焊台，但是因为我贴片元件焊接的经验实在是太少了，所以还是焊了好长的时间，最后终于焊好了，我在内心祈祷着，很怕电源一接通，就会短路，板子会烧掉。结果还好，电源指示灯成功点亮，然后就开始尝试下载程序，结果却是失败，最后又找了好久才发现问题所在。原因是我没有考虑到单片机下载程序时需要冷启动，最后又飞了好几根线，才终于下载成功。开心爆了！！！\u003c/p\u003e\n\u003cp\u003e然后又依次下载了一些测试程序，结果又发现了一些问题，比如走时不太准，纽扣电池掉电太快，又想了好久才解决这些问题。走时不准是因为我的手上有静电，会使走时受到干扰；纽扣电池掉电太快，是因为有一根线连错了，然后又飞了一根线，加了两个电容，然后终于可以了，可以实现所有的功能了。这些事情说着简单，其实真的很难，那些问题都太奇怪了，网上根本找不到资料，都是一点点试出来的，太难了。然后就是漫长的软件开发过程了，这可是比硬件更难的啊。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e软件开发又花了好长的时间，写了好久，然后终于可以写好了，现在当然还是有一些bug，不过都是小问题，大概的已经OK了。程序加起来有好几千行，一个模块是一个C文件和H文件，模块化程序，可以让以后移植程序变得很方便。最后大概实现以下功能：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e实时时钟（可以实现掉电不掉时）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e温度测量（分辨率0.125℃，测量范围:-55℃～+125℃）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e可设置闹钟（也可作为定时器，可设置的时间范 围:1s～24小时，到设定时间有蜂鸣器提醒）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e秒表（最小精度1ms，可中途暂停，可清零）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e电压表（范围:0～5V,精确度:10位精度AD转换）（改程序ing，因为有bug）\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BD%9C%E5%93%81%E5%B0%8F%E4%B8%80/img-8.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e基本不可能有其他功能了，因为STC15W408AS只有8kflash,而现在的程序文件已经7.56k了，只能等以后换用更强大的芯片，才能有更多的功能了。演示视频我放在下面了，也算是比较成功吧，我还是超级满意的呀。\u003c/p\u003e\n\u003cp\u003e因为飞线很不美观，所以我打算升级小一，PCB电路图已经画好了，应该是不会再有前两版的那些错误了，而且加入了新的功能，比如可以用锂电池供电，给锂电池充电等功能。\u003c/p\u003e\n\u003cp\u003e哈哈，超开心，小一是我真正意义上的第一件作品，希望我以后会有更多的作品，小一会有更多的兄弟姐妹，也会升级小一，给他更强大的大脑，拥有更多的功能。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e有兴趣的兄弟姐妹，可以跟我交流，我可以帮你们解决一些我力所能及的问题。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"我的第一个作品——小一"},{"content":" 上大学后，第一次接触到单片机和电路设计，我便觉得这将是我一生所爱。\n从小学到初中，我一直都算是一个爱折腾的小孩子，对什么都特别的有兴趣，拆过很多东西，电器，玩具，不计其数。那时候，什么也不懂，就自己折腾着玩。\n记得有一段时间，自己很想有一架自己的遥控飞机，刚开始就觉得有翅膀，有电机，就能飞起来，甚至还用塑料片子和泡沫做了一架原型，最后当然失败了，然后又自己用手机在网上搜索，才明白原来做一个遥控飞机是那么的复杂，要有舵机，起落架，能产生升力的机翼，还要有无刷电机，遥控装置，这些对于当时的我来说都是遥不可及的东西，最后这个做飞机的事情也不了了之了。\n到了初三的时候，用仅有的钱在淘宝上买了无刷电机和电调，但是却无论如何无法让电机转起来，一直不知道是什么原因，后来就上网上找资料，最后发现是缺少一个控制的东西，然后就在淘宝买了一个舵机测试仪，最后终于转起来了，（直到不久前，我才明白舵机控制仪的原理，是通过旋钮，来控制输出一定占空比的脉冲，然后当做信号驱动电机或者舵机，用来控制电机转速，或者说舵机转动的角度，前几天我已经用单片机输出脉冲成功驱动我买到的舵机。）当时真的是激动极了，而且那无刷电机的转速超过我玩过的任何一个电机。当时拍的视频，现在还能找到。\n后来有一段时间又迷上了土豆炮，是偶然间在网上看见的，然后就开始自己制作，原理很简单，就是在密闭容器内放入可燃气体，然后用电火花将气体点燃即可。燃料燃烧，气体膨胀，就会将填充的土豆发射出去，威力挺大。第一次做就做成功了，土豆炮弹的威力让我很是满意。后来改进了好多的版本，有迷你版本的，是用打火机做的，特别小巧，用花露水做为燃料，可以发射牙签或者火柴，也能飞个好几米。也有比较大的，可以发射干电池，用杀虫剂喷雾作为燃料，威力很大，近距离甚至可以打碎数厘米的瓦片，就是发射速度比较慢，因为燃料和炮弹都是手工装填，直到现在这个问题我也没有找到解决办法。\n这些东西都很有意思，我在不懂任何电路知识的时候，甚至自己做过一个三极管自激电路，可以用来产生震荡，然后驱动变压器，或者一个超短距离的无线输电，当时根本不懂为什么，只是按照网上的接线方法，找一些相似的元件，然后连接起来就可以了，成功当然会让我很开心，失败也不会让我很沮丧，只是觉得好玩罢了。可能实践有助于知识的掌握理解，前些天我学习三极管的知识的时候，很快就能理解三极管的原理和应用。\n在初中的时候，因为是留级生，学习压力很小很小，然后就开始做了很多的小手枪，有用橡皮筋作为动力的，有用弹簧作为动力的，甚至还有一个用磁铁作为动力的。当时我的想法真的是天马行空，好多好多奇奇怪怪的想法。因为有时候会拆很多东西，不懂的就查资料，也学得了很多奇奇怪怪的知识，比如饮水机里的制冷片，对的，饮水机制冷的原理跟冰箱完全不同，饮水机用的是半导体制冷片，只要通电，就能一面发热，一面制冷，特别神奇。而且可以根据温差发电，这些我都验证过，真的是太神奇了。还有什么焦耳小偷，ZVS电路，斯特林发动机等等，可能我后来对电子一类的感兴趣就跟这有很大的关系吧，因为这些东西真的有趣。\n因为爱折腾，也做过好多傻事，比如我知道502的味道，有点甜，知道花露水的味道，很上头；在有一次做水火箭的时候，压力过大，然后瓶盖被崩出来，正对着我的脑门来了一下，肿了好几天。胳膊被烧过好几次，汗毛烧了又长，长了又烧，可能也习惯了，头发也被烧过两次，还好最后都长回来了。\n上了大学，学了自动化这个专业，好像跟我的兴趣挺符合的，又入了单片机这个坑，感觉很棒，兴趣能成为工作当然是很棒的一件事。\n这是我的第一篇文章，以后，我会写一些我学习的过程，以及学习中的一些问题和思考，会一直写的，因为一直在学习。\n","permalink":"https://fan-pengfei.top/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e上大学后，第一次接触到单片机和电路设计，我便觉得这将是我一生所爱。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e从小学到初中，我一直都算是一个爱折腾的小孩子，对什么都特别的有兴趣，拆过很多东西，电器，玩具，不计其数。那时候，什么也不懂，就自己折腾着玩。\u003c/p\u003e\n\u003cp\u003e记得有一段时间，自己很想有一架自己的遥控飞机，刚开始就觉得有翅膀，有电机，就能飞起来，甚至还用塑料片子和泡沫做了一架原型，最后当然失败了，然后又自己用手机在网上搜索，才明白原来做一个遥控飞机是那么的复杂，要有舵机，起落架，能产生升力的机翼，还要有无刷电机，遥控装置，这些对于当时的我来说都是遥不可及的东西，最后这个做飞机的事情也不了了之了。\u003c/p\u003e\n\u003cp\u003e到了初三的时候，用仅有的钱在淘宝上买了无刷电机和电调，但是却无论如何无法让电机转起来，一直不知道是什么原因，后来就上网上找资料，最后发现是缺少一个控制的东西，然后就在淘宝买了一个舵机测试仪，最后终于转起来了，（直到不久前，我才明白舵机控制仪的原理，是通过旋钮，来控制输出一定占空比的脉冲，然后当做信号驱动电机或者舵机，用来控制电机转速，或者说舵机转动的角度，前几天我已经用单片机输出脉冲成功驱动我买到的舵机。）当时真的是激动极了，而且那无刷电机的转速超过我玩过的任何一个电机。当时拍的视频，现在还能找到。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/img-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e后来有一段时间又迷上了土豆炮，是偶然间在网上看见的，然后就开始自己制作，原理很简单，就是在密闭容器内放入可燃气体，然后用电火花将气体点燃即可。燃料燃烧，气体膨胀，就会将填充的土豆发射出去，威力挺大。第一次做就做成功了，土豆炮弹的威力让我很是满意。后来改进了好多的版本，有迷你版本的，是用打火机做的，特别小巧，用花露水做为燃料，可以发射牙签或者火柴，也能飞个好几米。也有比较大的，可以发射干电池，用杀虫剂喷雾作为燃料，威力很大，近距离甚至可以打碎数厘米的瓦片，就是发射速度比较慢，因为燃料和炮弹都是手工装填，直到现在这个问题我也没有找到解决办法。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/img-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e这些东西都很有意思，我在不懂任何电路知识的时候，甚至自己做过一个三极管自激电路，可以用来产生震荡，然后驱动变压器，或者一个超短距离的无线输电，当时根本不懂为什么，只是按照网上的接线方法，找一些相似的元件，然后连接起来就可以了，成功当然会让我很开心，失败也不会让我很沮丧，只是觉得好玩罢了。可能实践有助于知识的掌握理解，前些天我学习三极管的知识的时候，很快就能理解三极管的原理和应用。\u003c/p\u003e\n\u003cp\u003e在初中的时候，因为是留级生，学习压力很小很小，然后就开始做了很多的小手枪，有用橡皮筋作为动力的，有用弹簧作为动力的，甚至还有一个用磁铁作为动力的。当时我的想法真的是天马行空，好多好多奇奇怪怪的想法。因为有时候会拆很多东西，不懂的就查资料，也学得了很多奇奇怪怪的知识，比如饮水机里的制冷片，对的，饮水机制冷的原理跟冰箱完全不同，饮水机用的是半导体制冷片，只要通电，就能一面发热，一面制冷，特别神奇。而且可以根据温差发电，这些我都验证过，真的是太神奇了。还有什么焦耳小偷，ZVS电路，斯特林发动机等等，可能我后来对电子一类的感兴趣就跟这有很大的关系吧，因为这些东西真的有趣。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/img-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e因为爱折腾，也做过好多傻事，比如我知道502的味道，有点甜，知道花露水的味道，很上头；在有一次做水火箭的时候，压力过大，然后瓶盖被崩出来，正对着我的脑门来了一下，肿了好几天。胳膊被烧过好几次，汗毛烧了又长，长了又烧，可能也习惯了，头发也被烧过两次，还好最后都长回来了。\u003c/p\u003e\n\u003cp\u003e上了大学，学了自动化这个专业，好像跟我的兴趣挺符合的，又入了单片机这个坑，感觉很棒，兴趣能成为工作当然是很棒的一件事。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e这是我的第一篇文章，以后，我会写一些我学习的过程，以及学习中的一些问题和思考，会一直写的，因为一直在学习。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"我的第一篇博客"}]