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

CAN信号通信

这章讲述下can信号收发,先基于回路
1:硬件与环境
keil5.38
stm32f103c8t6
STM32CubeMX 1.19
rt-thread 3.3 (这个不用也可以)

2:CAN 介绍

CAN 相关介绍
CAN时钟频率:STM32中CAN外设的时钟频率,通常来自于APB1总线。 
Prescaler:分频系数,用于将CAN时钟分频得到时间量子(Time Quanta,TQ)的频率。
TimeSeg1和TimeSeg2:分别代表位时间段1和位时间段2,以时间量子(TQ)为单位。
注意:一个位时间总共包括Sync_Seg(固定为1TQ)、TimeSeg1和TimeSeg2。所以总TQ数为1+TimeSeg1+TimeSeg2。
SyncJumpWidth(同步跳转宽度),它定义了在重同步时允许的最大时间量子调整量,取值范围从1到4(对应的宏为CAN_SJW_1TQ到CAN_SJW_4TQ)
SyncJumpWidth不能大于TimeSeg21: 核心公式
波特率 = CAN时钟频率 / (Prescaler × 总时间量子数)
总时间量子数 = 1 + TimeSeg1 + TimeSeg22:采样点公式:
采样点位置 = (1 + TimeSeg1) / 总时间量子数
3:Prescaler (预分频器)
作用:将CAN时钟分频得到时间量子(TQ)的频率
公式:TQ频率 = CAN时钟 / Prescaler
范围:1-1024
影响:决定了时间量子的精度4 TimeSeg
1>TimeSeg1 (BS1)
作用:相位缓冲段1 + 传播段
包含:
传播时间段(Prop_Seg)
相位缓冲段1(Phase_Seg1)
范围:1-16个TQ
影响:信号传播补偿和相位误差补偿2>
TimeSeg2 (BS2)
作用:相位缓冲段2
范围:1-8个TQ
影响:相位误差补偿,决定重同步能力5:完整的位时间结构
一个CAN位时间 = SYNC_SEG + TimeSeg1 + TimeSeg2= 1 TQ    + BS1      + BS2
SYNC_SEG (1 TQ):硬同步点,边沿应在此段内
TimeSeg1 (BS1):补偿物理延迟和相位误差
TimeSeg2 (BS2):提供重同步的调整空间6: 计算方法和步骤推荐的比例:
TimeSeg1 : TimeSeg2 ≈ 2:1 到 8:1
采样点位置:75% - 90% 之间1>已知参数求波特率
已知:CAN时钟 = 36MHz, Prescaler = 9, BS1 = 13, BS2 = 2
步骤:
1. TQ频率 = 36MHz / 9 = 4MHz
2. 总TQ数 = 1 + 13 + 2 = 16
3. 波特率 = 4MHz / 16 = 250kbps
4. 采样点 = (1 + 13) / 16 = 87.5%

3: CubeMX 配置
在这里插入图片描述

