一、platform總線、設備與驅動
1.一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對于本身依附于PCI、USB、I2C、SPI等的設備而言,這自然不是問題,
但是在嵌入式系統里面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設等確不依附于此類總線。
基于這一背景,Linux發明了一種虛擬的總線,稱為platform總線,相應的設備稱為platform_device,而驅動成為 platform_driver。
2.注意,所謂的platform_device并不是與字符設備、塊設備和網絡設備并列的概念,而是Linux系統提供的一種附加手段,
例如,在 S3C6410處理器中,把內部集成的I2C、RTC、SPI、LCD、看門狗等控制器都歸納為platform_device,而它們本身就是字符設備。
3.基于Platform總線的驅動開發流程如下:
(1)定義初始化platform bus
(2)定義各種platform devices
(3)注冊各種platform devices
(4)定義相關platform driver
(5)注冊相關platform driver
(6)操作相關設備
4.平臺相關結構
//platform_device結構體
struct platform_device {
const char * name;/* 設備名 */
u32 id;//設備id,用于給插入給該總線并且具有相同name的設備編號,如果只有一個設備的話填-1。
struct device dev;//結構體中內嵌的device結構體。
u32 num_resources;/* 設備所使用各類資源數量 */
struct resource * resource;/* //定義平臺設備的資源*/
};
//平臺資源結構
struct resource {
resource_size_t start; //定義資源的起始地址
resource_size_t end; //定義資源的結束地址
const char *name; //定義資源的名稱
unsigned long flags; //定義資源的類型,比如MEM,IO,IRQ,DMA類型
struct resource *parent, *sibling, *child;
};
//設備的驅動:platform_driver這個結構體中包含probe()、remove()、shutdown()、suspend()、 resume()函數,通常也需要由驅動實現。
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct pm_ext_ops *pm;
struct device_driver driver;
};
//系統中為platform總線定義了一個bus_type的實例platform_bus_type,
struct bus_type platform_bus_type = {
.name = “platform”,
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
//這里要重點關注其match()成員函數,正是此成員表明了platform_device和platform_driver之間如何匹配。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev;
pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
//匹配platform_device和platform_driver主要看二者的name字段是否相同。
//對platform_device的定義通常在BSP的板文件中實現,在板文件中,將platform_device歸納為一個數組,最終通過platform_add_devices()函數統一注冊。
//platform_add_devices()函數可以將平臺設備添加到系統中,這個函數的 原型為:
int platform_add_devices(struct platform_device **devs, int num);
//該函數的第一個參數為平臺設備數組的指針,第二個參數為平臺設備的數量,它內部調用了platform_device_register()函 數用于注冊單個的平臺設備。
1. platform bus總線先被kenrel注冊。
2. 系統初始化過程中調用platform_add_devices或者platform_device_register,將平臺設備(platform devices)注冊到平臺總線中(platform bus)
3. 平臺驅動(platform driver)與平臺設備(platform device)的關聯是在platform_driver_register或者driver_register中實現,一般這個函數在驅動的初始化過程調用。
通過這三步,就將平臺總線,設備,驅動關聯起來。
二.Platform初始化
系統啟動時初始化時創建了platform_bus總線設備和platform_bus_type總線,platform總線是在內核初始化的時候就注冊進了內核。
內核初始化函數kernel_init()中調用了do_basic_setup() ,該函數中調用driver_init(),該函數中調用platform_bus_init(),我們看看platform_bus_init()函數:
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup(); //清除platform設備鏈表
//該函數把設備名為platform 的設備platform_bus注冊到系統中,其他的platform的設備都會以它為parent。它在sysfs中目錄下.即 /sys/devices/platform。
//platform_bus總線也是設備,所以也要進行設備的注冊
//struct device platform_bus = {
//.init_name = "platform",
//};
error = device_register(&platform_bus);//將平臺bus作為一個設備注冊,出現在sys文件系統的device目錄
if (error)
return error;
//接著bus_register(&platform_bus_type)注冊了platform_bus_type總線.
/*
struct bus_type platform_bus_type = {
.name = “platform”,
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
*/
//默認platform_bus_type中沒有定義probe函數。
error = bus_register(&platform_bus_type);//注冊平臺類型的bus,將出現sys文件系統在bus目錄下,創建一個platform的目錄,以及相關屬性文件
if (error)
device_unregister(&platform_bus);
return error;
}
//總線類型match函數是在設備匹配驅動時調用,uevent函數在產生事件時調用。
//platform_match函數在當屬于platform的設備或者驅動注冊到內核時就會調用,完成設備與驅動的匹配工作。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);//比較設備和驅動的名稱是否一樣
}
static const struct platform_device_id *platform_match_id(struct platform_device_id *id,struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
//不難看出,如果pdrv的id_table數組中包含了pdev->name,或者drv->name和pdev->name名字相同,都會認為是匹配成功。
//id_table數組是為了應對那些對應設備和驅動的drv->name和pdev->name名字不同的情況。
//再看看platform_uevent()函數:platform_uevent 熱插拔操作函數
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct platform_device *pdev = to_platform_device(dev);
add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, (pdev->id_entry) ? pdev->id_entry->name : pdev->name);
return 0;
}
//添加了MODALIAS環境變量,我們回顧一下:platform_bus. parent->kobj->kset->uevent_ops為device_uevent_ops,bus_uevent_ops的定義如下:
static struct kset_uevent_ops device_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
//當調用device_add()時會調用kobject_uevent(&dev->kobj, KOBJ_ADD)產生一個事件,這個函數中會調用相應的kset_uevent_ops的uevent函數,
三.Platform設備的注冊
我們在設備模型的分析中知道了把設備添加到系統要調用device_initialize()和platform_device_add(pdev)函數。
Platform設備的注冊分兩種方式:
1.對于platform設備的初注冊,內核源碼提供了platform_device_add()函數,輸入參數platform_device可以是靜態的全局設備,它是進行一系列的操作后調用device_add()將設備注冊到相應的總線(platform總線)上,
內核代碼中platform設備的其他注冊函數都是基于這個函數,如platform_device_register()、platform_device_register_simple()、platform_device_register_data()等。
2.另外一種機制就是動態申請platform_device_alloc()一個platform_device設備,然后通過platform_device_add_resources及platform_device_add_data等添加相關資源和屬性。
無論哪一種platform_device,最終都將通過platform_device_add這冊到platform總線上。
區別在于第二步:其實platform_device_add()包括device_add(),不過要先注冊resources,然后將設備掛接到特定的platform總線。
第一種平臺設備注冊方式
//platform_device是靜態的全局設備,即platform_device結構的成員已經初始化完成
//直接將平臺設備注冊到platform總線上
/*platform_device_register和device_register的區別:
(1).主要是有沒有resource的區別,前者的結構體包含后面,并且增加了struct resource結構體成員,后者沒有。
platform_device_register在device_register的基礎上增加了struct resource部分的注冊。
由此。可以看出,platform_device---paltform_driver_register機制與device-driver的主要區別就在于resource。
前者適合于具有獨立資源設備的描述,后者則不是。
(2).其實linux的各種其他驅動機制的基礎都是device_driver。只不過是增加了部分功能,適合于不同的應用場合.
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);//初始化platform_device內嵌的device
return platform_device_add(pdev);//把它注冊到platform_bus_type上
}
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;//設置父節點,即platform_bus作為總線設備的父節點,其余的platform設備都是它的子設備
//platform_bus是一個設備,platform_bus_type才是真正的總線
pdev->dev.bus = &platform_bus_type;//設置platform總線,指定bus類型為platform_bus_type
//設置pdev->dev內嵌的kobj的name字段,將platform下的名字傳到內部device,最終會傳到kobj
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, "%s", pdev->name);
//初始化資源并將資源分配給它,每個資源的它的parent不存在則根據flags域設置parent,flags為IORESOURCE_MEM,
//則所表示的資源為I/O映射內存,flags為IORESOURCE_IO,則所表示的資源為I/O端口。
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)//資源名稱為NULL則把設備名稱設置給它
r->name = dev_name(&pdev->dev);
p = r->parent;//取得資源的父節點,資源在內核中也是層次安排的
if (!p) {
if (resource_type(r) == IORESOURCE_MEM) //如果父節點為NULL,并且資源類型為IORESOURCE_MEM,則把父節點設置為iomem_resource
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)//否則如果類型為IORESOURCE_IO,則把父節點設置為ioport_resource
p = &ioport_resource;
}
//從父節點申請資源,也就是出現在父節點目錄層次下
if (p && insert_resource(p, r)) {
printk(KERN_ERR "%s: failed to claim resource %d\n",dev_name(&pdev->dev), i);ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",dev_name(&pdev->dev), dev_name(pdev->dev.parent));
//device_creat() 創建一個設備并注冊到內核驅動架構...
//device_add() 注冊一個設備到內核,少了一個創建設備..
ret = device_add(&pdev->dev);//就在這里把設備注冊到總線設備上,標準設備注冊,即在sys文件系統中添加目錄和各種屬性文件
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
4.第二種平臺設備注冊方式
//先分配一個platform_device結構,對其進行資源等的初始化
//之后再對其進行注冊,再調用platform_device_register()函數
struct platform_device * platform_device_alloc(const char *name, int id)
{
struct platform_object *pa;
/*
struct platform_object {
struct platform_device pdev;
char name[1];
};
*/
pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);//該函數首先為platform設備分配內存空間
if (pa) {
strcpy(pa->name, name);
pa->pdev.name = pa->name;//初始化platform_device設備的名稱
pa->pdev.id = id;//初始化platform_device設備的id
device_initialize(&pa->pdev.dev);//初始化platform_device內嵌的device
pa->pdev.dev.release = platform_device_release;
}
return pa ? &pa->pdev : NULL;
}
//一個更好的方法是,通過下面的函數platform_device_register_simple()動態創建一個設備,并把這個設備注冊到系統中:
struct platform_device *platform_device_register_simple(const char *name,int id,struct resource *res,unsigned int num)
{
struct platform_device *pdev;
int retval;
pdev = platform_device_alloc(name, id);
if (!pdev) {
retval = -ENOMEM;
goto error;
}
if (num) {
retval = platform_device_add_resources(pdev, res, num);
if (retval)
goto error;
}
retval = platform_device_add(pdev);
if (retval)
goto error;
return pdev;
error:
platform_device_put(pdev);
return ERR_PTR(retval);
}
//該函數就是調用了platform_device_alloc()和platform_device_add()函數來創建的注冊platform device,函數也根據res參數分配資源,看看platform_device_add_resources()函數:
int platform_device_add_resources(struct platform_device *pdev,struct resource *res, unsigned int num)
{
struct resource *r;
r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);//為資源分配內存空間
if (r) {
memcpy(r, res, sizeof(struct resource) * num);
pdev->resource = r; //并拷貝參數res中的內容,鏈接到device并設置其num_resources
pdev-> num_resources = num;
}
return r ? 0 : -ENOMEM;
}
四:設備資源
可以通過相關函數
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
//中斷資源也可以通過:
int platform_get_irq(struct platform_device *dev, unsigned int num)
資源的使用主要是驅動實現過程中需要使用到的,但是后期的使用一般需要在驅動的probe函數中實現申請中斷或者IO內存才能使用,而不能直接使用。特別是資源中的地址通常是物理地址,需要通過申請IO內存和映射完成物理到虛擬地址的轉換,便于進程的訪問。
五.Platform設備驅動的注冊
我們在設備驅動模型的分析中已經知道驅動在注冊要調用driver_register(),
platform driver的注冊函數platform_driver_register()同樣也是進行其它的一些初始化后調用driver_register()將驅動注冊到platform_bus_type總線上.
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;//它將要注冊到的總線
/*設置成platform_bus_type這個很重要,因為driver和device是通過bus聯系在一起的,
具體在本例中是通過 platform_bus_type中注冊的回調例程和屬性來是實現的,
driver與device的匹配就是通過 platform_bus_type注冊的回調例程platform_match ()來完成的。
*/
if (drv->probe)
drv-> driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);//注冊驅動
}
//然后設定了platform_driver內嵌的driver的probe、remove、shutdown函數。
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev);//調用platform_driver的probe()函數,這個函數一般由用戶自己實現
//例如下邊結構,回調的是serial8250_probe()函數
/*
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe,
.remove = __devexit_p(serial8250_remove),
.suspend = serial8250_suspend,
.resume = serial8250_resume,
.driver = {
.name = "serial8250",
.owner = THIS_MODULE,
},
};
*/
}
static int platform_drv_remove(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->remove(dev);
}
static void platform_drv_shutdown(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
drv->shutdown(dev);
}
六:簡單模板
實現的總線平臺驅動模型的最簡單源碼:
平臺設備的實現:device.c
#include
#include
#include
#include
#include
#include
/*平臺模型驅動的平臺設備對象*/
static struct platform_device *my_device;
/*初始化函數*/
static int __init my_device_init(void)
{
int ret = 0;
/*采用platform_device_alloc分配一個platform_device對象
參數分別為platform_device的name,和id。
*/
my_device = platform_device_alloc("my_dev",-1);
/*注冊設備,注意不是platform_device_register,將平臺設備注冊到內核中*/
ret = platform_device_add(my_device);
/*如果出錯釋放相關的內存單元*/
if(ret)
{
platform_device_put(my_device);
}
return ret;
}
/*卸載處理函數*/
static void __exit my_device_exit(void)
{
platform_device_unregister(my_device);
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-
平臺驅動的實現:driver.c
#include
#include
#include
#include
#include
/*平臺驅動中的probe和remove函數是必須實現的函數*/
/*設備驅動的探測函數,主要實現檢測總線上是否有該驅動對應的設備*/
static my_probe(struct device *dev)
{
/*
如果添加實際的設備到該平臺總線設備驅動模型中,則可以在該函數
中實現具體的設備驅動函數的初始化操作,包括設備號的申請,設備
的初始化,添加。自動設備文件創建函數的添加等操作。
或者是混雜字符設備的相關初始化操作。當然結構體的相關處理仍
然采取全局變量的形式。
*/
printk("Driver found devices which this driver can be handle\n");
return 0;
}
/*設備驅動的移除函數,主要檢測該驅動支持設備的移除活動檢測*/
static my_remove(struct device *dev)
{
/*
如果添加實際的設備到該平臺總線設備驅動模型中,則可以在該函數
中實現具體的設備的釋放,包括設備的刪除,設備號的注銷等操作。
*/
printk("Driver found device unpluded\n");
return 0;
}
static struct platform_driver my_driver =
{
/*平臺驅動的probe函數實現*/
.probe = my_probe,
/*平臺驅動的remove函數實現*/
.remove = my_remove,
/*實現設備驅動的name和owner變量*/
.driver =
{
/*該參數主要實現總線中的匹配函數調用*/
.name = "my_dev",
/*該函數表示模塊的擁有者*/
.owner = THIS_MODULE,
},
};
/*初始化函數*/
static int __init my_driver_init(void)
{
/*注冊平臺驅動*/
return platform_driver_register(&my_driver);
}
/*退出函數*/
static void __exit my_driver_exit(void)
{
/*注銷平臺驅動*/
return platform_driver_unregister(&my_driver);
}
/*加載和卸載*/
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-
//七:總結:
1.從這三個函數的代碼可以看到,又找到了相應的platform_driver和platform_device,然后調用platform_driver的probe、remove、shutdown函數。這是一種高明的做法:
在不針對某個驅動具體的probe、remove、shutdown指向的函數,而通過上三個過度函數來找到platform_driver,然后調用probe、remove、shutdown接口。
如果設備和驅動都注冊了,就可以通過bus ->match、bus->probe或driver->probe進行設備驅動匹配了。
2.驅動注冊的時候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),
對每個掛在虛擬的platform bus的設備作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match()->比較strncmp(pdev->name, drv->name, BUS_ID_SIZE),
如果相符就調用platform_drv_probe()->driver->probe(),如果probe成功則綁定該設備到該驅動。
上一篇:2416開發記錄九:實現設備節點的自動創建
下一篇:2416開發記錄十一:按鍵驅動(platform/中斷)
推薦閱讀
史海拾趣
隨著市場的不斷發展,Delphi Connection Systems公司意識到單靠自身的力量難以在激烈的市場競爭中立足。于是,公司積極尋求與其他企業的戰略合作,通過資源共享、優勢互補,共同開拓市場。公司與一家知名的汽車制造商建立了戰略合作關系,為其提供高質量的連接器產品,從而成功進入了汽車電子行業市場。
為了降低成本、提高生產效率,Cretex開始對產業鏈進行整合與優化。他們與上游供應商建立長期穩定的合作關系,確保原材料的穩定供應和質量可靠。同時,公司還加強了對下游客戶的服務與支持,通過提供個性化的解決方案和優質的售后服務,增強了客戶黏性。這些舉措使得Cretex在激烈的市場競爭中保持了領先地位。
埃特斯是一家在ETC技術創新和市場應用方面均表現出色的企業。公司擁有一支高素質的研發團隊,不斷推出具有創新性的ETC產品和解決方案。同時,埃特斯還注重將創新技術應用于實際場景中,為客戶提供高效、便捷的ETC服務。通過技術創新和市場應用的有機結合,埃特斯在ETC行業中樹立了良好的口碑和品牌形象。
為了提升產品質量和競爭力,意普(ESPE)公司開始注重品質管理,建立了嚴格的質量檢測體系。同時,公司積極申請國際認證,成功獲得了歐洲四級安全認證體系,進一步提升了產品的國際競爭力。這些努力不僅贏得了客戶的信任,也為公司打開了國際市場的大門。
Heyco公司成立于1906年(部分資料提及為1926年或1937年,這里以多個資料綜合為準),最初在德國柏林成立。起初,公司專注于制造手工工具,隨著汽車工業的興起,Heyco迅速轉型為汽車裝配線所需工具的生產商。在二戰后,Heyco抓住了汽車工業快速發展的機遇,進一步擴大了生產規模,不僅為德國本土汽車制造商如寶馬、奧迪、大眾和梅賽德斯奔馳提供定制工具,還逐漸拓展到國際市場。這一時期,Heyco憑借其高質量的產品和卓越的服務贏得了市場的廣泛認可。
隨著工業互聯網和智能制造技術的快速發展,Horizon緊跟時代步伐,積極推進數字化轉型和智能制造升級。公司引入先進的自動化生產線和智能管理系統,實現生產過程的智能化和精細化管理。通過數字化轉型,Horizon不僅提高了生產效率和產品質量,還降低了運營成本和市場風險。同時,公司還積極探索智能制造的新模式和新應用,為電子行業的未來發展貢獻智慧和力量。
4、彩色攝像機 VC-913D 技術規格: 圖像傳感器:1/3”SONY Super HAD CCD 有效像素:PAL:500(水平)*582(垂直)NTSC:510(水平)*492(垂直) 感光面積:4.9mm*3.7mm 信號系統:PAL/NTSC制式 水平清晰:420電視線 鏡頭安裝方式:C/CS ...… 查看全部問答∨ |
pxa270 系統flash只有nor 沒有nand怎么保存hive注冊表 我的系統的存儲設備只有nor cf卡和sd卡 想實現hive 可以在nor下實現嗎 我看到網上好多都是nand保持的 謝謝了… 查看全部問答∨ |
菜鳥問題 我想請問一下,嵌入式開發從頭到尾的實際操作過程是什么樣的?比如都需要下載什么軟件,怎樣修改并裝載引導程序,怎樣移植操作系統,怎樣調試,怎樣燒寫..... 謝謝啦!!! … 查看全部問答∨ |
|
每個STM32芯片都有一個內部的參照電壓,相當于一個標準電壓測量點,在芯片內部連接到ADC1的通道17。 根據數據手冊中的數據,這個參照電壓的典型值是1.20V,最小值是1.16V,最大值是1.24V。這個電壓基本不隨外部供電電壓的變化而變化。 不少 ...… 查看全部問答∨ |
|
我在上電后,從flash裝載程序,有時能正常運行,有時不運行,是什么原因啊?寫到flash里的應該是對的,不然肯定是一直都不會運行的,是不是flash和dsp之間的線有虛焊什么的?還有其他的原因嗎?… 查看全部問答∨ |
|
設計資源 培訓 開發板 精華推薦
- 泰科電子工業事業部邀您云逛展 ——打破時間、空間的限制,TE Connectivity 線上工博會為您帶來沉浸式VR觀展體驗
- Microchip有獎直播:為什么選擇FPGA,而非MCU?
- 有獎直播 | 同質化嚴重,缺乏創新,ST60毫米波非接觸連接器,賦予你獨特的產品設計,重拾市場話語權
- Molex緊湊型Type-C連接器 為您的設計節約寶貴空間!下載好禮送!
- “贊一贊我的國”:集合啦,侃侃好用的國產單片機
- Microchip電源評估板促銷,還有紅包送!
- 看MPLAB® Harmony集成軟件框架之學習篇 你來評論我送禮!
- 答題贏好禮| ADI 智能樓宇煙霧探測方案
- 智能家居當下趨勢與挑戰 泰科電子助力連接舒適未來
- 直播已結束|Littelfuse 智能樓宇電子設備安全與可靠性解決方案