完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
概述
CH374 是一个 USB 总线的通用接口芯片,支持 USB-HOST 主机方式和 USB-DEVICE/SLAVE 设备方式,内置 3 端口 HUB 根集线器,支持低速和全速的控制传输、批量传输、中断传输以及同步/等时传输。 在本地端,CH374 具有 8 位数据总线和读、写、片选控制线以及中断输出,可以方便地挂接到单片机 /DSP/MCU/MPU 等控制器的系统总线上。除此之外,CH374 还提供了节约 I/O 引脚的 SPI 串行通讯方式,通过 3 线或者 4 线 SPI 串行接口以及中断输出与单片机/DSP/MCU/MPU 等相连接。 ● 支持 1.5Mbps 低速和 12Mbps 全速 USB 通讯,兼容 USB V2.0,外围元器件只需要电容。 ● 支持 USB-HOST 主机接口和 USB-DEVICE 设备接口,支持动态切换主机方式与设备方式。 ● CH374F/U 芯片内置 3 端口 USB 根集线器 ROOT-HUB,可以同时连接和管理 3 个 USB 设备。 ● 支持常用的低速和全速 USB 设备的控制传输、批量传输、中断传输、同步/等时传输。 ● 自动检测低速和全速 USB 设备的连接和断开,提供设备连接和断开的中断通知。 ● 内置 USB 信号线的阻抗匹配串联电阻、USB 设备端的上拉电阻、USB 主机端的下拉电阻。 ● 可选两种单片机接口:6MB 速度的 8 位被动并行接口和 3.5MB/28MHz 速度的 SPI 串行接口。 ● 并行接口包含 8 位数据总线,1 位地址,3 线控制:片选输入、写选通以及可选的读选通。 ● 并行接口只占用两个地址位:索引地址口和数据口,读写数据口后内部索引地址自动递增。 ● SPI 串行接口包含 SPI 片选、串行时钟、串行输入和输出,并且 SPI 输出与输入可以并联。 ● 中断输出引脚是可选连接,低电平有效,可以通过查询寄存器中的中断标志位代替。 ● 提供辅助功能:可编程时钟输出,上电复位输出以及可选的看门狗复位。 ● 提供支持 FAT12/FAT16/FAT32 文件系统的 U 盘文件级子程序库,实现单片机读写 U 盘文件。 ● 支持 5V 电源电压和 3.3V 电源电压甚至 3V 电源电压。 ● 提供 QFN-28、SSOP-24、SOP-16、SOP-28 和 SSOP-20 无铅封装,兼容 RoHS,可以提供 DIP28转换板,引脚基本兼容 CH375 和 CH372 芯片。 硬件设计 本手册中所指的单片机基本适用于 DSP 或者 SCM/MCU/MPU/CPU 等。CH374 的内部寄存器以及缓冲区分配在地址从 00H 到 0FFH 的范围内,由单片机寻址后访问。复位后的默认值都是以二进制数表示,并可以由若干个字符标志说明其特性. 硬件参数 电气参数 基本时序 SPI 串口时序 原理图 并口方式 晶体 X1、电容 C1 和 C2 用于 CH374 的时钟振荡电路。USB-HOST 主机方式要求时钟频率比较准确,X1 的频率是 24MHz±0.4‰,参考手册中的设置,X1 的频率也可以选用 12MHz。C1 和 C2 是容量约为22pF 的独石或高频瓷片电容。电容 C5 是可选的,仅用于延长电源上电时 CH374 芯片的复位时间,一般的应用电路中可以省去 C5,或者也可以由单片机的普通 I/O 引脚控制 CH374 复位。 CH374 还为单片机系统提供了以下辅助信号:RST 和 RST#引脚可以用于为单片机提供上电复位和 看门狗复位信号;CKO 引脚可以用于为单片机提供频率可动态编程的时钟信号;SLP 引脚可以用于为 单片机或者其它外设提供睡眠断电后的自动唤醒控制。 如果不连接中断请求输出引脚 INT#,那么单片机程序也可以通过查询中断标志寄存器代替。 SPI 串口方式 如果 CH374 芯片的 RD#引脚和 WR#引脚为低电平(接地)并且 CS#引脚为高电平(接正电源),那么 CH374 将工作于 SPI 串口方式。在 SPI 串口方式下,CH374 只需要与单片机/DSP/MCU 连接 5 个信号线:SCS#引脚、SCK 引脚、SDI 引脚和 SDO 引脚以及 INT#引脚,其它引脚都可以悬空。 为了节约引脚,INT#引脚可以不连接,而代之以查询中断标志寄存器,但是查询效率较低。 为了节约引脚,CH374 的 SDO 输出引脚可以在串接 330Ω的电阻 R4 后并联到 SDI 引脚上,再与单片机的 SDI 和 SDO 连接,当然,单片机的 SDO 引脚必须也是三态输出或者是可以关闭输出的。 SPI 串口方式除了连接线比并口方式较少之外,其它外围电路与并口方式基本相同。在软件编程方面,除了硬件抽象层的接口子程序不同之外,所有功能性的程序基本相同。 内置 HUB 连接 3 个设备 CH374F 和 CH374U 芯片内置了三端口根集线器 Root-HUB,作为 USB-Host 主机使用时,可以同时连接 3 个 USB 设备,支持 USB 全速和低速设备混合应用。其中端口 P5 和 P6 只能用于 Host 方式连接外部 USB 设备,端口 P4 既可以用于 Host 方式连接外部 USB 设备,也能用于 Device 方式连接外部 Host 主机。 软件驱动 dts配置 /* kernelmsm-4.9archarm64bootdtsqcommsm8953-mtp.dtsi */ &spi_6{ status = "ok"; spi_ch37x_hcd@0 { status = "ok"; compatible = "qcom,spi_ch37x_hcd"; //interrupt-parent = <&tlmm>; //interrupts = //pinctrl-names = "default", "sleep", "inactive"; //pinctrl-0 = <&eint7_function_C_active_pins>; //pinctrl-1 = <&eint7_function_C_sleep_pins>; //pinctrl-2 = <&eint7_function_C_inactive_pins>; #size-cells = <1>; reg = <0>; interrupt-parent = <&tlmm>; interrupts = <98 0x2>; u***-hcd-int = <&tlmm 98 0x2008>;//中断脚 spi-max-frequency = <960000>;//spi频率可调 //spi-cpha;//设置spi模式 //spi-cpol; poll_mode = <0>;//polling模式 type = <0>; enable_dma = <0>;//dma通信 }; }; /* kernelmsm-4.9archarm64bootdtsqcommsm8953.dtsi */ spi_6: spi@7af6000 { /* BLSP2 QUP2 */ compatible = "qcom,spi-qup-v2"; #address-cells = <1>; #size-cells = <0>; reg-names = "spi_physical", "spi_bam_physical"; reg = <0x7af6000 0x600>, <0x7ac4000 0x1f000>; interrupt-names = "spi_irq", "spi_bam_irq"; interrupts = <0 300 0>, <0 239 0>; spi-max-frequency = <960000>;//spi频率 pinctrl-names = "spi_default", "spi_sleep"; pinctrl-0 = <&spi6_default &spi6_cs0_active>; pinctrl-1 = <&spi6_sleep &spi6_cs0_sleep>; clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, <&clock_gcc clk_gcc_blsp2_qup2_spi_apps_clk>; clock-names = "iface_clk", "core_clk"; qcom,infinite-mode = <0>; qcom,use-bam; qcom,use-pinctrl; qcom,ver-reg-exists; qcom,bam-consumer-pipe-index = <6>; qcom,bam-producer-pipe-index = <7>; qcom,master-id = <84>; status = "disabled"; }; 驱动配置 ch37x_probe 在probe函数中初始化spi设备,通过u***_create_hcd()创建hcd节点 static int ch37x_probe(struct spi_device *spi) { struct ch37x_hcd_data *ch37x_hcd; struct u***_hcd *hcd = NULL; int irq ; //int error; static struct xgold_spi_chip *spi_chip_data; struct device_node *np = of_node_get(spi->dev.of_node); // struct device_state_pm_state *pm_state; int retval = -ENOMEM; dev_err(&spi->dev, "ch37x_proben"); #ifndef GPIO_SPI if(!spi) return -ENOMEM; dev_err(&spi->dev, "ch37x_proben"); if (spi_setup(spi) < 0) { dev_err(&spi->dev, "Unable to setup SPI bus"); return -EFAULT; } #endif if (u***_disabled()) return -ENODEV; hcd = u***_create_hcd(&ch37x_hcd_desc, &spi->dev, dev_name(&spi->dev)); if (!hcd) { dev_err(&spi->dev, "failed to create HCD structuren"); goto error; } //set_bit(HCD_FLAG_POLL_RH, &hcd->flags); ch37x_hcd = hcd_to_ch37x(hcd); ch37x_hcd->next = ch37x_hcd_list; ch37x_hcd_list = ch37x_hcd; INIT_LIST_HEAD(&ch37x_hcd->ep_list); spin_lock_init(&ch37x_hcd->lock); spin_lock_init(&spilock); 之后通过spi_ch37x_hcd_parse_dt()获取dts节点信息,设置初始化信息及中断gpio。 static struct xgold_spi_chip *spi_ch37x_hcd_parse_dt(struct u***_hcd *hcd, struct device *dev) { u32 temp; struct xgold_spi_chip *spi_chip_data; struct ch37x_hcd_data *ch37x_hcd = hcd_to_ch37x(hcd); spi_chip_data = devm_kzalloc(dev, sizeof(*spi_chip_data), GFP_KERNEL); if (!spi_chip_data) { dev_err(dev, "memory allocation for spi_chip_data failedn"); return ERR_PTR(-ENOMEM); } if (of_property_read_u32(dev->of_node, "poll_mode", &temp)) { dev_warn(dev, "fail to get poll_mode, default set 0n"); spi_chip_data->poll_mode = 0; } else { spi_chip_data->poll_mode = temp; } if (of_property_read_u32(dev->of_node, "type", &temp)) { dev_warn(dev, "fail to get type, default set 0n"); spi_chip_data->type = 0; } else { spi_chip_data->type = temp; } if (of_property_read_u32(dev->of_node, "enable_dma", &temp)) { dev_warn(dev, "fail to get enable_dma, default set 0n"); spi_chip_data->enable_dma = 0; } else { spi_chip_data->enable_dma = temp; } ch37x_hcd_dbg(ch37x_hcd, "%s: poll_mode=%d, type=%d, enable_dma=%dn",__func__, spi_chip_data->poll_mode, spi_chip_data->type, spi_chip_data->enable_dma); g_ch37x_ctrl.u***_hcd_int_gpio= of_get_named_gpio(dev->of_node, "u***-hcd-int", 0); g_ch37x_ctrl.ch37x_ldo_en_gpio= of_get_named_gpio(dev->of_node, "ch37x-ldo-en", 0); g_ch37x_ctrl.ch37x_vbus_en_gpio= of_get_named_gpio(dev->of_node, "u***-vbus-en", 0); pr_err( "%s: u***_hcd_int_gpio=%d ch37x_ldo_en_gpio=%d ch37x_vbus_en_gpio=%d n",__func__, g_ch37x_ctrl.u***_hcd_int_gpio,g_ch37x_ctrl.ch37x_ldo_en_gpio, g_ch37x_ctrl.ch37x_vbus_en_gpio); return spi_chip_data; } 然后创建工作队列ch37x_spi_thread,通过ch37x_irq_handler()中断触发spi通信。 irq = gpio_request(g_ch37x_ctrl.u***_hcd_int_gpio, "u*** hcd int"); if(!irq) { gpio_direction_input(g_ch37x_ctrl.u***_hcd_int_gpio); irq = gpio_to_irq(g_ch37x_ctrl.u***_hcd_int_gpio); g_ch37x_ctrl.u***_hcd_irq= irq; } else { dev_err(&spi->dev, "failed to request u*** hcd int n"); } g_ch37x_ctrl.hcd=hcd; spi->irq = irq; ch37x_hcd->spi = spi; ch37x_hcd->dev = &spi->dev; ch37x_hcd->irq = spi->irq; ch37x_hcd->tx = devm_kzalloc(&spi->dev, sizeof(*ch37x_hcd->tx), GFP_KERNEL); if (!ch37x_hcd->tx) { dev_err(&spi->dev, "failed to kmalloc tx buffern"); goto error; } ch37x_hcd->rx = devm_kzalloc(&spi->dev, sizeof(*ch37x_hcd->rx), GFP_KERNEL); if (!ch37x_hcd->rx) { dev_err(&spi->dev, "failed to kmalloc rx buffern"); goto error; } kthread_init_worker(&ch37x_hcd->spi_worker); ch37x_hcd->spi_thread = kthread_run(kthread_worker_fn, &ch37x_hcd->spi_worker, "ch37x_spi_thread"); if (IS_ERR(ch37x_hcd->spi_thread)) { retval = PTR_ERR(ch37x_hcd->spi_thread); ch37x_hcd->spi_thread = NULL; pr_info("failed to run spi_threadn"); goto error; } kthread_init_work(&ch37x_hcd->spi_work, ch37x_spi_thread); retval = u***_add_hcd(hcd, 0, 0); if (retval) { dev_err(&spi->dev, "failed to add HCDn"); goto error; } spi_set_drvdata(spi, ch37x_hcd); g_ch37x_hcd = ch37x_hcd; atomic_set(&ch37x_hcd->suspend_flag, 0); INIT_DELAYED_WORK(&ch37x_hcd->delaywork, ch37x_resume_delaywork_func); atomic_set(&ch37x_hcd->debug_flag, 0); atomic_set(&ch37x_hcd->urb_process, 0); mutex_init(&ch37x_hcd->thread_mutex); ch37x_debugfs_init(hcd); retval = devm_request_threaded_irq(&spi->dev, spi->irq, ch37x_irq_handler, ch37x_irq_thread_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT , "spi-ch37x-hcd", hcd); if (retval < 0) { dev_err(&spi->dev, "failed to request irq %d error %dn", spi->irq, retval); goto error; } ch37x_spi_thread static void ch37x_spi_thread(struct kthread_work *work) { struct ch37x_hcd_data *ch37x_hcd = container_of(work, struct ch37x_hcd_data, spi_work); struct u***_hcd *hcd = ch37x_to_hcd(ch37x_hcd); int i_worked = 1; //int ret = 0; int urb_null = 0; int index = 0; pr_info("%s:line=%d startn",__func__,__LINE__); while (!kthread_should_stop()) { if (ch37x_hcd->rh_state == CH374_RH_RUNNING) { break; } msleep(1000); } while (!kthread_should_stop() && !atomic_read(&ch37x_hcd->suspend_flag)) { if (!i_worked) { set_current_state(TASK_INTERRUPTIBLE); if (test_and_clear_bit(ENABLE_IRQ, &ch37x_hcd->todo)) { enable_irq(ch37x_hcd->irq); ch37x_hcd_dbg(ch37x_hcd, "%s:enable irq %dn",__func__, ch37x_hcd->irq); } // check if urb is really null (enqueue comes) if ((urb_null == 1) && (atomic_read(&ch37x_hcd->urb_process) == 1)) { if (!test_and_set_bit(ENABLE_IRQ, &ch37x_hcd->todo)) { disable_irq_nosync(ch37x_hcd->irq); __set_current_state(TASK_RUNNING); } urb_null = 0; i_worked = 1; ch37x_hcd_dbg(ch37x_hcd, "%s:new urb_enqueue, dont schedulen",__func__); continue; } schedule(); __set_current_state(TASK_RUNNING); ch37x_hcd_dbg(ch37x_hcd, "%s: wakeup! line=%dn",__func__, __LINE__); } i_worked = 0; if (ch37x_hcd->urb_done) i_worked |= ch37x_urb_done(hcd); else if (ch37x_handle_irqs(hcd)) i_worked = 1; else if (!ch37x_hcd->curr_urb) { i_worked |= ch37x_select_and_start_urb(hcd); if (i_worked == 0) { urb_null = 1; } else { urb_null = 0; } } else if (ch37x_hcd->curr_urb) { ch37x_hcd_dbg(ch37x_hcd, "%s:line=%d, curr_urb not nulln", __func__, __LINE__); } if (test_and_clear_bit(RESET_HCD, &ch37x_hcd->todo)) /* reset the HCD: */ i_worked |= ch37x_reset_hcd(hcd); for (index = 0; index < NUM_PORTS; index++) { if (test_and_clear_bit(RESET_PORT, &ch37x_hcd->ports[index].todoport)) { /* perform a USB bus reset: */ host_reset_bus(hcd, index); i_worked = 1; ch37x_hcd_dbg(ch37x_hcd, "%s:line=%d, wPortStatus[%d]=0x%x RESET_PORTn", __func__, __LINE__, index, ch37x_hcd->ports[index].port_status.wPortStatus); } } if (test_and_clear_bit(CHECK_CONNECT, &ch37x_hcd->todo)) { pr_info("%s:line=%dn", __func__, __LINE__); for (index = 0; index < NUM_PORTS; index++) ch37x_detect_conn(hcd, index); i_worked = 1; } // 处理已经dequeue的URB if (test_and_clear_bit(CHECK_UNLINK, &ch37x_hcd->todo)) { pr_info("%s:line=%dn", __func__, __LINE__); i_worked |= ch37x_check_unlink(hcd); } ch37x_hcd_dbg(ch37x_hcd, "%s:line=%d, i_worked=%d continuen", __func__, __LINE__, i_worked); } set_current_state(TASK_RUNNING); return; // pr_info("%s:i_worked=0x%x suspend=%d thread=%d exitn", __func__, i_worked, // atomic_read(&ch37x_hcd->suspend_flag), kthread_should_stop()); } |
|
|
|
只有小组成员才能发言,加入小组>>
3311 浏览 9 评论
2994 浏览 16 评论
3493 浏览 1 评论
9058 浏览 16 评论
4087 浏览 18 评论
1176浏览 3评论
604浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
598浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2334浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1895浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 20:51 , Processed in 1.483759 second(s), Total 77, Slave 58 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号