STM32CubeMX-HAL库开发笔记

前言

我自己刚刚开始学习STM32时,跟随正点原子课程,一节节课慢慢学,裸机开发可以深入了解和学习到寄存器内部,但是也偏无聊一点。后来,在做项目时,发现很难选择芯片型号,一直使用F103C8T6这款芯片。随着2021年芯片价格翻了接近30倍,自己觉得选型芯片的无比重要,遂学习STM32CubeMX。起初HAL库令自己很难适应,毕竟写个一个延时还得找资料,但是随着学习,发现CubeMX对项目的帮助是巨大的,它可以帮助你不受芯片型号的限制,快速开发项目,且内置FreeRTOS,随手就能跑个操作系统,简直不要太香。

不过界面至今没有汉化版,也是唯一的遗憾。如果只学习HAL库的话,可能会有不知底层是何为的困惑,但是也无妨。就像我作为物理学的学生,老师常说只要会用数学公式一样,我们会用即可。

这是自己编写的一个打地鼠的小游戏,串口显示程序运行位置,右方小灯显示分数,达到17分进入彩蛋程序。

Proteus 8 配置工程

使用Proteus 8可以仿真STM32F103系列T4、T6、C4、C6、R4、R6单片机,可以帮助项目前期少走一点弯路,学习者前期可以少花点钱去学习STM32。

使用STM32CubeMX配置基础工程的部分不做讲解,因为图形化真的很简单。本文章主要记录在配置工程后,HAL库函数的使用。

1、GPIO

读取IO:

HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin);

写入IO:

HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
HAL_GPIO_WritePin(GPIOA,LED0_Pin,GPIO_PIN_RESET)            //置0;
HAL_GPIO_WritePin(GPIOA,LED0_Pin,GPIO_PIN_SET)          //置1;

翻转IO:

HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_GPIO_TogglePin(GPIOA, LED0_Pin);

按键例子:

if(HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin) == GPIO_Pin_SET);
{while(HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin) == GPIO_Pin_SET);//等待按键抬起HAL_GPIO_TogglePin(GPIOA, LED0_Pin);                  //翻转HAL_Delay(200);                                         //延时200msHAL_GPIO_TogglePin(GPIOA, LED0_Pin);                   //翻转
}

2、串口通信

串口通信

串口通信模式:

Asynchronous:异步通信

发送、接收数据

//发送数据
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
uint8_t temp[] = "Hello World!\n\r";
HAL_UART_Transmit(&huart1,temp,12,50);
HAL_UART_Transmit((UART_HandleTypeDef *) &huart1, (uint8_t *) "Hello World!\r\n", (uint16_t) 14, (uint32_t) 30);
//接收数据
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

printf 重定向

在Private includes中引入

#include <stdio.h>

在USER CODE BEGIN 0 添加

int fputc(int ch,FILE *f){uint8_t temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,2); //h-uart1需要根据自己的配置修改return ch;
}

然后就可以在任意地方使用printf语句方便的输出你想要的内容。

printf("Hello World!\n\r");
HAL_Delay(200);

多个串口发送数据

#include <stdio.h>
#include "stdarg.h"
#include "string.h"#define    TXBUF_SIZE_MAX    100void uart?_printf(const char *format, ...)
{va_list args;uint32_t length;uint8_t txbuf[TXBUF_SIZE_MAX] = {0};va_start(args, format);length = vsnprintf((char *)txbuf, sizeof(txbuf), (char *)format, args);va_end(args);HAL_UART_Transmit(&huart?, (uint8_t *)txbuf, length, HAL_MAX_DELAY);memset(txbuf, 0, TXBUF_SIZE_MAX);
}

scanf重定向

int fgetc(FILE *f)
{uint8_t ch = 0;HAL_UART_Receive(&huart1, &ch, 1, 0xffff);return ch;
}
scanf("%d %d",&a,&b);

多个串口接收信息-回调函数

  1. 开启中断,只起一次作用
HAL_UART_Receive_IT(&huart1,(uint8_t *)aRxBuffer1,1);
HAL_UART_Receive_IT(&huart2,(uint8_t *)aRxBuffer2,1);
  1. 回调函数