4:上代码
在这里插入图片描述
main.c main 函数
main.h 定义开关

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "rtthread.h"
#include "usart.h"
#include "can.h"
/* USER CODE END Includes */
/* USER CODE BEGIN EM */
#define  CAN_USE_LOOPBACK   1   //1环回模式 0 正常模式
#define  SUB_LOOPBACK_QUERY_INTERRUPT 0  //1 查询  0  中断
/* USER CODE END EM */
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_CAN_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */static uint32_t  ucount =0;// 用户CAN初始化CAN_Init_Interrupt();rt_tick_t last_print_time = 0;uint8_t tx_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};uint8_t counter = 0;rt_kprintf("while start\r\n");rt_thread_delay(1000);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);  //灭rt_kprintf("while start[1]\r\n");rt_tick_t tick_count =  0;//rt_tick_get(); // 获取当前系统节拍 typedef unsigned long  rt_tick_t  //32bit
//		tick_count = rt_tick_get(); // 获取当前系统节拍
//millisecond = (tick_count * 1000) / RT_TICK_PER_SECOND; // 转换为毫秒 //RT_TICK_PER_SECOND 的值(通常在 rtconfig.h 中定义 //  <i>Default: 1000   (1ms)
//#define RT_TICK_PER_SECOND 1000
//rt_tick_from_millisecond()// 开始轮询测试
//#if   CAN_USE_LOOPBACK ==1		
//    CAN_Polling_Test();
//#endif	
#if SUB_LOOPBACK_QUERY_INTERRUPT #if   CAN_USE_LOOPBACK ==1	// 开始轮询测试CAN_Polling_Test();#endif
#else		// 配置CAN中断优先级CAN_Interrupt_Config();
#endif		/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){
//		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
//		rt_thread_delay(2000); /* ��ʱ 500 �� tick */
//		rt_kprintf("led1_thread running,[%d]->\r\n",ucount++);///////////////////////////////////////////////////////////////
#if SUB_LOOPBACK_QUERY_INTERRUPT ==0			// 定期发送测试消息(每2秒)tick_count = rt_tick_get();if (tick_count >last_print_time+ 2000){// 发送CAN消息if (CAN_Send_Msg(0x123, tx_data, 8) == HAL_OK){rt_kprintf("[TX] Sent message ID:0x123  %02X %02X %02X %02X \r\n",tx_data[0],tx_data[1],tx_data[2],tx_data[3]);}// 更新数据for(int i = 0; i < 8; i++){tx_data[i] = counter + i;}counter++;last_print_time = tick_count;}// 处理CAN接收队列//		rt_kprintf("CAN_Process_Queue,ready[%d][%d]->\r\n",ucount,counter);CAN_Process_Queue();if((ucount++ &31) ==0){rt_kprintf("CAN_Process_Queue,ready[%d][%d]->\r\n",ucount,counter);}
#endif       // 其他任务...// HAL_Delay(10);rt_thread_delay(100);///////////////////////////////////////////////////////////* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

can.c 里 MX_CAN_Init 函数

