CH390使用注意事项

发布时间 2023-12-18 11:13:32作者: 小小小学僧

关于CH390使用注意事项

CH390替换DM90xx硬件注意事项

1、CH390L替换DM9000

  • AVDD33的对地电容建议1uF贴近芯片放置,42脚为主电源AVDD33需10uF并联0.1uF。各AVDD33需分别接电容,供电电压都为3.3V;
  • AVDDK和DVDDK需要分别外接电容并贴近芯片放置。两个AVDDK引脚建议任选一个外接1uF,另一个外接0.1uF。DVDDK外接0.1uF对地电容,DM9000 1脚接外置电阻的位置应改成0.1uF电容;
  • VDDIO为CH390L的I/O电源,VDDIO支持3.3V或者2.5V,外接0.1uF对地电容;
  • 43脚和44脚XI、XO不要接电阻,由于芯片内置12pf负载电容,因此如果晶振的负载电容是12pf则不需要再接负载电容,如果晶体负载电容为20pF时建议贴15pF的负载电容;晶振必须是25MHz优选贴片晶振;如果是外部时钟输入,需要从43脚XI输入;
  • 芯片已内置终端电阻,因此不要接49.9欧或50欧等任何电阻;
  • 网络变压器的中心抽头分别通过0.1uF~1uF电容接地,不要接任何电源;

2、CH390H替换DM9051

  • AVDD33的对地电容建议1uF贴近芯片放置,42脚为主电源AVDD33需10uF并联0.1uF。各AVDD33需分别接电容,供电电压都为3.3V;
  • AVDDK和DVDDK需要分别外接电容并贴近芯片放置。两个AVDDK引脚建议任选一个外接1uF,另一个外接0.1uF。DVDDK外接0.1uF对地电容,DM9051 1脚接外置电阻的位置应改成0.1uF电容;
  • VDDIO为CH390H的I/O电源,VDDIO支持3.3V或者2.5V,外接0.1uF对地电容;
  • 30脚和31脚XI、XO不要接电阻,由于芯片内置12pf负载电容,因此如果晶振的负载电容是12pf则不需要再接负载电容,如果晶体负载电容为20pF时建议贴15pF的负载电容;晶振必须是25MHz优选贴片晶振;如果是外部时钟输入,那么从30脚XI输入;
  • 芯片已内置终端电阻,因此不要接49.9欧或50欧等任何电阻;
  • 网络变压器的中心抽头分别通过0.1uF~1uF电容接地,不要接任何电源;

PS:如果采用杜邦线或飞线连接评估板调试,因CH390支持最高50M的SPICLK为防止串扰需要注意MCU与CH390之间可靠接地其他联络线尽量短且可靠连接,若出现SPI读写多数据或少数据的情况可以考虑在MCU与CH390的CS与CLK两端分别接10到20pF电容到地。

CH390DS1手册注意事项

CH390DS1手册中寄存器描述(H)对应CH390H,(L)对应CH390L;未标明(H)或(L)则适用于CH390H与CH390L。

引脚:I=输入;O=输出;I/O=输入/输出;P=电源;PD=内置下拉电阻;PU=内置上拉电阻。

寄存器描述中,默认形式如下:

复位值:

1位设置为逻辑1;

0位设置为逻辑0;

X没有默认值;

P=上电复位默认值;

H=硬件复位默认值;

S=软件复位默认值;

E=来自EEPROM的默认值;

T=配置引脚的默认值;

h=十六进制。

访问类型:

RO=只读;

WO=只写;

RW=读/写;

R/C=读和清除。

RW/C1=读写、通过写1清除;

保留位在读写访问时未定义

以中断屏蔽寄存器(IMR)为例,如果用的是CH390H需要访问的地址是7Fh,如果是CH390L则需要访问FFh,该寄存器的上电复位与软件复位默认值是00h,除第6位保留位为只读外其他位可读可写。

附CH390DS1手册:https://www.wch.cn/downloads/CH390DS1_PDF.html

软件注意事项

