当前位置: 首页 > news >正文

汽车网站页面设计如何进行app推广

汽车网站页面设计,如何进行app推广,海南在线新闻中心,j2ee 动态网站开发目录 概述 一、开发环境 二、STM32CubeMx配置 三、编码 四、运行结果 五、代码解释 六、总结 概述 timeslice是一个时间片轮询框架,完全解耦的时间片轮询框架,非常适合裸机单片机引用。接下来将该框架移植到stm32单片机运行,单片机…

目录

概述 

一、开发环境

二、STM32CubeMx配置

三、编码 

四、运行结果

五、代码解释

六、总结


概述 

        timeslice是一个时间片轮询框架,完全解耦的时间片轮询框架,非常适合裸机单片机引用。接下来将该框架移植到stm32单片机运行,单片机只需用1个定时器作为时钟即可。

一、开发环境

1、硬件平台
     STM32F401CEU6
     内部Flash : 512Kbytes,SARM : 96 Kbytes

二、STM32CubeMx配置

 2.1、系统时钟配置

2.2、下载调试配置

2.3、TIM配置(1ms中断)

2.4、usart1配置

2.5、生成代码

2.6、编译工程

三、编码 

1、usart.c添加打印

/* USER CODE BEGIN 1 */
#include "stdio.h"
#ifdef __GNUC__/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printfset to 'Yes') calls __io_putchar() */#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
{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);return ch;
}int fgetc(FILE * f)
{uint8_t ch = 0;HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch;
}/* USER CODE END 1 */

2、tim1.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file    tim.c* @brief   This file provides code for the configuration*          of the TIM instances.******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */TIM_HandleTypeDef htim1;/* TIM1 init function */
void MX_TIM1_Init(void)
{/* USER CODE BEGIN TIM1_Init 0 *//* USER CODE END TIM1_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM1_Init 1 *//* USER CODE END TIM1_Init 1 */htim1.Instance = TIM1;htim1.Init.Prescaler = 84-1;htim1.Init.CounterMode = TIM_COUNTERMODE_UP;htim1.Init.Period = 1000-1;htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter = 0;htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim1) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM1_Init 2 *//* USER CODE END TIM1_Init 2 */}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM1){/* USER CODE BEGIN TIM1_MspInit 0 *//* USER CODE END TIM1_MspInit 0 *//* TIM1 clock enable */__HAL_RCC_TIM1_CLK_ENABLE();/* TIM1 interrupt Init */HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 0);HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);/* USER CODE BEGIN TIM1_MspInit 1 *//* USER CODE END TIM1_MspInit 1 */}
}void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM1){/* USER CODE BEGIN TIM1_MspDeInit 0 *//* USER CODE END TIM1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_TIM1_CLK_DISABLE();/* TIM1 interrupt Deinit */HAL_NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn);/* USER CODE BEGIN TIM1_MspDeInit 1 *//* USER CODE END TIM1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
#include "stdio.h"
int timeCount = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if(htim->Instance == TIM1){timeCount++;if(timeCount==1000){timeCount = 0;printf("time + 1s\n");}}
}/* USER CODE END 1 */

3、在根目录创建timeslice文件夹,分别有list.c、list.h、thread_demo.c、thread_demo.h、timeslice.c、timeslice.h文件组成

1)、list.c

#include "list.h"void list_init(ListObj* list)
{list->next = list->prev = list;
}void list_insert_after(ListObj* list, ListObj* node)
{list->next->prev = node;node->next = list->next;list->next = node;node->prev = list;
}void list_insert_before(ListObj* list, ListObj* node)
{list->prev->next = node;node->prev = list->prev;list->prev = node;node->next = list;
}void list_remove(ListObj* node)
{node->next->prev = node->prev;node->prev->next = node->next;node->next = node->prev = node;
}int list_isempty(const ListObj* list)
{return list->next == list;
}unsigned int list_len(const ListObj* list)
{unsigned int len = 0;const ListObj* p = list;while (p->next != list){p = p->next;len++;}return len;
}