void MX_CAN_Init(void)
{/* USER CODE BEGIN CAN_Init 0 *//* USER CODE END CAN_Init 0 *//* USER CODE BEGIN CAN_Init 1 *//* USER CODE END CAN_Init 1 */hcan.Instance = CAN1;
#if CAN_USE_LOOPBACK	hcan.Init.Mode = CAN_MODE_LOOPBACK;//CAN_MODE_LOOPBACK; 环回模式  //CAN_MODE_NORMAL
#elsehcan.Init.Mode = CAN_MODE_NORMAL;
#endifhcan.Init.TimeTriggeredMode = DISABLE;hcan.Init.AutoBusOff = DISABLE;hcan.Init.AutoWakeUp = DISABLE;hcan.Init.AutoRetransmission = ENABLE;  //ENABLE;自动重传  //DISABLEhcan.Init.ReceiveFifoLocked = DISABLE;hcan.Init.TransmitFifoPriority = DISABLE;// 波特率配置 - 512kbps (APB1=36MHz)hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;hcan.Init.TimeSeg1 = CAN_BS1_12TQ;hcan.Init.TimeSeg2 = CAN_BS2_5TQ;hcan.Init.Prescaler = 4;     // 36MHz/(1+12+5)/4 = 512kHz// 波特率配置 - 1Mbps (APB1=36MHz)
//    hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
//    hcan.Init.TimeSeg1 = CAN_BS1_3TQ;             // BS1 = 3TQ
//    hcan.Init.TimeSeg2 = CAN_BS2_2TQ;             // BS2 = 2TQ
//    hcan.Init.Prescaler = 6;   //// 36MHz/(1+3+2)/6 = 1MHzif (HAL_CAN_Init(&hcan) != HAL_OK){Error_Handler();}/* USER CODE BEGIN CAN_Init 2 *//* USER CODE END CAN_Init 2 */}

canuser.c

// can.c
#include "canuser.h"//extern CAN_HandleTypeDef hcan;// CAN初始化
// CAN初始化
void CAN_Init_Interrupt(void)
{// 初始化队列can_queue_head = 0;can_queue_tail = 0;can_queue_count = 0;// 配置过滤器CAN_Filter_Config();int  falg =0;// 启动CANif (HAL_CAN_Start(&hcan) != HAL_OK){Error_Handler();falg |=1;}#if SUB_LOOPBACK_QUERY_INTERRUPT ==0		// 激活CAN接收中断if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK){Error_Handler();falg |=2;}
#endif		// 启用CAN中断  放到 CAN_Interrupt_Config 函数里// HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 2, 0);// HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);rt_kprintf("CAN_Init_User %d\r\n",falg);
}// CAN过滤器配置
void CAN_Filter_Config(void)
{CAN_FilterTypeDef can_filter;can_filter.FilterBank = 0;can_filter.FilterMode = CAN_FILTERMODE_IDMASK;can_filter.FilterScale = CAN_FILTERSCALE_32BIT;can_filter.FilterIdHigh = 0x0000;can_filter.FilterIdLow = 0x0000;can_filter.FilterMaskIdHigh = 0x0000;can_filter.FilterMaskIdLow = 0x0000;can_filter.FilterFIFOAssignment = CAN_RX_FIFO0;can_filter.FilterActivation = ENABLE;can_filter.SlaveStartFilterBank = 14;if (HAL_CAN_ConfigFilter(&hcan, &can_filter) != HAL_OK){Error_Handler();rt_kprintf("CAN_Filter_Config  error\r\n");}
}// CAN发送函数
HAL_StatusTypeDef CAN_Send_Msg(uint32_t id, uint8_t* data, uint8_t len)
{CAN_TxHeaderTypeDef tx_header;uint32_t tx_mailbox;uint8_t tx_data[8];// 配置发送头tx_header.StdId = id;tx_header.ExtId = 0;tx_header.IDE = CAN_ID_STD;tx_header.RTR = CAN_RTR_DATA;tx_header.DLC = len;tx_header.TransmitGlobalTime = DISABLE;// 复制数据for(int i = 0; i < len && i < 8; i++){tx_data[i] = data[i];}return HAL_CAN_AddTxMessage(&hcan, &tx_header, tx_data, &tx_mailbox);
}// 配置CAN中断优先级(在main函数中调用)
void CAN_Interrupt_Config(void)
{// 设置CAN接收中断优先级// 注意:不要使用优先级0,使用1或2更安全HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 1, 0);HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);rt_kprintf("CAN Interrupt Configured\r\n");
}// CAN接收函数
HAL_StatusTypeDef CAN_Receive_Msg(CAN_Message* msg)
{CAN_RxHeaderTypeDef rx_header;uint8_t rx_data[8];// 检查是否有接收到的消息if (HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0) == 0){return HAL_ERROR;}// 读取消息if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &rx_header, rx_data) != HAL_OK){return HAL_ERROR;}// 填充消息结构msg->id = rx_header.StdId;msg->len = rx_header.DLC;msg->format = rx_header.RTR;msg->type = rx_header.IDE;for(int i = 0; i < rx_header.DLC; i++){msg->data[i] = rx_data[i];}return HAL_OK;
}// CAN接收FIFO0消息挂起回调函数
//void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
//{
//    CAN_Message received_msg;
//    
//    if (CAN_Receive_Msg(&received_msg) == HAL_OK)
//    {
//        // 在这里处理接收到的CAN消息
//        // 例如:点亮LED、发送响应等
//        
//        // 示例:如果收到ID为0x456的消息,则发送响应
//        if (received_msg.id == 0x456)
//        {
//            uint8_t response_data[8] = {0xAA, 0xBB, 0xCC, 0xDD};
//            CAN_Send_Msg(0x123, response_data, 4);
//        }
//    }
//}void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_RxHeaderTypeDef rx_header;CAN_Message received_msg;uint8_t rx_data[8];//	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);//亮// 读取消息if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK){// 填充消息结构received_msg.id = rx_header.StdId;received_msg.len = rx_header.DLC;received_msg.format = rx_header.RTR;received_msg.type = rx_header.IDE;//  received_msg.timestamp = HAL_GetTick(); // 获取时间戳//rt-thread 下用rt_tick_get();received_msg.timestamp =(uint32_t)rt_tick_get();// 复制数据for(int i = 0; i < rx_header.DLC; i++){received_msg.data[i] = rx_data[i];}// 将消息放入队列(非阻塞)CAN_Queue_Put(&received_msg);}
}void CAN_Polling_Test(void)
{CAN_TxHeaderTypeDef TxHeader;CAN_RxHeaderTypeDef RxHeader;uint8_t TxData[8];uint8_t RxData[8];uint32_t TxMailbox;uint32_t test_count = 0;// 配置发送消息头TxHeader.StdId = 0x123;           // 标准IDTxHeader.ExtId = 0x00;TxHeader.RTR = CAN_RTR_DATA;TxHeader.IDE = CAN_ID_STD;TxHeader.DLC = 8;TxHeader.TransmitGlobalTime = DISABLE;rt_kprintf("=== CAN Loopback Polling Test Started ===\r\n");while (1){// 准备测试数据for(int i = 0; i < 8; i++){TxData[i] = (test_count * 8) + i;}rt_kprintf("\r\n--- Test Count: %lu ---\r\n", test_count);// 发送消息HAL_StatusTypeDef tx_status = HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox);rt_kprintf("Send Status: %s, Mailbox: %lu\r\n", (tx_status == HAL_OK) ? "OK" : "FAIL", TxMailbox);if (tx_status != HAL_OK){rt_kprintf("Send failed, skipping receive check\r\n");// HAL_Delay(1000);rt_thread_delay(1000);test_count++;continue;}// 短暂延时,确保消息处理完成rt_thread_delay(5);// 检查接收FIFO状态uint32_t fifo_level = HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0);rt_kprintf("FIFO0 Message Count: %lu\r\n", fifo_level);if (fifo_level > 0){// 读取消息HAL_StatusTypeDef rx_status = HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData);if (rx_status == HAL_OK){rt_kprintf("*** SUCCESS: Message Received! ***\r\n");rt_kprintf("ID: 0x%03X, DLC: %d\r\n", RxHeader.StdId, RxHeader.DLC);rt_kprintf("Data: ");for(int i = 0; i < RxHeader.DLC; i++){rt_kprintf("%02X ", RxData[i]);}rt_kprintf("\r\n");// 验证数据是否正确int data_match = 1;for(int i = 0; i < 8; i++){if (RxData[i] != TxData[i]){data_match = 0;break;}}if (data_match){rt_kprintf("*** DATA VERIFICATION: PASS ***\r\n");}else{rt_kprintf("*** DATA VERIFICATION: FAIL ***\r\n");}}else{rt_kprintf("Receive failed with status: %d\r\n", rx_status);}}else{rt_kprintf("No message received in FIFO0\r\n");// 检查错误状态uint32_t error_code = HAL_CAN_GetError(&hcan);if (error_code != 0){rt_kprintf("CAN Error Code: 0x%08lX\r\n", error_code);}}// 等待一段时间再进行下一次测试rt_thread_delay(2000);test_count++;// 每几次测试改变一次ID,便于观察if (test_count % 3 == 0){TxHeader.StdId++;if (TxHeader.StdId > 0x7FF){TxHeader.StdId = 0x123;}rt_kprintf("Changed CAN ID to: 0x%03X\r\n", TxHeader.StdId);}}
}