可以先尝试读CH390的VID/PID验证SPI或并口通信是否正常,如果读VID/PID异常需确认接口通信是否满足CH390的时序要求。

1、CH390H支持从模式SPI模式0与SPI模式3。

SPI模式0

SPI模式3

需要注意的是一次SPI操作于SCS下降沿启动,于SCS上升沿停止,因此每次读写寄存器都需要拉低SCS直到读写完毕。

2、CH390L支持16位或者8位的被动并行总线接口,通过片选CS来选择访问CH390L。CS引脚默认为低电平有效,可以通过EEPROM配置重新定义。通过主机接口可以访问两个端口,一个是INDEX地址索引端口,另一个是DATA数据端口。引脚CMD=0时,选择INDEX端口,CMD=1时,选择DATA端口。INDEX端口的内容是DATA端口的寄存器地址。在访问任何寄存器之前,该寄存器的地址必须保存在INDEX端口中。

CH390数据包收发

  1. 数据包发送

首先通过写指令寄存器MWCMD将数据写入发送缓冲区,然后将字节数写入TXPLL和TXPLH寄存器,最后将TCR寄存器位0置1开启数据包发送。上电默认从索引A开始发送数据,在数据包A发送结束之前,可以将下一个(索引B)数据包的数据写入发送缓冲区。在数据包A的传输完成后,将数据包B的长度写入TXPLL和TXPLH寄存器,并置位TCR寄存器即可发送数据包B。后续数据包以A,B,A,B…交替的顺序以同样的方式发送。

 1 /**
 2  * @name ch390_write_mem
 3  * @brief Write data to TX SRAM
 4  * @param data - Data buffer
 5  * @param length - Length to write
 6  */
 7 void ch390_write_mem(uint8_t *data, int length)
 8 {
 9     int i;
10     ch390_if.scs(0);
11     ch390_if.spi_exc_data(OPC_MEM_WRITE);
12     for(i = 0; i < length; i++) {
13         ch390_if.spi_exc_data(data[i]);
14     }
15     ch390_if.scs(1);
16 }
1 /**
2  * @name ch390_send_request
3  * @brief Issue transmit request
4  */
5 void ch390_send_request()
6 {
7     uint8_t tcr = ch390_read_reg(CH390_TCR);
8     ch390_write_reg(CH390_TCR, tcr | TCR_TXREQ);
9 }
 1 /**
 2  * @name ch390_send_packet
 3  * @brief Send packet
 4  * @param buff - Data to be sent
 5  * @param length - Less than 3k bytes.
 6  */
 7 void ch390_send_packet(uint8_t *buff, uint16_t length)
 8 {
 9     // Write data to SRAM
10     ch390_write_mem(buff, length);
11     // Wait until last transmit complete
12     while(ch390_read_reg(CH390_TCR) & TCR_TXREQ);
13     // Set current packet length
14     ch390_write_reg(CH390_TXPLL, length & 0xff);
15     ch390_write_reg(CH390_TXPLH, (length >> 8) & 0xff);
16     // Issue transmit request
17     ch390_send_request();
18 }
  1. 数据包接受

接收缓冲区是一个环形缓冲区。系统复位后,接收缓冲区的起始地址为0C00h。每个数据包包含4字节的帧头,数据域,以及CRC。4字节帧头的格式为:01h,状态,数据长度低字节,数据长度高字节。

接收数据时需要先读两次无地址递增内存数据预取读命令寄存器(MRCMDX),第一次读为执行预取命令,第二次读才是取该寄存器里的实际值。通过该值判断接收到的是否为有效数据,如果为有效数据则需要通过地址递增的内存数据读命令寄存器(MRCMD)取数据即可。

 1 /**
 2  * @name ch390_receive_packet
 3  * @brief Receive packet
 4  * @param buff - Size equal to CH390_PKT_MAX
 5  * @param rx_status - Output abnormal status while receiving packet.
 6  *                    It has the same meaning as RSR(06h).
 7  * @return Packet length
 8  */
 9 uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