#define USART1_RXBUFF_SIZE   1024              //定义串口2 接收缓冲区大小 1024字节
char USART1_RXBUFF[USART1_RXBUFF_SIZE];     //定义接收数组
uint8_t data;
uint8_t t=0;uint8_t aRxBuffer1[1]; //定义临时存放数组
uint8_t aRxBuffer2[1];  //定义临时存放数组void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1) // 判断是由哪个串口触发的中断{data = aRxBuffer1[0];USART1_RXBUFF[t] = data;t++;HAL_UART_Receive_IT(&huart1,aRxBuffer1,1);        // 重新使能串口1接收中断}if(huart->Instance == USART2){HAL_UART_Transmit(&huart2,aRxBuffer2,1,100);  // 接收到数据马上使用串口1发送出去HAL_UART_Receive_IT(&huart2,aRxBuffer2,1);       // 重新使能串口2接收中断}
}
  1. while函数输出
HAL_Delay(2000);
int i;
for(i=0;i<USART1_RXBUFF_SIZE;i++)printf("%c",USART1_RXBUFF[i]);
t=0;

例2

#define USART1_RXBUFF_SIZE   256     //最大接收字节数
#define USART2_RXBUFF_SIZE   256     //最大接收字节数char Usart1_RxBuff[USART1_RXBUFF_SIZE];    //接收数据
char Usart2_RxBuff[USART2_RXBUFF_SIZE];  //接收数据
uint8_t aRxBuffer1;             //接收中断缓冲
uint8_t aRxBuffer2;             //接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0;      //接收缓冲计数
uint8_t Uart2_Rx_Cnt = 0;      //接收缓冲计数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)         //串口回调函数
{if(huart->Instance == USART1) // 判断是由哪个串口触发的中断{//      HAL_UART_Transmit(&huart1,(uint8_t *)&aRxBuffer1,1,100);    // 接收到数据马上使用串口1发送出去
//      HAL_UART_Transmit(&huart2,(uint8_t *)&aRxBuffer1,1,100);    // 接收到数据马上使用串口2发送出去
//      HAL_UART_Receive_IT(&huart1,(uint8_t *)&aRxBuffer1,1);      // 重新使能串口1接收中断if(Uart1_Rx_Cnt >= 255)  //溢出判断{Uart1_Rx_Cnt = 0;memset(Usart1_RxBuff,0x00,sizeof(Usart1_RxBuff));HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);    }else{Usart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer1;   //接收数据转存if((Usart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Usart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位{HAL_UART_Transmit(&huart1, (uint8_t *)&Usart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束HAL_UART_Transmit(&huart2, (uint8_t *)&Usart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去while(HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束Uart1_Rx_Cnt = 0;memset(Usart1_RxBuff,0x00,sizeof(Usart1_RxBuff)); //清空数组}}}HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer1, 1);   //再开启接收中断if(huart->Instance == USART2){//      HAL_UART_Transmit(&huart1,(uint8_t *)&aRxBuffer2,1,100);    // 接收到数据马上使用串口1发送出去if(Uart2_Rx_Cnt >= 255)  //溢出判断{Uart2_Rx_Cnt = 0;memset(Usart2_RxBuff,0x00,sizeof(Usart2_RxBuff));HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);   }else{Usart2_RxBuff[Uart2_Rx_Cnt++] = aRxBuffer2;   //接收数据转存if((Usart2_RxBuff[Uart2_Rx_Cnt-1] == 0x4B)&&(Usart2_RxBuff[Uart2_Rx_Cnt-2] == 0x4F)) //判断结束位“OK”{HAL_UART_Transmit(&huart1, (uint8_t *)&Usart2_RxBuff, Uart2_Rx_Cnt,0xFFFF); //将收到的信息发送出去while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束Uart2_Rx_Cnt = 0;memset(Usart2_RxBuff,0x00,sizeof(Usart2_RxBuff)); //清空数组}   }       }HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer2, 1);   //再开启接收中断
}

strstr()函数检索信息

  • 函数的声明
char *strstr(const char *haystack, const char *needle)
参数 说明
haystack 要被检索的 C 字符串
needle 在 haystack 字符串内要搜索的小字符串

该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

  • 实例