canuser.h

#ifndef __CANUSER_H
#define __CANUSER_H#include "main.h"
#include "can.h"
#include <string.h>// CAN消息结构体
typedef struct {uint32_t id;        // 消息IDuint8_t data[8];    // 数据uint8_t len;        // 数据长度uint32_t timestamp; // 时间戳uint8_t format;     // 帧格式uint8_t type;       // 帧类型
} CAN_Message;// 队列相关定义
#define CAN_QUEUE_SIZE 20// 函数声明
//canuser.c 实现
void CAN_Init_Interrupt(void);
HAL_StatusTypeDef CAN_Send_Msg(uint32_t id, uint8_t* data, uint8_t len);
HAL_StatusTypeDef CAN_Receive_Msg(CAN_Message* msg);
void CAN_Filter_Config(void);// 配置CAN中断优先级(在main函数中调用)
void CAN_Interrupt_Config(void);void CAN_Polling_Test(void);/////////////////////////////////////////////////////
//实现在can_queue.c
void CAN_Process_Queue(void);
uint8_t CAN_Queue_Put(CAN_Message* msg);
uint8_t CAN_Queue_Get(CAN_Message* msg);
uint8_t CAN_Queue_IsEmpty(void);
uint8_t CAN_Queue_IsFull(void);// 外部声明(在main.c中定义)
extern CAN_Message can_queue[CAN_QUEUE_SIZE];
extern volatile uint8_t can_queue_head;
extern volatile uint8_t can_queue_tail;
extern volatile uint8_t can_queue_count;#endif

