當前位置:首頁 > 公眾號精選 > 嵌入式云IOT技術圈
[導讀]最近在優(yōu)化公司的一款基于RT-Thread操作系統(tǒng)的液體探測儀產品。關于RT-Thread,我最開始用的是RT-Thread Nano,所以這款產品也是基于RT-Thread nano進行開發(fā)的,關于RT-Thread之前也寫了一些文章。

最近在優(yōu)化公司的一款基于RT-Thread操作系統(tǒng)的液體探測儀產品。關于RT-Thread,我最開始用的是RT-Thread Nano,所以這款產品也是基于RT-Thread nano進行開發(fā)的,關于RT-Thread之前也寫了一些文章,如下:

RT-Thread編程高階用法-函數(shù)擴展之$Sub$$與$Super$$

移植一個實時OS很難?那就手把手教你如何快速移植一個RT-Thread Nano吧!

在這個項目中就運用到了大彩串口屏,大彩串口屏是我們的優(yōu)質合作廠家,他們的技術實力相當牛逼,所以在我們的產品上運用了很多年,質量相當可觀!在很早以前我在公眾號和CSDN博客就寫過大彩串口屏使用的相關文章:

帶串口屏顯示的Bootloader

大彩串口屏是基于類似消息隊列的機制來實現(xiàn)的:

詳情可以看之前的文章或者參考大彩科技官方提供的文檔。

我們的產品是基于多頁面進行開發(fā)的,在每個頁面上又有N多個按鈕控件,用于實現(xiàn)界面的交互。

1、串口屏解析邏輯

1.1、STM32CubeMX配置

我在CubeMX上將串口配置為DMA模式,以便于高效的進行串口屏數(shù)據的處理和接收。

1.2、軟件處理邏輯

串口接收數(shù)據結構:

#define?HMI_LCD_U2_BUFFER_SIZE?100
typedef?struct{
????uint8_t??HMI_LCD_U2_Buffer[HMI_LCD_U2_BUFFER_SIZE];
}HMI_LCD_HandleTypeDef;
extern?HMI_LCD_HandleTypeDef?HMI_LCD_Handler?;

暫時定義為以上規(guī)格,方便以后拓展和維護;串口屏中斷處理函數(shù)實現(xiàn):