#include <stdio.h>
#include <string.h>int main()
{const char haystack[20] = "RUNOOB";const char needle[10] = "NOOB";char *ret;ret = strstr(haystack, needle);printf("子字符串是: %s\n", ret);return(0);
}
char *tim_strl = strstr((char *)MQTT_CMDOutPtr+2,"timing\":");  //strstr返回字符首次出现的地址
if((int)*(tim_strl+9) == 125)            //时间为0 - 9{timing = (int)(*(tim_strl+8)) - 48;                       //char转化为int}else if((int)*(tim_strl+10) == 125)          {timing = ((int)(*(tim_strl+8))-48)*10 + ((int)(*(tim_strl+9)) - 48);                       //char转化为int}else if((int)*(tim_strl+11) == 125){timing = ((int)(*(tim_strl+8))-48)*100 + ((int)(*(tim_strl+9))-48)*10 + ((int)(*(tim_strl+10))-48); }sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"203302322\",\"params\":{\"timing\":%d,\"currentstate\":1},\"version\":\"1.0.0\"}",timing);  //需要回复状态给服务器MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));   //添加数据,发布给服务器

memset()清空接收缓存区

memset(USART1_RXBUFF,0,USART1_RXBUFF_SIZE);  //清空WiFi接收缓冲区

每次发送数据前,先情况一下缓存区

LOG信息打印

主流嵌入式输出格式:[日志级别] 文件名: 日志信息

"[info] main.c : init ok!"
"[debug] adc.c : adc_getvalue -> 3.3V"
printf("[info] main.c : HAL_Init ok! \r\n");

条件编译

在单片机开发过程中,需要大量的LOG信息;但是开发结束后,不需要一直打印(拖慢单片机速度)。
所以在main.h头文件添加:

#define Log 1 //打印Log信息,不想打印时改为0即可

再把.c文件中所有printf语句包裹上#id Log 与 #endif:

#if Log
printf("[info] main.c : HAL_Init ok! \r\n");
# endif

个性化串口输出

字符转ASCII码网站: 个性化

#define Log 1 //打印Log信息,不想打印时改为0即可
#if Log
printf("  _____  ______ _______ _    _ _____  _   _ \r\n");
printf(" |  __ )|  ____|__   __| |  | |  __ )( ) | |\r\n");
printf(" | |__) | |__     | |  | |  | | |__) |  )| |\r\n");
printf(" |  _  /|  __|    | |  | |  | |  _  /| . ` |\r\n");
printf(" | | ) )| |____   | |  | |__| | | ) (| |(  |\r\n");
printf(" |_|  )_)______|  |_|  (_____/|_|  )_(_| (_|\r\n");
# endif

可变参数宏

在Private includes中引入

#include <stdio.h>

在USER CODE BEGIN 0 添加

int fputc(int ch,FILE *f){uint8_t temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,2); //h-uart1需要根据自己的配置修改return ch;
}

在USER CODE BEGIN 0 添加

#define USER_LOG     //注释此行,不打印#ifdef USER_LOG
#define user_main_printf(format,...) printf(format "\r\n",##__VA_ARGS__)
#define user_main_info(format,...) printf("[main]info:" format "\r\n",##__VA_ARGS__)
#define user_main_debug(format,...) printf("[main]debug:" format "\r\n",##__VA_ARGS__)
#define user_main_error(format,...) printf("[main]error:" format "\r\n",##__VA_ARGS__)
#else
#define user_main_printf(format,...)
#define user_main_info(format,...)
#define user_main_debug(format,...)
#define user_main_error(format,...)
#endif

在while()中添加

user_main_info("Hello World!");
HAL_Delay(200);

自动添加报头和报尾

发送、接收中断

//发送中断
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
//接收中断
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

DMA

//使用DMA发送
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
//使用DMA接收
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
//DMA暂停
HAL_UART_DMAPause(UART_HandleTypeDef *huart);
//DMA恢复
HAL_UART_DMAResume(UART_HandleTypeDef *huart);
//DMA停止
HAL_UART_DMAStop(UART_HandleTypeDef *huart);

3、外部中断

回调函数

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){if(GPIO_Pin == BUTTON0_Pin); //多个外部中断,需要进一步判断}

4、定时器


使能

HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Start_IT(&htim1);  //定时器1使能

回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == htim1.Instance){//定时器1中断业务}
}