10 {
11     uint8_t rx_ready;
12     uint16_t rx_len = 0;
13     uint8_t ReceiveData[4];
14 
15     // Check packet ready or not
16     ch390_read_reg(CH390_MRCMDX);
17     rx_ready = ch390_read_reg(CH390_MRCMDX);
18 
19     // if rxbyte != 1 or 0 reset device
20     if (rx_ready & CH390_PKT_ERR)
21     {
22         // Reset RX FIFO pointer
23         ch390_write_reg(CH390_RCR, 0);        //RX disable
24         ch390_write_reg(CH390_MPTRCR, 0x01);  //Reset RX FIFO pointer
25         ch390_write_reg(CH390_MRRH, 0x0c);
26         ch390_if.delay_us(1000);
27         ch390_write_reg(CH390_RCR, RCR_RXEN); //RX Enable
28         return 0;
29     }
30     if (!(rx_ready & CH390_PKT_RDY))
31     {
32         return 0;
33     }
34 
35     ch390_read_mem(ReceiveData, 4);
36 
37     *rx_status = ReceiveData[1];
38     rx_len = ReceiveData[2] | (ReceiveData[3] << 8);
39 
40     if(rx_len <= CH390_PKT_MAX)
41     {
42         ch390_read_mem(buff, rx_len);
43     }
44 
45     if ((*rx_status & 0x3f) || (rx_len > CH390_PKT_MAX))
46     {
47         return 0;
48     }
49     return rx_len;
50 }
 1 /**
 2  * @name ch390_read_mem
 3  * @brief Read data from RX SRAM
 4  * @param data - Data buffer
 5  * @param length - Length to read
 6  */
 7 void ch390_read_mem(uint8_t *data, int length)
 8 {
 9     int i;
10     ch390_if.scs(0);
11     ch390_if.spi_exc_data(OPC_MEM_READ);
12 
13     for(i = 0; i < length; i++) {
14         data[i] = ch390_spi_dummy_read();
15     }
16     ch390_if.scs(1);
17 }

CH390中断说明

中断状态寄存器(ISR)写1清零,其中第五位LINKCHG位比较特殊需要如下处理否则可能会出现不报中断状态的情况。

 1 /**
 2  * @name ch390_int_handler
 3  * @brief Handle CH390 interrupt events, include packet receive
 4  */
 5 void ch390_int_handler()
 6 {
 7     uint8_t int_status = ch390_get_int_status();
 8     // Link status changed
 9     if(int_status & ISR_LNKCHG)
10     {
11         Delay_Ms(100);
12         ch390_write_phy(0x1f, 0);
13         ch390_read_phy(0x1e);
14         phy_linked = ch390_get_link_status();
15         printf("Link status: %d\r\n", phy_linked);
16         ch390_write_reg(CH390_ISR, ISR_LNKCHG);
17     }
18     // Receive overflow
19     if(int_status & ISR_ROS) printf("Receive overflow\r\n");
20     // Receive overflow counter overflow
21     if(int_status & ISR_ROO) printf("Overflow counter overflow\r\n");
22     // Packet transmitted
23     if(int_status & ISR_PT) printf("Packet sent\r\n");
24     // Packet received
25     if(int_status & ISR_PR)
26     {
27         /* Multiple packets may be received in a single packet receive
28          * event. So "ch390_receive_packet" should be called until the
29          * return value is 0. Otherwise data will accumulate in RX SRAM
30          * and cause overflow. */
31         uint8_t rx_status = 0;
32         while(rx_fifo.write_p->valid == 0)
33         {
34             rx_fifo.write_p->length = ch390_receive_packet(
35                             rx_fifo.write_p->data,
36                             &rx_status);
37             if(rx_fifo.write_p->length != 0)
38             {
39                 rx_fifo.write_p->valid = 1;
40                 rx_fifo.write_p = rx_fifo.write_p->next;
41             }
42             else break;
43         }
44     }
45 }

附CH390EVT例程:https://www.wch.cn/downloads/CH390EVT_ZIP.html