/**
??*?@brief?This?function?handles?USART2?global?interrupt.
??*/
void?USART2_IRQHandler(void)
{
??/*?USER?CODE?BEGIN?USART2_IRQn?0?*/
????uint32_t?i?;
????uint32_t?uart2_dma_rxlen?;
????/*獲取空閑中斷*/
????if(__HAL_UART_GET_IT_SOURCE(&huart2,?UART_IT_IDLE)?!=?RESET)
????{
????????__HAL_UART_CLEAR_IDLEFLAG(&huart2);
????????HAL_UART_DMAStop(&huart2);
????????/*獲取此次接收到的數(shù)據長度*/
????????uart2_dma_rxlen?=?HMI_LCD_U2_BUFFER_SIZE?-?(__HAL_DMA_GET_COUNTER(huart2.hdmarx));
????????/*將數(shù)據丟進大彩科技實現(xiàn)的環(huán)形隊列里*/
????????for(i?=?0;?i?????????{
????????????queue_push(HMI_LCD_Handler.HMI_LCD_U2_Buffer[i]);
????????}
????????/*重新打開DMA和空閑中斷,進行新的一輪數(shù)據接收*/
????????__HAL_UART_ENABLE_IT(&huart2,?UART_IT_IDLE);
????????HAL_UART_Receive_DMA(&huart2,?HMI_LCD_Handler.HMI_LCD_U2_Buffer,?HMI_LCD_U2_BUFFER_SIZE);
????}

??/*?USER?CODE?END?USART2_IRQn?0?*/
??HAL_UART_IRQHandler(&huart2);
??/*?USER?CODE?BEGIN?USART2_IRQn?1?*/

??/*?USER?CODE?END?USART2_IRQn?1?*/
}

在主任務中,我定義了一個串口屏任務以及信號量方式用于處理和同步串口屏上報的數(shù)據,關于創(chuàng)建和使用任務,創(chuàng)建信號量以及使用信號量的方法可以參考RT-Thread官方文檔:

為什么要采用RTOS多任務?

對于普通的項目來說,比如密碼鎖類項目,單獨的一個傳感器模塊的開發(fā),某些簡單的儀器儀表等等,對于這類場景單一,業(yè)務需求也單一的項目來說,使用狀態(tài)機或者事件驅動的方式就足以完成項目的基本功能了。

但是如果開發(fā)一個巨量代碼的工程項目,項目可能設計到傳感器數(shù)據讀取、無線數(shù)據上傳與接收、數(shù)據傳輸、UI實時刷新、算法處理等等,功能諸多還需要相互配合的情況下,那么如果還在用裸機的思想去完成,那么開發(fā)者一般會面臨以下兩個問題:

  • 設計思路過于復雜,光怎么想程序的設計思路就得想好久了
  • 設計下來的各個功能,要考慮相互配合的問題,實時性可能得不到要求

RTOS的多任務就可以解決對應的問題,它既能讓項目開發(fā)起來思路清晰,方便易維護;同時RTOS也能保證整個產品運行的實時性。

為什么要采用RTOS信號量?

信號量,俗話說就是信號的數(shù)量,它是一種任務間傳遞系統(tǒng)可用資源的機制;舉一個生產者與消費者的問題;也就是說消費者在消費了一個資源之前需要等待資源釋放,生產者生產資源以后要即時去通知其它的消費者,簡單的說就是凡事都要有個先來后到,所以信號量最常用的地方就是實現(xiàn)任務間同步。

以下是我寫的串口屏任務以及信號量的同步處理:

/***************串口液晶屏解析任務*************/
rt_sem_t?lcd_sem?=?RT_NULL;
#define?HMI_LCD_THREAD_PRIORITY?????????5
#define?HMI_LCD_THREAD_STACK_SIZE???????512
#define?HMI_LCD_THREAD_TIMESLICE????????5
static?rt_thread_t?hmi_lcd_thread?=?RT_NULL;
/*串口液晶屏線程入口函數(shù)?*/
static?void?hmi_lcd_thread_entry(void?*parameter);
/***************串口液晶屏解析任務*************/

int?main(void)
{
????rt_kprintf("create?main_thread\n");
????//.....省略其它代碼
????/*3、創(chuàng)建串口屏解析任務*/
????hmi_lcd_thread?=?rt_thread_create("lcd_thread",
??????????????????????????????????????hmi_lcd_thread_entry,?RT_NULL,
??????????????????????????????????????HMI_LCD_THREAD_STACK_SIZE,
??????????????????????????????????????HMI_LCD_THREAD_PRIORITY,?HMI_LCD_THREAD_TIMESLICE);

????/*?如果獲得線程控制塊,啟動這個線程?*/
????if?(hmi_lcd_thread?!=?RT_NULL)
????????rt_thread_startup(hmi_lcd_thread);
????//.....省略其它代碼
????while?(1)
????{
????????rt_thread_mdelay(100);
????}
}

/*串口液晶屏線程入口函數(shù)?*/
static?void?hmi_lcd_thread_entry(void?*parameter)
{
????rt_kprintf("create?lcd_thread\n");
????rt_err_t?result;
????queue_reset();
????rt_thread_mdelay(500);
????/*?創(chuàng)建一個動態(tài)信號量,初始值是?0?*/
????lcd_sem?=?rt_sem_create("lcd_sem",?0,?RT_IPC_FLAG_FIFO);

????if?(lcd_sem?==?RT_NULL)
????{
????????rt_kprintf("create?lcd_sem?failed.\n");
????????return?;
????}
????/*使能空閑中斷,打開DMA接收*/
????__HAL_UART_ENABLE_IT(&huart2,?UART_IT_IDLE);
????HAL_UART_Receive_DMA(&huart2,?HMI_LCD_Handler.HMI_LCD_U2_Buffer,?HMI_LCD_U2_BUFFER_SIZE);
????while?(1)
????{
????????/*獲取LCD信號量*/
????????result?=?rt_sem_take(lcd_sem,?RT_WAITING_FOREVER);
????????if?(RT_EOK?==?result)
????????{
????????????//進行消息處理
????????????ProcessMessage((PCTRL_MSG)lcd_handler_def.buffer,?lcd_handler_def.size);
????????}
????}
}

那么這個信號量是從哪里發(fā)來的呢?在大彩串口屏提供的tft_cmd_queue.c文件中,有一個queue_find_cmd的函數(shù),這個函數(shù)的作用就是將接收的隊列進行整合的過程,在整合完畢,確認數(shù)據是大彩串口屏協(xié)議規(guī)格的時候,即是一個完整的數(shù)據幀,我們就可以在這個位置發(fā)送信號量,主任務接收到信號量,此時就完成了數(shù)據同步的過程,主任務將我自己定義的拷貝數(shù)組拿出來后,放到消息處理函數(shù)ProcessMessage里,即完成一幀數(shù)據的解析和應用。

/*我自己定義的,用來拷貝完整的數(shù)據幀*/
typedef?struct
{
????qsize??size;
????qdata?buffer[CMD_MAX_SIZE]?;
}?lcd_handler?;
extern?lcd_handler?lcd_handler_def?;
/*大彩科技實現(xiàn)的*/
qsize?queue_find_cmd(qdata?*buffer,?qsize?buf_len)
{
????qsize?cmd_size?=?0;
????qdata?_data?=?0;

????while(queue_size()?>?0)
????{
????????//取一個數(shù)據
????????queue_pop(&_data);

????????if(cmd_pos?==?0?&&?_data?!=?CMD_qhead)?//指令第一個字節(jié)必須是幀頭,否則跳過
????????{
????????????continue;
????????}

????????if(cmd_pos?????????????buffer[cmd_pos++]?=?_data;

????????cmd_state?=?((cmd_state?<
????????//最后4個字節(jié)與幀尾匹配,得到完整幀
????????if(cmd_state?==?CMD_TAIL)
????????{
????????????cmd_size?=?cmd_pos;?//指令字節(jié)長度
????????????cmd_state?=?0;??//重新檢測幀尾巴
????????????cmd_pos?=?0;?//復位指令指針

????????????#if(CRC16_ENABLE)

????????????//去掉指令頭尾EE,尾FFFCFFFF共計5個字節(jié),只計算數(shù)據部分CRC
????????????if(!CheckCRC16(buffer?+?1,?cmd_size?-?5))?//CRC校驗
????????????????return?0;

????????????cmd_size?-=?2;//去掉CRC16(2字節(jié))
????????????#endif
????????????/*發(fā)送隊列*/
????????????lcd_handler_def.size?=?cmd_size?;
????????????memcpy(lcd_handler_def.buffer,?buffer,?cmd_size);
????????????/*給出一個信號量*/
????????????rt_sem_release(lcd_sem);
????????????queue_reset();
????????????return?cmd_size;
????????}
????}
????//沒有形成完整的一幀
????return?0;
}

由于我的項目涉及多個頁面以及多個按鈕控件的交互,所以按鈕控件的處理函數(shù)用得是最多的:

/*!
?*??\brief??按鈕控件通知
?*??\details??當按鈕狀態(tài)改變(或調用GetControlValue)時,執(zhí)行此函數(shù)
?*??\param?screen_id?畫面ID
?*??\param?control_id?控件ID
?*??\param state 按鈕狀態(tài):0彈起,1按下
?*/
void?NotifyButton(u16?screen_id,?u16?control_id,?u8??state)
{
????//TODO:?添加用戶代碼
??Menu_Select_Item(screen_id,control_id,state);
}

我是怎么來做的呢?根據之前寫表驅動狀態(tài)機的思維,將這個過程抽象成了一個框架結構,如下:

/*當前菜單*/
typedef?enum
{
????WELCOME_PAGE?=?0,
????MAIN_PAGE,
????SETTING_PAGE,
????BACKLIGHT_PAGE,
????VOLUME_PAGE,
????LANGUAGE_PAGE,
????TIMESET_PAGE,
????DB_UPDATE_PAGE,
????DATA_UP_PAGE,
????PASSWORD_PAGE,
????PASSWORD_START_PAGE,
????PROJECT_CONFIGUARE_PAGE,
????PROJECT_SELECT_PAGE,
????METAL_TEST_PAGE,
????NONMETAL_TEST_PAGE,
????USB_MODE_PAGE,
????RECOVERY_PAGE,
????DEVICE_TEST_PAGE,
????CREATE_NONMETAL_SAMPLE_LIB_PAGE1,
????CREATE_NONMETAL_SAMPLE_LIB_PAGE2,
????CREATE_NONMETAL_SAMPLE_LIB_PAGE3,
????CLEART_NONMETAL_SAMPLE_LIB_PAGE4,
????CREATE_NEW_ST_SAMPLE_LIB_PAGE,
????CLEAR_SAMPLE_LIB_PAGE,
????CLEAR_SAMPLE_LIB_CONFIRM_PAGE,
????CLEAR_DETECT_CONFIRM_PAGE
}?OP_PAGE;

typedef?void?(*menu_op_func)(uint16_t,?uint8_t);
typedef?struct?OP_STRUCT
{
????uint16_t?op_menu?;?????/*操作菜單*/
????menu_op_func?opfun?;??/*帶參數(shù)的操作方法*/
}?OP_MENU_PAGE;

typedef?struct
{
????/*界面操作游標*/
????uint16_t?flow_cursor?;
}?Cursor?;
extern?Cursor?Flow_Cursor?;


/*菜單初始化*/
void?Menu_Init(void);
/*跳轉到下一個菜單*/
void?Menu_Jump(uint16_t?screen_id);
/*菜單操作*/
void?Menu_Select_Item(uint16_t?screen_id,?uint16_t?control_id,?uint8_t?state);

然后一堆復制粘貼,就復用了以前寫的一套代碼:

基于事件型表驅動法菜單框架之小熊派簡易氣體探測器實戰(zhàn)項目開發(fā)(上)

基于事件型表驅動法菜單框架之小熊派簡易氣體探測器實戰(zhàn)項目開發(fā)(中)

TencentOS tiny危險氣體探測儀產品級開發(fā)重磅高質量更新(Flash都快用完了!)

成功的將跳轉表的思想運用到了串口屏多頁面多按鈕的交互菜單產品開發(fā)上:

Cursor?Flow_Cursor?;

/*菜單操作表定義*/
static?OP_MENU_PAGE?g_opStruct[]?=
{
????{WELCOME_PAGE,??NULL},???????????/*歡迎頁面*/
????{MAIN_PAGE??????,?main_page_process},?????/*主頁面*/
??{SETTING_PAGE???,?setting_page_process},???/*設置頁面*/
??{BACKLIGHT_PAGE?,?backlight_page_process},??/*背光設置頁面*/
??{VOLUME_PAGE??,?volume_page_process},????/*音量設置頁面*/
??{LANGUAGE_PAGE,NULL},
??{TIMESET_PAGE??,?datetime_page_process},???/*日期時間設置頁面*/
??{DB_UPDATE_PAGE,NULL},
??{DATA_UP_PAGE,NULL},
??{PASSWORD_PAGE,NULL},
??{PASSWORD_START_PAGE,NULL},
??{PROJECT_CONFIGUARE_PAGE,engineer_password_page_process},?/*工程配置頁面密碼輸入*/
??{PROJECT_SELECT_PAGE,engineer_select_page_process},?/*工程選擇頁面*/
??{METAL_TEST_PAGE,metal_test_page_process},
??{NONMETAL_TEST_PAGE,nonmetal_test_page_process},
??{USB_MODE_PAGE,NULL},
??{RECOVERY_PAGE,recovery_page_process},
??{DEVICE_TEST_PAGE,NULL},
??{CREATE_NONMETAL_SAMPLE_LIB_PAGE1,NULL},
??{CREATE_NONMETAL_SAMPLE_LIB_PAGE2,NULL},
??{CREATE_NONMETAL_SAMPLE_LIB_PAGE3,NULL},
??{CLEART_NONMETAL_SAMPLE_LIB_PAGE4,NULL},
??{CREATE_NEW_ST_SAMPLE_LIB_PAGE,NULL},
??{CLEAR_SAMPLE_LIB_PAGE,NULL},
??{CLEAR_SAMPLE_LIB_CONFIRM_PAGE,NULL},
??{CLEAR_DETECT_CONFIRM_PAGE,NULL},
};


/*跳轉到表所對應的頁面*/
static?int?JUMP_Table(int16_t?op,?int16_t?control_id,?uint8_t?state)
{
????assert(op?>=?sizeof(g_opStruct)?/?sizeof(g_opStruct[0]));
????assert(op???if(g_opStruct[op].opfun?!=?NULL)
???g_opStruct[op].opfun(control_id,?state);
??else
???printf("current_page?op?is?null!\n");
????return?0?;
}

/*菜單初始化*/
void?Menu_Init(void)
{
????current_screen_id?=?MAIN_PAGE?;
????Flow_Cursor.flow_cursor?=?current_screen_id?;
????SetScreen(Flow_Cursor.flow_cursor);
}

/*跳轉到下一個菜單*/
void?Menu_Jump(uint16_t?screen_id)
{
????current_screen_id?=?screen_id?;
????Flow_Cursor.flow_cursor?=?current_screen_id?;
????SetScreen(Flow_Cursor.flow_cursor);
}

/*菜單選擇項*/
void?Menu_Select_Item(uint16_t?screen_id,?uint16_t?control_id,?uint8_t?state)
{
????JUMP_Table(screen_id,?control_id,?state);
}

以背光調節(jié)為例,結合大彩科技的VisualTFT界面開發(fā)軟件所畫的UI如下:

根據大彩串口屏背光調節(jié)實現(xiàn)代碼邏輯實現(xiàn)如下:

/*************按鈕****************/
#define?BACKLIGHT_SUB_BUTTON??1
#define?BACKLIGHT_ADD_BUTTON??2
#define?BACKLIGHT_BACK_BUTTON?8
/*************按鈕****************/
#define?BACKLIGHT_LEVEL????3
#define?BACKLIGHT_LEVEL_TEXT??5

BackLight_Handler?backlight_handler?;

//背光調節(jié)
void?Backlight_level_set(uint8_t?level)
{
????uint8_t?text_buf[20]?=?{0};

????switch(level)
????{
????case?0:
????????SetBackLight(200);
????????break?;

????case?1:
????????SetBackLight(160);
????????break?;

????case?2:
????????SetBackLight(120);
????????break?;

????case?3:
????????SetBackLight(80);
????????break?;

????case?4:
????????SetBackLight(40);
????????break?;

????case?5:
????????SetBackLight(0);
????????break?;

????default:
????????break?;
????}

????para_stroge.Backligh_level?=?level?;
????sprintf((char?*)text_buf,?"當前亮度:%d",?level);
????SetTextValue(BACKLIGHT_PAGE,?BACKLIGHT_LEVEL_TEXT,?text_buf);
????AnimationPlayFrame?(BACKLIGHT_PAGE,?BACKLIGHT_LEVEL,?level);
}

/*背光頁面初始化*/
void?backlight_page_init(void)
{
????backlight_handler.level?=?para_stroge.Backligh_level?;
????Menu_Jump(BACKLIGHT_PAGE);
}

/*背光頁面操作*/
void?backlight_page_process(uint16_t?control_id,?uint8_t?state)
{
????switch(control_id)
????{
????case?BACKLIGHT_SUB_BUTTON:
????????(backlight_handler.level?>?0)???(backlight_handler.level--)?:?(backlight_handler.level?=?0);
????????Backlight_level_set(backlight_handler.level);
????????break?;

????case?BACKLIGHT_ADD_BUTTON:
????????(backlight_handler.level?????????Backlight_level_set(backlight_handler.level);
????????break?;

????case?BACKLIGHT_BACK_BUTTON:
????????WriteUserFlash(0x00000000,?1,?¶_stroge.Backligh_level);
????????Menu_Jump(SETTING_PAGE);
????????break?;

????default:
????????break?;
????}
}

然后此時我們需要在菜單框架里將backlight_page_process注冊到對應的區(qū)域,這樣在對應的界面才能對相應的按鈕事件進行一一關聯(lián):

由于代碼涉及了一些產品的核心需求,故不能完全開放,但與大彩串口屏多界面交互的思想就是狀態(tài)機+表驅動,這是任何一個項目都可以應用的。

2、騰訊云征文

本人受邀擔任第一季【年度征文】活動的評審,朋友圈轉發(fā)活動詳情海報和我的個人評審海報并保留至活動截稿日 12月23 日,將截圖發(fā)送給發(fā)送給云+社區(qū)小編(微信號:juxiaoka66)參與抽獎,將抽取5位讀者每人一張騰訊視頻月卡(云+社區(qū)定制卡面)活動詳情鏈接:https://cloud.tencent.com/developer/article/1752258

往期精彩

RT-Thread編程高階用法-函數(shù)擴展之$Sub$$與$Super$$

上海出差之行--領略外灘美景、RT-Thread總部之旅、嵌友面基、返程記錄

移植一個實時OS很難?那就手把手教你如何快速移植一個RT-Thread Nano吧!

整理了很久之前在碼云/Github/CSDN上收藏的嵌入式產品級項目分享開源

覺得本次分享的文章對您有幫助,隨手點[在看]并轉發(fā)分享,也是對我的支持。

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或將催生出更大的獨角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據媒體報道,騰訊和網易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據產業(yè)博覽會開幕式在貴陽舉行,華為董事、質量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據產業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經營業(yè)績穩(wěn)中有升 落實提質增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