读取定时器1的数值

int time_num = __HAL_TIM_GET_COUNTER(&htim1);   //读取定时器1的数值

5、IIC

特点:简单、双向、二线制、同步串行总线

注意:IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI

I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。

每个连接到总线的设备都有一个独立的地址,主机正是利用该地址对设备进行访问

IIC写函数

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数 功能说明
*hi2c 设置使用的是那个IIC &hi2c2
DevAddress 写入的地址 设置写入数据的地址 0xA0
*pData 需要写入的数据 “Hello”
Size 要发送的字节数 5
Timeout 最大传输时间,超过传输时间将自动退出传输函数 10

IIC读函数

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_I2C_Master_Receive(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;
参数 功能说明
*hi2c 设置使用的是那个IIC &hi2c1
DevAddress 写入的地址 设置写入数据的地址 0xA1
*pData 存储读取到的数据 (uint8_t*)TxData
Size 发送的字节数 2
Timeout 最大读取时间,超过时间将自动退出读取函数 1000

IIC写数据函数

HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数 功能说明
I2C_HandleTypeDef I2C操作句柄 &hi2c1
DevAddress 从机设备地址 0xA0
MemAddress 从机寄存器地址 每写入一个字节数据,地址就会自动+1
MemAddSize 从机寄存器地址长度 从机寄存器地址字节长度 8位或16位
*pData 发送的数据的起始地址 8位还是16位
Size 传输数据的大小
Timeout 操作超时时间

#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1
#define BufferSize 256
uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize];for(i=0;i<BufferSize;i++)
{HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write,i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入
}
HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT,ReadBuffer,BufferSize, 0xff);

6、SPI

SPI 共包含 4 条总线。

环形总线结构

两个简单的移位寄存器,传输的数据为8位,根据上面的传输模式发出一位数据就会接收到一位数据,移位寄存器在旧的数据没有发完之前是不会接受新的数据的。

SS(Slave Select):片选信号线,当有多个SPI 设备与 MCU 相连时,每个设备的这个片选信号线是与 MCU 单独的引脚相连的,而其他的 SCK、MOSI、MISO 线则为多个设备并联到相同的 SPI 总线上,低电平有效。

SCK (Serial Clock):时钟信号线,由主通信设备产生,不同的设备支持的时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 f PCLK /2。

MOSI (Master Output Slave Input):主设备输出 / 从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入数据,即这条线上数据的方向为主机到从机。

MISO(Master Input Slave Output):主设备输入 / 从设备输出引脚。主机从这条信号线读入数据,从机的数据则由这条信号线输出,即在这条线上数据的方向为从机到主机。

(1)打开软件,选择对应芯片后,配置好时钟源;

(2)勾选SPI1为全双工,硬件NSS关闭,如下图:

(3)勾选好后,PA5、PA6、PA7如下图,在配置PA4为普通io口,gpio_output

(4)SPI1的参数配置选择默认,如下图所示

HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
//发送数据HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
//接收数据

7、FreeRTOS

(1)使能FreeRTOS

(2)创建两个FreeRTOS任务:Task1和Task2

配置流程