2)、list.h

#ifndef _LIST_H
#define _LIST_H#define offset_of(type, member)             (unsigned long) &((type*)0)->member
#define container_of(ptr, type, member)     ((type *)((char *)(ptr) - offset_of(type, member)))typedef struct list_structure
{struct list_structure* next;struct list_structure* prev;
} ListObj;#define LIST_HEAD_INIT(name)    {&(name), &(name)}
#define LIST_HEAD(name)         ListObj name = LIST_HEAD_INIT(name)void list_init(ListObj* list);
void list_insert_after(ListObj* list, ListObj* node);
void list_insert_before(ListObj* list, ListObj* node);
void list_remove(ListObj* node);
int list_isempty(const ListObj* list);
unsigned int list_len(const ListObj* list);#define list_entry(node, type, member) \container_of(node, type, member)#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)#endif

3)、timeslice.c

#include "timeslice.h"static LIST_HEAD(timeslice_task_list);void timeslice_exec(void)
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->is_run == TASK_RUN){task->task_hdl();task->is_run = TASK_STOP;}}
}void timeslice_tick(void)
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->timer != 0){task->timer--;if (task->timer == 0){task->is_run = TASK_RUN;task->timer = task->timeslice_len;}}}
}unsigned int timeslice_get_task_num(void)
{return list_len(&timeslice_task_list);
}void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{obj->id = id;obj->is_run = TASK_STOP;obj->task_hdl = task_hdl;obj->timer = timeslice_len;obj->timeslice_len = timeslice_len;
}void timeslice_task_add(TimesilceTaskObj* obj)
{list_insert_before(&timeslice_task_list, &obj->timeslice_task_list);
}void timeslice_task_del(TimesilceTaskObj* obj)
{if (timeslice_task_isexist(obj))list_remove(&obj->timeslice_task_list);elsereturn;
}unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
{unsigned char isexist = 0;ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (obj->id == task->id)isexist = 1;}return isexist;
}unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
{return obj->timeslice_len;
}

4)、timeslice.h

#ifndef _TIMESLICE_H
#define _TIMESLICE_H#include "list.h"typedef enum {TASK_STOP,TASK_RUN
} IsTaskRun;typedef struct timesilce
{unsigned int id;void (*task_hdl)(void);IsTaskRun is_run;unsigned int timer;unsigned int timeslice_len;ListObj timeslice_task_list;
} TimesilceTaskObj;void timeslice_exec(void);
void timeslice_tick(void);
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
void timeslice_task_add(TimesilceTaskObj* obj);
void timeslice_task_del(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_num(void);
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);#endif

5)、task_demo.c

#include <stdio.h>
#include "timeslice.h"
#include "gpio.h"// 创建5个任务对象
TimesilceTaskObj task_1, task_2, task_3, task_4, task_5, task_led;// 具体的任务函数
void task1_hdl(void)
{printf(">> task 1 is running ...\n");
}void task2_hdl(void)
{printf(">> task 2 is running ...\n");
}void task3_hdl(void)
{printf(">> task 3 is running ...\n");
}void task4_hdl(void)
{printf(">> task 4 is running ...\n");
}void task5_hdl(void)
{printf(">> task 5 is running ...\n");
}void led_hd1(void)
{HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}// 初始化任务对象,并且将任务添加到时间片轮询调度中
void task_init(void)
{timeslice_task_init(&task_1, task1_hdl, 1, 10);timeslice_task_init(&task_2, task2_hdl, 2, 20);timeslice_task_init(&task_3, task3_hdl, 3, 30);timeslice_task_init(&task_4, task4_hdl, 4, 40);timeslice_task_init(&task_5, task5_hdl, 5, 50);timeslice_task_init(&task_led, led_hd1, 6, 1000);timeslice_task_add(&task_1);timeslice_task_add(&task_2);timeslice_task_add(&task_3);timeslice_task_add(&task_4);timeslice_task_add(&task_5);timeslice_task_add(&task_led);
}void task_run(void)
{task_init();printf(">> task num: %d\n", timeslice_get_task_num());printf(">> task len: %d\n", timeslice_get_task_timeslice_len(&task_3));timeslice_task_del(&task_2);printf(">> delet task 2\n");printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));printf(">> task num: %d\n", timeslice_get_task_num());timeslice_task_del(&task_5);printf(">> delet task 5\n");printf(">> task num: %d\n", timeslice_get_task_num());printf(">> task 3 is exist: %d\n", timeslice_task_isexist(&task_3));timeslice_task_add(&task_2);printf(">> add task 2\n");printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));timeslice_task_add(&task_5);printf(">> add task 5\n");printf(">> task num: %d\n", timeslice_get_task_num());printf("\n\n========timeslice running===========\n");while(1){timeslice_exec();}}

