2016年8月18日 星期四
STM32 Flash 之 .icf檔
在使用Flash功能時,為了確保資料寫入ROM後,下一次燒code到機器時,寫入的資料不希望被清掉,可以修改*.icf檔,IAR會根據這份文件,來避開某段FLASH_SECTOR。
由於Sector3的位置在0x0800C000~0x0800FFFF之間,因此將ROM1_end_ 設定為 0x800C000-1,ROM2_start_設定為0x0800FFFF+1,最後在ROM_region將ROM1跟ROM2照原本的方式設定上去即可。
2.切到【Linker】頁面,勾選Override default,並將選擇檔案存放路徑。
3.Check the linker map file,看是否有成功linker到載入的檔案。
Flash模組的組成
可由reference manual中找到自己平台的Flash module。
Step 1. 修改*.icf檔
假設我們需要使用Sector3來存放我們不想被清掉的資料,那我們的*.icf檔的寫法如下。
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0803FFFF;
define symbol __ICFEDIT_region_ROM1_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM1_end__ = 0x0800BFFF;
define symbol __ICFEDIT_region_ROM2_start__ = 0x08010000;
define symbol __ICFEDIT_region_ROM2_end__ = 0x0803FFFF;
...
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region ROM_region = mem:[from __ICFEDIT_region_ROM1_start__ to __ICFEDIT_region_ROM1_end__] | mem:[from __ICFEDIT_region_ROM2_start__ to __ICFEDIT_region_ROM2_end__];由於Sector3的位置在0x0800C000~0x0800FFFF之間,因此將ROM1_end_ 設定為 0x800C000-1,ROM2_start_設定為0x0800FFFF+1,最後在ROM_region將ROM1跟ROM2照原本的方式設定上去即可。
Step 2.在IDE中載入*.icf檔
1.在Project右鍵選擇【Options...】
3.Check the linker map file,看是否有成功linker到載入的檔案。
2016年8月15日 星期一
2016年7月27日 星期三
Report Descriptor之ReportID功能
在做MCU使用USB通信協定與PC端溝通的實驗時,發現在linux與windows上有了不同的狀況。在windows用Bus Hound與自己寫的程式,都能正常地抓到MCU端丟出來的資料,但在Linux上卻有狀況。
最後追到了Report Descriptor描述表中的ReportID這個項目,發現MUC端在Report Descriptor中有定義ReportID的話,送出的Data必須在最前面加上ReportID,不然Linux端會抓不到資料,後來去翻了HID1_11的文件,終於搞清楚是怎麼一回事了。
在HID1_11文件5.6 Reports有提到Report ID的特性:
1.Report ID items are used to indicate which data fields are represented in each report structure.
2.A Report ID item tag assigns a 1-byte identification prefix to each report transfer.
3.If no Report ID item tags are present in the Report descriptor, it can be assumed that only one Input, Output, and Feature report structure.
這裡總結一下以上的描述,當你的Report Descriptor只有一個Input, Output, 或是 Feature的話,可以不用定義Report ID,但是有用到Report ID的話,必須在每一個report transfer最前面加入一個Byte的Report ID參數。
最後追到了Report Descriptor描述表中的ReportID這個項目,發現MUC端在Report Descriptor中有定義ReportID的話,送出的Data必須在最前面加上ReportID,不然Linux端會抓不到資料,後來去翻了HID1_11的文件,終於搞清楚是怎麼一回事了。
在HID1_11文件5.6 Reports有提到Report ID的特性:
1.Report ID items are used to indicate which data fields are represented in each report structure.
2.A Report ID item tag assigns a 1-byte identification prefix to each report transfer.
3.If no Report ID item tags are present in the Report descriptor, it can be assumed that only one Input, Output, and Feature report structure.
這裡總結一下以上的描述,當你的Report Descriptor只有一個Input, Output, 或是 Feature的話,可以不用定義Report ID,但是有用到Report ID的話,必須在每一個report transfer最前面加入一個Byte的Report ID參數。
2016年7月24日 星期日
STM32F411xC 基礎教學 :Timer使用方式
STM32F411xC/E Timer可分為以下三組:
1. Advanced-control timer(TIM1)
2.General-purpose timers(TIM2 to TIM5)
3.General-purpose timers(TIM9 to TIM11)
ps. TIM8 在STM32F411xC/E中是不能用的。
這三組有各自相對應的block diagram,根據使用不同的AHB/APB2或AHB/APB1,也會對時間計算有不同的結果,細節可自行參考RM0383 Reference manual。
STM32 Timer 每次進入中斷時間間隔計算方式:
((1+TIM_Prescaler) / 系統頻率)*(1+TIM_Period) =
((1+9599)/96Mhz) * (1+9999) = 1 sec。
main function:
Debug GPIO(PC15) init:
TIM2 init:
TIM2 IRQHandler:
驗證:
下圖為邏輯分析儀測量的結果,由圖中可看出PC15每一秒鐘會觸發一次,符合我們所預期的時間。
1. Advanced-control timer(TIM1)
2.General-purpose timers(TIM2 to TIM5)
3.General-purpose timers(TIM9 to TIM11)
ps. TIM8 在STM32F411xC/E中是不能用的。
這三組有各自相對應的block diagram,根據使用不同的AHB/APB2或AHB/APB1,也會對時間計算有不同的結果,細節可自行參考RM0383 Reference manual。
STM32 Timer 每次進入中斷時間間隔計算方式:
((1+TIM_Prescaler) / 系統頻率)*(1+TIM_Period) =
((1+9599)/96Mhz) * (1+9999) = 1 sec。
main function:
int main(void)
{
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
debug_io_init();
TIM2_Init();
/* Infinite loop */
while (1)
}
Debug GPIO(PC15) init:
void debug_io_init( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC, ENABLE );
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init( GPIOC, &GPIO_InitStructure );
GPIO_ResetBits(GPIOC, GPIO_Pin_15);
GPIO_SetBits(GPIOC, GPIO_Pin_15);
}
TIM2 init:
void TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Enable the TIM2 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 10000-1;
TIM_TimeBaseStructure.TIM_Prescaler = 9600-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
TIM2 IRQHandler:
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
GPIO_ResetBits(GPIOC, GPIO_Pin_15);
GPIO_SetBits(GPIOC, GPIO_Pin_15);
GPIO_ToggleBits(GPIOC, GPIO_Pin_0);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
驗證:
下圖為邏輯分析儀測量的結果,由圖中可看出PC15每一秒鐘會觸發一次,符合我們所預期的時間。
2016年7月21日 星期四
STM32F411xC 基礎教學 :SysTick使用方式
本篇基於STM32F411xC平台,紀錄SysTick的使用方式。
實驗說明:
使用GPIO(PC15)來觀察程式進入SysTick_Handler,到下一次進入SysTick_Handler的時間。
Main function:
SysTick_Handler function:
使用邏輯分析儀觀察GPIO:
由下圖觀察到,由於我們在SysTick_Handler中有做將GPIO拉high->low的動作,因此觀察GPIO high的間格時間,即可知道SysTick是否依我們的設計每10m觸發一次。