can_queue.c

// can_queue.c
#include "canuser.h"
#include "stdio.h"
#include "rtthread.h"
// 队列变量定义
CAN_Message can_queue[CAN_QUEUE_SIZE];
volatile uint8_t can_queue_head = 0;
volatile uint8_t can_queue_tail = 0;
volatile uint8_t can_queue_count = 0;// 放入消息到队列
uint8_t CAN_Queue_Put(CAN_Message* msg)
{if (CAN_Queue_IsFull()) {return 0; // 队列已满}// 复制消息到队列memcpy(&can_queue[can_queue_tail], msg, sizeof(CAN_Message));// 更新队列指针can_queue_tail = (can_queue_tail + 1) % CAN_QUEUE_SIZE;// 原子操作增加计数__disable_irq();can_queue_count++;__enable_irq();return 1; // 成功
}// 从队列获取消息
uint8_t CAN_Queue_Get(CAN_Message* msg)
{if (CAN_Queue_IsEmpty()) {return 0; // 队列为空}// 从队列复制消息memcpy(msg, &can_queue[can_queue_head], sizeof(CAN_Message));// 更新队列指针can_queue_head = (can_queue_head + 1) % CAN_QUEUE_SIZE;// 原子操作减少计数__disable_irq();can_queue_count--;__enable_irq();return 1; // 成功
}// 检查队列是否为空
uint8_t CAN_Queue_IsEmpty(void)
{return (can_queue_count == 0);
}// 检查队列是否已满
uint8_t CAN_Queue_IsFull(void)
{return (can_queue_count == CAN_QUEUE_SIZE);
}///////////////////////////////////////////////////
// 处理队列中的CAN消息(在主循环中调用)
void CAN_Process_Queue(void)
{CAN_Message msg;static char buffer[256];int len;// 处理队列中的所有消息while (!CAN_Queue_IsEmpty()){if (CAN_Queue_Get(&msg)){/////////////////////////////////////////////////////////////len = snprintf(buffer, sizeof(buffer), "[RX]CAN ID:0x%03X Len:%d Time:%lums Data:", msg.id, msg.len,msg.timestamp);for(int i = 0; i < msg.len && len < sizeof(buffer)-3; i++){len += snprintf(buffer + len, sizeof(buffer) - len, "%02X ", msg.data[i]);}snprintf(buffer + len, sizeof(buffer) - len, "\r\n");rt_kprintf(buffer);//	rt_kprintf("Time:%lums\n", msg.timestamp);///////////////////////////////////////////////////////////// 格式化打印CAN消息
//            snprintf(buffer, sizeof(buffer), 
//                    "[CAN] ID:0x%03X Len:%d Data:", msg.id, msg.len);
//            
//            // 添加到主打印缓冲区
//            printf("%s", buffer);
//            
//            // 打印数据字节
//            for(int i = 0; i < msg.len; i++)
//            {
//                printf("%02X ", msg.data[i]);
//            }
//            
//            // 打印时间戳
//            rt_kprintf("Time:%lums\n", msg.timestamp);}}
}

rt-thread 下串口输出 修改kservice.c
在这里插入图片描述
stm32f1xx_it.c

#if SUB_LOOPBACK_QUERY_INTERRUPT ==0
// CAN接收中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{HAL_CAN_IRQHandler(&hcan);
}
#endif

在这里插入图片描述

#include "usart.h"
RT_WEAK void rt_hw_console_output(const char *str)
{
//	char tp ='\0';rt_size_t len = rt_strnlen(str,512);/* empty console output */rt_enter_critical();///////////////////////////////////////////////////////////////一次全写入
//	 HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);HAL_UART_Transmit(&huart1,(uint8_t *)str,(uint16_t)len,0xFFFF); //同步
//	HAL_UART_Transmit_DMA(&huart1, (uint8_t*)str, (uint16_t)len); //异步///////////////////////////////////////////////////////////////* 直到字符串结束 */
//	while (*str!='\0') {
//		if (*str=='\n' && tp !='\r') { // 换行 
//			HAL_UART_Transmit(&huart1,(uint8_t *)'\r',1,0xFFFF);
//		}
//		tp =*str;
//		HAL_UART_Transmit(&huart1,(uint8_t *)(str++),1,0xFFFF);
//	}/////////////////////////////////////////////////////////////* 退出临界段 */rt_exit_critical();
}

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
IRQn_Type IRQn 对应相应函数
eg:USB_LP_CAN1_RX0_IRQn ->USB_LP_CAN1_RX0_IRQHandler
弱函数 类试与 c++的虚拟函数,子类重写就调用子类的
在这里插入图片描述

5:测试结果 如果对你又帮助,麻烦点个赞,加个关注
后面用2个机子对接下,测试下CAN通信
在这里插入图片描述

http://www.dtcms.com/a/475651.html

相关文章:

  • 昌平网站建设哪家强企业网站推广方案在哪里
  • 国外商品网站网站开发服务合同范本
  • mysql 收费 网站建设地图网站建设
  • 【Git学习】初识git:简单介绍及安装流程
  • 在线推广企业网站的方法有wordpress主题文件夹在哪里
  • 华夏润达建设有限公司网站ui设计难吗
  • 阿勒泰建设局网站wordpress技术教程
  • 新知识点背诵
  • 南昌企业建站html5 购物网站
  • 淄博高效网站建设wordpress视频播放
  • 长沙专业网站建设wordpress网站回调域
  • 帝国cms网站搬家网站模板在线制作
  • 中山品牌网站建设推广徐州企业网站排名优化
  • 10.【Linux系统编程】缓冲区详解——库缓冲区 VS 内核缓冲区
  • 做网站模版与定制的区别信通网站开发中心
  • ppt网站模板佟年做网站给KK
  • 女人做绿叶网站相亲拉人深圳网站设计x
  • 凡科网怎么做网站网络域名后缀
  • 企业网站建站 优帮云做网站其实不贵
  • 个人或主题网站建设服装效果图网站
  • 像美团这种网站怎么做的深圳外包软件开发
  • 公司网站是否做地方分站智能展厅设计公司
  • 极速网站建设广州网站优化推荐
  • 网站建设公司的会计分录蒙古文网站建设情况
  • 家具flash网站模板下载wordpress用php版本
  • QML学习笔记(三十三)QML的CheckBox
  • 吸引企业做网站网页设计分类
  • 做装饰画的行业网站reactjs wordpress
  • 《XOR》与《再次跳跃吧,俊潇!》题解
  • 四川省建设厅申报网站关键词推广排名