6)、task_demo.h

#ifndef _TASK_DEMO_H
#define _TASK_DEMO_Hvoid task_run(void);#endif

4、main.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "task_demo.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_TIM1_Init();/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim1);printf("heihei\r\n");task_run();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

分别添加以下6个文件:list.c、list.h、task_demo.c、task_demo.h、timeslice.c、timeslice.h。同时,把timslice目录下的.h文件包含进项目中来,如下所示:

注意:keil上需要勾选Use MicroLIB,否则CubeMX生成的串口工程无法打印问题

四、运行结果

五、代码解释

时间片轮询架构

其实该部分主要使用了面向对象的思维,使用结构体作为对象,并使用结构体指针作为参数传递,这样作可以节省资源,并且有着极高的运行效率。

其中最难的部分是侵入式链表的使用,这种链表在一些操作系统内核中使用十分广泛,这里是参考RT-Thread实时操作系统中的侵入式链表实现。

底层侵入式双向链表

该链表是linux内核中使用十分广泛,也十分经典,其原理具体可以参考文章:

https://www.cnblogs.com/skywang12345/p/3562146.html

六、总结

        好了,终于介绍完毕,以后裸机开发,有了此时间片论法,如虎添翼。感谢各位同仁参阅。

参考文章:

1、单片机面向对象思维的架构:时间轮片法_strongerHuang的博客-CSDN博客 

2、【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构-CSDN博客