實驗說明:
使用GPIO(PC15)來觀察程式進入SysTick_Handler,到下一次進入SysTick_Handler的時間。
Main function:
int main(void)
{
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config(960000+1);//10ms中斷一次
while ( 1 ) {
if (flag_10ms_period){
flag_10ms_period = 0;
//每10ms要執行的function
}
}
}
SysTick_Handler function:
extern volatile uint8_t flag_10ms_period;
void SysTick_Handler(void)
{
GPIO_SetBits(GPIOC, GPIO_Pin_15);
flag_10ms_period = 1;
GPIO_ResetBits(GPIOC, GPIO_Pin_15);
}
SysTick會依照你使用MCU的系統頻率來計算中斷次數,下圖為STM32F411xC的SysClk,這次範例要計算每10ms中斷一次,計算方式為96000MHz(實體頻率) * 10,並在main.c中加入SysTick_Config(96000*10)。
| STM32F411xC Sysclk |
由下圖觀察到,由於我們在SysTick_Handler中有做將GPIO拉high->low的動作,因此觀察GPIO high的間格時間,即可知道SysTick是否依我們的設計每10m觸發一次。

2016年7月20日 星期三
STM32F411xC 基礎教學 : Button與 LED使用方式
本篇基於STM32F411xC平台,記錄板端Button與Led的使用方式。
實驗說明:
按下Button1(PD2)點亮Led2(PC0),Button2(PC12)點亮Led5(PC1)。
Button 初始化:
LED 初始化:
Main function:
實驗說明:
按下Button1(PD2)點亮Led2(PC0),Button2(PC12)點亮Led5(PC1)。
Button 初始化:
//button1:PD2
//button2:PC12
void Btn_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init( GPIOD, &GPIO_InitStructure );
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init( GPIOC, &GPIO_InitStructure );
}
LED 初始化:
//LED2:PC0
//LED5:PC1
//LED3:PC2
//LED4:PC3
void gpio_led_config( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init( GPIOC, &GPIO_InitStructure );
}
Main function:
int main(void)
{
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
Btn_Init();
gpio_led_config();
while ( 1 ) {
//Sw1
if (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_2) == 0){
//turn on PC0:LED2
GPIO_SetBits(GPIOC, GPIO_Pin_0);
}else{
//turn off PC0:LED2
GPIO_ResetBits(GPIOC, GPIO_Pin_0);
}
//Sw2
if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_12) == 0){
GPIO_SetBits(GPIOC, GPIO_Pin_1);
}else{
GPIO_ResetBits(GPIOC, GPIO_Pin_1);
}
}
}
2016年6月29日 星期三
MFC AfxBeginThread 使用方法
1.在主要functino的class中使用CWinThread宣告Thread指標。
2.定義MyThread(),必將要執行的工作寫在裡面,可搭配
【在 MFC 上使用lusb-win32】一文中提到的Console _cprintf來觀察是否有進入MyThread()。
3.在欲開啟MyThread的Dialog按鈕程式碼中創建Thread
4.使用TerminateThread關閉MyThread
執行結果:
當按下Button1後,會一直執行MyThread()中的「_cprintf("Hello World!\n");」,按下Button2後可以看到確實將MyThread()給關掉,Console不再印出Hello World!!字串。
class CMFCApplication1Dlg : public CDialogEx
{
public:
CMFCApplication1Dlg(CWnd* pParent = NULL); // 標準建構函式
CWinThread* pThread;
// 對話方塊資料
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX);
...
// 程式碼實作
protected:
...
}
2.定義MyThread(),必將要執行的工作寫在裡面,可搭配
【在 MFC 上使用lusb-win32】一文中提到的Console _cprintf來觀察是否有進入MyThread()。
UINT MyThread(LPVOID LParam)
{
_cprintf("Start MyThread...\n");
...
while(1){
_cprintf("Hello World!\n");
}
return(0);
}
3.在欲開啟MyThread的Dialog按鈕程式碼中創建Thread
void CMFCApplication1Dlg::OnBnClickedButton1()
{
pThread = AfxBeginThread(MyThread, NULL);
...
}
4.使用TerminateThread關閉MyThread
void CMFCApplication1Dlg::OnBnClickedButton2()
{
// TODO: 在此加入控制項告知處理常式程式碼
TerminateThread(pThread->m_hThread, 0);
_cprintf("Close MyThread............\n");
}
執行結果:
當按下Button1後,會一直執行MyThread()中的「_cprintf("Hello World!\n");」,按下Button2後可以看到確實將MyThread()給關掉,Console不再印出Hello World!!字串。
在 MFC 上使用lusb-win32
將lusb-win32導入MFC專案步驟:
1.建立MFC專案後,將"libusb.lib"與"libusb0.dll"丟到所建立的專案目錄下。
2.在標頭檔中加入"lusb0_usb.h"檔案,加入"#pragma comment(lib, "libusb")"至"lusb0_usb.h"或是專案主程式XXX.cpp中來link libusb.lib。
3.在專案主程式XXX.cpp中include "lusb0_usb.h"後,就可以開始撰寫你要的功能了。
MFC(VS2015 Community)中的Console printf
1.添加標頭檔
2.新增InitConsoleWindow()至主程式中
3.在::OnInitDialog()初始化函數中添加InitConsoleWindow()
4.由於VS2015使用網路上printf的方式無法輸出到console上,有另外找到解決方法,加入"conio.h"用_cprintf可以解決無法輸出到console上的問題。
1.建立MFC專案後,將"libusb.lib"與"libusb0.dll"丟到所建立的專案目錄下。
2.在標頭檔中加入"lusb0_usb.h"檔案,加入"#pragma comment(lib, "libusb")"至"lusb0_usb.h"或是專案主程式XXX.cpp中來link libusb.lib。
3.在專案主程式XXX.cpp中include "lusb0_usb.h"後,就可以開始撰寫你要的功能了。
MFC(VS2015 Community)中的Console printf
1.添加標頭檔
#include "io.h"
#include "fcntl.h"
#include "conio.h"
2.新增InitConsoleWindow()至主程式中
void InitConsoleWindow()
{
int nCrt = 0;
FILE* fp;
AllocConsole();
nCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
fp = _fdopen(nCrt, "w");
*stdout = *fp;
setvbuf(stdout, NULL, _IONBF, 0);
}3.在::OnInitDialog()初始化函數中添加InitConsoleWindow()
BOOL CUsbXSTM32F4xxDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
...
}
SetIcon(m_hIcon, TRUE); // 設定大圖示
SetIcon(m_hIcon, FALSE); // 設定小圖示
InitConsoleWindow();
return TRUE; // 傳回 TRUE,除非您對控制項設定焦點
}
4.由於VS2015使用網路上printf的方式無法輸出到console上,有另外找到解決方法,加入"conio.h"用_cprintf可以解決無法輸出到console上的問題。
2016年6月21日 星期二
MinGW+MSYS 使用 libusb-win32 使PC與MCU通信
本篇文章將介紹如何使用libusb-win32來撰寫USB開發程式
準備軟體:
libusb-win32(1.2.6.0)
MinGW&MSYS: http://www.mingw.org/
MinGw教學:
http://shunyuan-chou.blogspot.tw/2010/02/2-mingw-windows-gnu.html
範例程式:
Usblib-win32-HID
下載libusb-win32-bin-1.2.6.0.zip解壓縮後,可以看到如下圖中有多個目錄。
當編譯程式告知缺少libusb0.sys或是libusb0.dll時,可至bin資料夾中選擇符合你環境的設定檔。
lib目錄下有不同compiler可以使用的library,由於本實驗使用gcc,所以將gcc下的libusb.a複製到撰寫程式的目錄下。
首先在C:\MiGW 下建立Project\HID,將libusb-win32-bin-1.2.6.0中的example與include複製到HID目錄下,libusb.a & libusb0.dll複製到example目錄下。
將bulk.c中定義的MY_VID與MY_PID更改為你要測試USB device 的參數。
編譯指令:
gcc -l ../include -g -c bulk.c -o bulk.o
gcc -g bulk.o libusb.a -o USB.exe
編譯完後可以執行USB.exe,測試是否有抓到USB device。
成功開啟USB裝置後,就可以使用lusb0_usb.h中的function來與MCU溝通拉。
準備軟體:
libusb-win32(1.2.6.0)
MinGW&MSYS: http://www.mingw.org/
MinGw教學:
http://shunyuan-chou.blogspot.tw/2010/02/2-mingw-windows-gnu.html
範例程式:
Usblib-win32-HID
下載libusb-win32-bin-1.2.6.0.zip解壓縮後,可以看到如下圖中有多個目錄。
當編譯程式告知缺少libusb0.sys或是libusb0.dll時,可至bin資料夾中選擇符合你環境的設定檔。
lib目錄下有不同compiler可以使用的library,由於本實驗使用gcc,所以將gcc下的libusb.a複製到撰寫程式的目錄下。
首先在C:\MiGW 下建立Project\HID,將libusb-win32-bin-1.2.6.0中的example與include複製到HID目錄下,libusb.a & libusb0.dll複製到example目錄下。
將bulk.c中定義的MY_VID與MY_PID更改為你要測試USB device 的參數。
編譯指令:
gcc -l ../include -g -c bulk.c -o bulk.o
gcc -g bulk.o libusb.a -o USB.exe
編譯完後可以執行USB.exe,測試是否有抓到USB device。
成功開啟USB裝置後,就可以使用lusb0_usb.h中的function來與MCU溝通拉。
int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);
2016年6月16日 星期四
Bus Hound USB Get_Descriptor Command之分析
使用Bus Hound 發送Get_Descriptor command
目前先將實驗解果稍作分析,未來對USB有更深入的了解後,有機會在更新這部分的資料。
Value: 00 01
USB回傳訊息:會回傳USB的Device Descriptor,裡面包含了VID&PID的資料。

