单片机学习小组
直播中

李宛蔓

7年用户 975经验值
私信 关注

CH374芯片具有哪些特性参数应用?

CH374芯片具有哪些特性参数应用?

回帖(1)

h1654155275.5714

2022-2-15 10:23:05
概述

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());
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分