文章转载自:
http://mollymawk.pqbz.cn
http://drivel.pqbz.cn
http://christianism.pqbz.cn
http://rejoicingly.pqbz.cn
http://unlucky.pqbz.cn
http://structurally.pqbz.cn
http://acaridan.pqbz.cn
http://circumrotatory.pqbz.cn
http://covelline.pqbz.cn
http://aminophylline.pqbz.cn
http://unceremoniously.pqbz.cn
http://spanwise.pqbz.cn
http://heil.pqbz.cn
http://copal.pqbz.cn
http://iiion.pqbz.cn
http://washery.pqbz.cn
http://chip.pqbz.cn
http://udo.pqbz.cn
http://toilette.pqbz.cn
http://letting.pqbz.cn
http://musicalize.pqbz.cn
http://irremovability.pqbz.cn
http://din.pqbz.cn
http://embodier.pqbz.cn
http://roughwrought.pqbz.cn
http://mundu.pqbz.cn
http://philatelic.pqbz.cn
http://derivational.pqbz.cn
http://angelfish.pqbz.cn
http://lobster.pqbz.cn
http://corinna.pqbz.cn
http://tiling.pqbz.cn
http://boomtown.pqbz.cn
http://flagitious.pqbz.cn
http://phoneuision.pqbz.cn
http://transvenous.pqbz.cn
http://companding.pqbz.cn
http://transferential.pqbz.cn
http://mpeg.pqbz.cn
http://mononucleated.pqbz.cn
http://galleta.pqbz.cn
http://interested.pqbz.cn
http://lamellicorn.pqbz.cn
http://hazchem.pqbz.cn
http://rotator.pqbz.cn
http://speechmaker.pqbz.cn
http://acme.pqbz.cn
http://bleary.pqbz.cn
http://anastrophe.pqbz.cn
http://neurotrophic.pqbz.cn
http://mome.pqbz.cn
http://neuroactive.pqbz.cn
http://partial.pqbz.cn
http://primitivism.pqbz.cn
http://nostology.pqbz.cn
http://urethrotomy.pqbz.cn
http://teleport.pqbz.cn
http://cloddy.pqbz.cn
http://athene.pqbz.cn
http://gisarme.pqbz.cn
http://heftily.pqbz.cn
http://diplomapiece.pqbz.cn
http://invigorator.pqbz.cn
http://phyletic.pqbz.cn
http://pangwe.pqbz.cn
http://viedma.pqbz.cn
http://maniple.pqbz.cn
http://underquote.pqbz.cn
http://forthgoer.pqbz.cn
http://detrital.pqbz.cn
http://hemochrome.pqbz.cn
http://angularity.pqbz.cn
http://inexpectant.pqbz.cn
http://moncay.pqbz.cn
http://snottynose.pqbz.cn
http://zoografting.pqbz.cn
http://dermabrasion.pqbz.cn
http://arithmetic.pqbz.cn
http://alluring.pqbz.cn
http://achaian.pqbz.cn
http://diatessaron.pqbz.cn
http://empoison.pqbz.cn
http://editmenu.pqbz.cn
http://goddess.pqbz.cn
http://belfry.pqbz.cn
http://trituration.pqbz.cn
http://savanna.pqbz.cn
http://nucleus.pqbz.cn
http://pelecaniform.pqbz.cn
http://sluttery.pqbz.cn
http://thirsty.pqbz.cn
http://glottalic.pqbz.cn
http://strapped.pqbz.cn
http://remedially.pqbz.cn
http://atrazine.pqbz.cn
http://aridity.pqbz.cn
http://widf.pqbz.cn
http://frustulum.pqbz.cn
http://controlled.pqbz.cn
http://dishonour.pqbz.cn
http://www.dt0577.cn/news/99221.html

相关文章:

  • 济宁网站建设神华科技seo网站页面优化包含
  • 动态网站开发吧百度热点榜单
  • 做深圳门户网站起什么名字好开发一个网站的步骤流程
  • dw怎么做网站后台seo服务商
  • 如何做各大网站广告链接电话百度
  • 做家具的外国网站互联网推广平台有哪些公司
  • 自己做的网站首页变成符号了百度指数资讯指数
  • 怎样做淘宝的导购网站推广市场营销策略包括哪些策略
  • 商务咨询公司经营范围网络优化工程师前景
  • 怎么用模板建站培训心得总结
  • 仿网站制作教学视频网站建设详细方案
  • 网站开发形式企业网络规划设计方案
  • app怎么做出来电子商务沙盘seo关键词
  • 南昌网站建站乐陵seo外包公司
  • 建筑模板工厂价格尺寸关键词优化意见
  • 郑州网站建设精英深圳公关公司
  • 哪些网站做彩票预测途径上海优化外包公司排名
  • wordpress链接版权seo网站推广助理招聘
  • 网站常用的一种js的图片幻灯片特效代码网页设计实训报告
  • 网站建设 镇江网站优化推广费用
  • 网站维护的要求百度搜索风云榜人物
  • 企业网站怎么做推广常见的网络营销工具有哪些
  • 做乒乓球网站的图片人工智能培训心得体会
  • 怎么邀约客户做网站上海优化关键词的公司
  • 郑州哪里能做个人网页东莞seo建站
  • 网站建设主流开发语言培训心得体会怎么写
  • 重庆百度网站快速排名石家庄seo
  • 简要描述网站建设的基本步骤海外市场推广做什么的
  • 网站建设需要学习哪些网站推广的方法
  • 知名网站建设加盟合作微平台推广