Device Descriptor 內容可對照usb_2.0 spec 表9-8。

目前先將實驗解果稍作分析,未來對USB有更深入的了解後,有機會在更新這部分的資料。
- Cmd: 80 06 00 01 00 00 FF 00
Value: 00 01
USB回傳訊息:會回傳USB的Device Descriptor,裡面包含了VID&PID的資料。

Device Descriptor 內容可對照usb_2.0 spec 表9-8。
- Cmd: 80 06 00 02 00 00 FF 00
Value: 00 01
USB回傳訊息:會回傳HID configuration descriptor,可以看出此裝置是甚麼樣的device(Mouse, Keyboard...)。
USB回傳訊息:會回傳HID configuration descriptor,可以看出此裝置是甚麼樣的device(Mouse, Keyboard...)。
HID Configuration descriptor的架構如下
Configuration descriptor
...Interface descriptor
......HID descriptor
.........Endpoint descriptor (for
HID interrupt IN Endpoint)
.........Optional Endpoint descriptor (for HID interrupt Out Endpoint)
詳細內容可參考usb_2.0 spec
9.6.3 Configuration
9.6.5 Interface
9.6.6 Endpoint
- Cmd: 80 06 01 03 00 00 FF 00~80 06 05 03 00 00 FF 00
此command可以取得儲存在USB的string。
2016年6月15日 星期三
STM32F4XX USB 透過 Bus Hound 存取 HidRaw Device
本篇參考假濕汀的Blogger,並修改文章中所提供的範例程式,透過Bus Hound監聽軟體來驗證傳輸資料是否正確。
由於原作者範例只能將hidraw中的資料由Device端送至PC端,無法由PC端送資料至Device端,因此這裡將介紹如何解決此問題,由於USB這邊是第一次接觸,還有很多不了解的地方,有問題還請各位前輩可以提出來。
假濕汀的Blogger:
http://iamjustinwang.blogspot.tw/2015/11/stm32f401411-hid-testkeyboard-mouse.html
程式連結:
STM32FXXX_HID
修改部分:
原程式中只有一個input Endpoint,因此須改改interface中定義Endpoint的數量。
增加Output Endpoint Descriptor
由於原作者範例只能將hidraw中的資料由Device端送至PC端,無法由PC端送資料至Device端,因此這裡將介紹如何解決此問題,由於USB這邊是第一次接觸,還有很多不了解的地方,有問題還請各位前輩可以提出來。
假濕汀的Blogger:
http://iamjustinwang.blogspot.tw/2015/11/stm32f401411-hid-testkeyboard-mouse.html
程式連結:
STM32FXXX_HID
修改部分:
- usbd_hid_core.c:增加USB_HID_DataOut function
- USBD_HID_CfgDesc array中需修改項目
原程式中只有一個input Endpoint,因此須改改interface中定義Endpoint的數量。
增加Output Endpoint Descriptor
- main.c
- Bus Hound驗證:
- 1.點選interrupt Out
- 2.輸入要送至Device端的資料與資料大小
- 3.點選Run,將資料送出
由上圖可觀察到,送出資料後,讀出hidraw data為PC端所輸入的資料,我們已成功將資料送至Device端。
訂閱:
文章 (Atom)