STM32CubeMX-HAL库开发笔记(常用语句)-基于Proteus仿真相关推荐

  1. STM32cubemx——HAL库学习笔记 六、IWDG独立看门狗的配置

    一.配置STM32cubeMX工程 在配置好时钟和调试设备以后进行一下操作即可以使用 看门狗的溢出时间公式为 Tout= 分频系数/ 时钟 * 重装载值 二.IWDG的技术讲解 可以到看,看门狗相对于 ...

  2. STM32系列之HAL库开发

    STM32系列文章目录 文章目录 STM32系列文章目录 前言 一.HAL库之串口收发 1.配置STM32CUBEID 2.使用串口中断收发 2.1 配置串口中断 2.2 HAL库中断处理逻辑 3 测 ...

  3. 基于stm32cubeMX的stm32f103c8t6的HAL库开发的智能小车------小车接线和材料准备

    我的第一辆智能小车 提示:小编也是初学者,本文适用于想完成一个基础智能四轮车的初学者,大佬还请勿喷,欢迎各位指出错误的地方 暑假在家无聊,刚好也在学习STM32的HAL库,就想着做个小车巩固自己学到的 ...

  4. STM32 HAL库开发学习笔记: USART1串口通讯(中断方式) IDE-STM32CubeIDE

    STM32串口通讯有三种方式,分别为阻塞(轮询).中断.DMA.这里将用中断的方式开发. 笔者也是刚入门STM32 HAL库开发,该笔记致希望于能帮到初学者,文中配置步骤.代码.实验现象均是笔者实践可 ...

  5. STM32 HAL库学习笔记1-HAL库简介

    STM32 HAL库学习笔记1-HAL库简介 HAL库 SPL 库 和 HAL 库两者相互独立,互不兼容.几种库的比较如下 目前几种库对不同芯片的支持情况如下 ST 中文官网上有一篇<关于ST库 ...

  6. STM32 HAL库学习笔记2 HAL库介绍

    STM32 HAL库学习笔记2 HAL库介绍 CMSIS标准 一.再次认识HAL库 HAL库设计思想 HAL库实现方式 以GPIO模块为例 GPIO外设数据类型 GPIO外设接口函数 二.使用HAL库 ...

  7. 配置CLion进行嵌入式STM32的HAL库开发

    前言 时不可以苟遇,道不可以虚行. 一.准备 1.软件 CLion-2020:百度网盘提取链接放在文章最后. STM32CubeMX:使用 6.5.0 版本的,不要使用最新版本的 CubeMX,不然没 ...

  8. STM32+RS485+Modbus-RTU(主机模式+从机模式)-标准库/HAL库开发

    modbus协议 完成modbus协议的编程之后,设备可以分别作为modbus协议的主机或者从机进行测试,使用模拟软件测试完毕后,完整代码以三个版本的形式进行介绍 1.版本一:使用串口接收数据超时完成 ...

  9. STM32F407霸天虎HAL库学习笔记——使用ADC采集MQ135的数据并通过OLED显示

    STM32F407霸天虎HAL库学习笔记--使用ADC采集MQ135的数据并通过OLED显示 一.软件准备 二.硬件准备 三.CubeMX配置 四.Keil MQ135.c MQ135.h main函 ...

最新文章

  1. 风变编程python论文_如何看待风变编程的 Python 网课?
  2. 工业镜头选型计算公式_变压器分接开关选型指南
  3. struts2 ModelDriven 和 Preparable 拦截器
  4. 实验一 命令解释程序编写
  5. solr的索引库配置
  6. 模拟电子技术知识点总结
  7. 保姆级 nas 服务器搭建手册
  8. Power BI----综合应用
  9. 推挽变压器与正激变压器的功率比较
  10. 爬取网易云音乐50000+首歌曲
  11. 2023年湖北一级(高级)技师二级技师报名时间、考试时间是什么时候?
  12. 华为云发布实时音视频行业加速器,为企业解决技术与商业双重难题
  13. GSM和GPRS网络原理的基本思路
  14. 与另一台计算机建立ipc,利用IPC$开启他人电脑远程桌面
  15. Ulua调用C#枚举
  16. 获取抖音商品详情接口调用展示
  17. Abaqus子程序Vumat报错Bad material definition
  18. LearnOpenGL学习笔记——几何着色器
  19. aoeplacebo:地理安慰剂检验
  20. elementui 弹窗遮罩问题;Message层级问题(被遮罩、弹窗遮住,设置层级;弹窗内容被遮罩遮挡)

热门文章

  1. php写入速度rabbit,PHP操作RabbitMQ简单Demo
  2. 使用VisualSFM和Meshlab实现三维重建过程
  3. 循环practice
  4. 微信小程序中自定义组件
  5. lambda表达式,embed = lambda x, eo=embedder_obj : eo.embed(x)解读
  6. 苹果开发者账号申请流程完整版 https://www.jianshu.com/p/655380201685
  7. [Python Scrapy爬虫] 二.翻页爬取农产品信息并保存本地
  8. 华为mate10可以用鸿蒙吗,可升级到鸿蒙2.0的四款华为手机,在用的恭喜了
  9. 如何在arxiv上面发论文
  10. 输出1-100的奇数(每行输出6个)