• 使用 Microchip SAM9X60 OTP 存储板卡的MAC地址和序列号


    1. 介绍

         SAM9X60 处理器有部分OTP(One Time Programming) Aera 可用于存储user data,这样的话我们就可以将板卡 MAC Address和 SN 序列号写到固定的OTP User Area中。

         为什么要使用 OTP 区域存储MAC地址和序列号呢?答案是为了省钱。如果不使用OTP的话,可能要考虑采用 eeprom 等掉电非易失的存储保存这些固定信息,这样的话就多了一颗料的钱,还得占用I2C总线,PCB布线等。

         这里提一句,NXP i.MX6系列的处理器,对 OTP 区域的划分比Microchip的处理器要详细和更容易访问一些,没有那么多弯弯绕绕,感兴趣的小伙伴可以看一下i.MX6 Reference Manual.

    2. MAC地址和SN 格式

    假如格式如下:
    在这里插入图片描述

    3. SAM9X60 OTP Address

    SAM9X60 处理器OTP相关的介绍。

    Memory Mapping
    在这里插入图片描述
    查看SAM9X60 芯片手册 ‘23. OTP Memory Controller(OTPC)’章节的相关内容。

    在这里插入图片描述
    在这里插入图片描述
    每一个Packet包含1个32-bit的Header和若干个32-bit的Payload(data)。

    Payload 的 SIZE 自己可以在Header的寄存器中定义。

    在这里插入图片描述
    我们这里要存储1个MAC地址,1个SN序列号的话,为了方便操作和录入,这里就使用4个32bit的payload。Header地址从0x00开始,然后地址0x01 ~ 0x04分别代表payload0~payload4。
    在这里插入图片描述

    3.1. 写操作流程

    在这里插入图片描述

    OTPC_MR (0x4)写 0xFF0000   - devmem 0xEFF00004 32 0xFF0000
    
    OTPC_CR (0x0)写 0x40
    
    读 OTPC_ISR (0x1C)查看这个位EOR (bit8)是否是1
    
    读 OTPC_HR (0x20)查看寄存器中的值是否有1,
    
    读 OTPC_DR (0x24)查看寄存器中的值是否有1  (或者读 OTPC_SR (0x0C)寄存器,查看 ONEF bit9 这一位是否为1)
    
    OTPC_MR (0x4)写 0x00000010 ,将OTPC_MR.ADDR 设置为 0,表示从0x0地址开始生成新的packet.这个操作可能触发自动 flush
    
    OTPC_HR (0x20)写 0x301 ,表示我们要使用4个payload, packet类型是 REGULAR 普通的用户数据。
    
    OTPC_AR (0x08)写 0x10000, 表示地址从0x0开始,并且再每次写入数据后,该地址会自动递增,就不用我们再手动增加写入的数据地址了。
    
    OTPC_DR (0x24)写入第一个数据 0x41300001
    
    OTPC_DR (0x24)写入第二个数据 0x00000220
    
    OTPC_DR (0x24)写入第三个数据 0x55508000
    
    OTPC_DR (0x24)写入第四个数据 0x0000001F
    
    OTPC_DR (0x24)写入第五个数据 0x55508001
    
    OTPC_DR (0x24)写入第六个数据 0x0000001F
    
    数据写完之后,OTPC_CR (0x0)写 0x71670001 ,使能programming.
    
    查看写操作是否结束,读 OTPC_ISR (0x1C) EOP bit0是否是1,或者读 OTPC_SR (0x0C) PGM bit0 是否是0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    3.2. OTP Lock操作流程

    数据写完之后进行Lock,操作如下:
    在这里插入图片描述

    3.3. 读操作流程

    在这里插入图片描述

    OTPC_MR (0x4) 寄存器中先写入 header 的地址 ,写入0x0
    
    OTPC_CR.READ (0x0)必须设置为1,OTPC_CR这个寄存器是write-only的,仅仅写入 0x40即可,这样就可以读 user area 了。
    
    等待 OTPC_ISR(0x1C)中 EOR(bit8) 这个位变为1 或者等待 OTPC_SR(0x0C)READ (bit6)这个位变成0,这就意味着整个packet 已经转移到临时寄存器中了
    
    读 OTPC_HR (0x20)头寄存器,读取每一个paylaod word,paylaod word的地址必须已经写入到 OTPC_AR(0x08).DADDR 中。 OTPC_AR.DADDR的地址在读取 OTPC_DR 后会自动递增,所以在连续读取paylaod word数据时,只需要去读 OTPC_DR即可。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4. MAC Address/SN Programming and Read

    4.1. Programming

    编写的用于板卡 MAC&SN 录入的shell 脚本如下,仅供参考。

    #!/bin/sh
     
    #########################################################
    ### Programming MAC Address and SN into SAM9X60 OTP
    ###
    ### Author: Heat.Huang
    ### Date:   2022-5-7
    #########################################################
     
    function usage(){
        echo "Usage:"
        echo ""
        echo "Programming OTP:"
        echo "    $0 22041100001 00:60:2D:0C:84:27"
     
    }
     
    function prog_mac(){
        # get serial number
        SN=$1
        if [ ${#SN} -ne 11 ]; then
            echo "ERROR: The type of serial number is error."
            usage
            exit 1
        else
            SN_1=0x${SN:3:11}
            SN_2=0x00000${SN:0:3}
            echo "SN_1: $SN_1"
            echo "SN_2: $SN_2"
        fi
     
        # get mac address
        MAC_ADDR=$2
        if [ ${#MAC_ADDR} -ne 17 ]; then
            echo "ERROR:  The format of MAC adddress you program is error."
            usage
            exit 1
        else
            /sbin/ifconfig eth0 down
            /sbin/ifconfig eth0 hw ether "$MAC_ADDR"
            if [ $? -eq 0 ]; then
                echo "MAC address is valid, can be programmed OTP."
                /sbin/ifconfig eth0 up
            else
                echo "MAC address is not valid."
                /sbin/ifconfig eth0 up
                usage
                exit 1
            fi
            echo "Start programming OTP ......"
            MAC_ADDR_1=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[1]}'`
            MAC_ADDR_2=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[2]}'`
            MAC_ADDR_3=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[3]}'`
            MAC_ADDR_4=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[4]}'`
            MAC_ADDR_5=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[5]}'`
            MAC_ADDR_6=`echo $MAC_ADDR | awk '{split($0,addr,":");print addr[6]}'`
     
            HW_OCOTP_MAC0=0x${MAC_ADDR_3}${MAC_ADDR_4}${MAC_ADDR_5}${MAC_ADDR_6}
            HW_OCOTP_MAC1=0x0000${MAC_ADDR_1}${MAC_ADDR_2}
            echo "HW_OCOTP_MAC0: $HW_OCOTP_MAC0"
            echo "HW_OCOTP_MAC1: $HW_OCOTP_MAC1"
        fi 
     
        ##
        ## Steps for program OTPC
        ##
     
        ###
        ### Write Access
        ###
        # read RC register
        echo "Read PMC Clock Generator Main Oscillator Register"
        devmem 0xfffffc20
        # enable RC clock
        echo "Enable the main RC in CKGR_MOR register"
        devmem 0xfffffc20 32 0x01370829
        # read RC register again
        echo "Read PMC Clock Generator Main Oscillator Register again"
        devmem 0xfffffc20
     
        # write OTPC_MR.NPCKT to 0
        echo "write OTPC_MR.NPCKT to 0"
        devmem 0xeff00004 32 0x00000000
        # write OTPC_MR.ADDR to its maximum value
        echo "write OTPC_MR.ADDR to its maximum value"
        devmem 0xeff00004 32 0x00ff0000
        # write OTPC_CR.READ to 1 and wait for the read completion
        echo "write OTPC_CR.READ to 1 and wait for the read completion"
        devmem 0xeff00000 32 0x71670040
        sleep 1
     
        echo "check if End of Read (EOR bit8) is 1"
        devmem 0xeff0001c
        echo "check OTPC Header Register"
        devmem 0xeff00020
        echo "check OTPC Data Register if have data of one"
        devmem 0xeff00024
     
        # Write OTPC_MR.ADDR to 0 and set NPCKT
        devmem 0xeff00004 32 0x00000010
        # write the header value in OTPC_HR
        devmem 0xeff00020 32 0x00000301
        # set DADDR to 0
        devmem 0xeff00008 32 0x00010000
        # write serial number into payload0 and payload1
        devmem 0xeff00024 32 $SN_1
        devmem 0xeff00024 32 $SN_2
        # write eth0 mac address into payload2 and payload3
        devmem 0xeff00024 32 $HW_OCOTP_MAC0
        devmem 0xeff00024 32 $HW_OCOTP_MAC1
     
        # after write , enable OTPC programming
        devmem 0xeff00000 32 0x71670001
        sleep 1
        # check if programming is end
        devmem 0xeff0001c
        sleep 1
        # read the address of new generated packet
        val=`devmem 0xeff00004`
        echo "the address of new generated packet: $val"
        val=0x${val:2:4}0000
        # clear OTPC_MR.NPCKT
        devmem 0xeff00004 32 $val
     
        ###
        ### Read data from OTP to check if write ok
        ###
        devmem 0xeff00000 32 0x00000040
        devmem 0xeff0001c
        devmem 0xeff00020
        devmem 0xeff00008 32 0x00000000
        SN_1_OTP=`devmem 0xeff00024`
        echo "SN_1_OTP = $SN_1_OTP"
     
        SN_2_OTP=`devmem 0xeff00024`
        echo "SN_2_OTP = $SN_2_OTP"
     
        HW_OCOTP_MAC0_OTP=`devmem 0xeff00024`
        echo "HW_OCOTP_MAC0_OTP = $HW_OCOTP_MAC0_OTP"
        HW_OCOTP_MAC0_OTP=${HW_OCOTP_MAC0_OTP:2:9}
        # transfer HEX to DEC
        HW_OCOTP_MAC0_OTP=$((16#$HW_OCOTP_MAC0_OTP))
     
        HW_OCOTP_MAC1_OTP=`devmem 0xeff00024`
        echo "HW_OCOTP_MAC1_OTP = $HW_OCOTP_MAC1_OTP"
        HW_OCOTP_MAC1_OTP=${HW_OCOTP_MAC1_OTP:2:9}
        # transfer HEX to DEC
        HW_OCOTP_MAC1_OTP=$((16#$HW_OCOTP_MAC1_OTP))
     
        ## transfer writen value from HEX to DEC
        HW_OCOTP_MAC0=${HW_OCOTP_MAC0:2:9}
        # transfer HEX to DEC
        HW_OCOTP_MAC0=$((16#$HW_OCOTP_MAC0))
        HW_OCOTP_MAC1=${HW_OCOTP_MAC1:2:9}
        # transfer HEX to DEC
        HW_OCOTP_MAC1=$((16#$HW_OCOTP_MAC1))
     
        if [ "$SN_1_OTP" == "$SN_1" ] && [ "$SN_2_OTP" == "$SN_2" ] && [ $HW_OCOTP_MAC0_OTP -eq $HW_OCOTP_MAC0 ] && [ $HW_OCOTP_MAC1_OTP -eq $HW_OCOTP_MAC1 ]; then
            echo "Programming MAC ADDR and serial number succussful."
        else
            echo "Programming OTP FAIL."
        fi
     
        ###
        ### Lock the new generated packet
        ###
        echo "Lock the new generated packet"
        # write the address value of the header of the packet to lock in OTPC_MR.ADDR
        devmem 0xeff00004 32 $val
        # start a read by seeting OTPC_CR.READ and waiting for the read completion indicated by OTPC_ISR.EOR
        devmem 0xeff00000 32 0x00000040
        sleep 1
        # check if End of Read (EOR bit8) is 1
        devmem 0xeff0001c
        # Write 0x7167 in the OTPC_CR.KEY field and '1' in OTPC_CR.CKSGEN
        devmem 0xeff00000 32 0x71670002
        # the end of the lock operation is indicated by OTPC_ISR.EOL='1' and/or OTPC_SR.LOCK='0'
        lock_ret=`devmem 0xeff0001c`
        eol=${lock_ret:9}
        if [ $eol -eq 2 ]; then
            echo "Lock ok."
        else
            echo "Lock fail."
        fi
     
    }
     
     
    # Entry
    if [ $# == 2 ];then
        prog_mac $@
    else
        usage
        exit 1
    fi
     
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197

    4.2. Read

        又写了一个读取MAC地址和SN的shell脚本 get_Mac.sh,可以用于板子 Linux系统启动后,运行该脚本从OTP相应的Aera中读取MAC address,如果读取失败会随机生成一个MAC地址,然后通过ifconfig eth0 hw ether 命令配置网卡的IP。脚本还会从OTP读取序列号,并写入到文件 /images/data/SN 中。

    get_Mac.sh 脚本源码如下。

    #!/bin/sh
     
     
     
    ## add by heat
    ## Steps for set MAC address and get SN from OTPC
    ##
     
    # read RC register
    echo "read RC register"
    /sbin/devmem 0xfffffc20
    # enable RC clock
    echo "enable the main RC in CKGR_MOR register"
    /sbin/devmem 0xfffffc20 32 0x01370829
    # read RC register again
    echo "read RC register again"
    /sbin/devmem 0xfffffc20
     
    # write the address of header into OTPC_MR
    /sbin/devmem 0xeff00004 32 0x00000000
    # OTPC_CR.READ set to 1 to enable user area
    /sbin/devmem 0xeff00000 32 0x00000040
    # check OTPC_ISR.EOR if 0 to wait for start reading
    /sbin/devmem 0xeff0001c
    sleep 1
    # read the header of the packet
    /sbin/devmem 0xeff00020
    # start address of payload
    /sbin/devmem 0xeff00008 32 0x00000000
     
    # start reading value from OTP
    SN_TAIL=`/sbin/devmem 0xeff00024`
    SN_HEAD=`/sbin/devmem 0xeff00024`
    MAC0_TAIL=`/sbin/devmem 0xeff00024`
    MAC0_HEAD=`/sbin/devmem 0xeff00024`
    echo "SN_TAIL=$SN_TAIL"
    echo "SN_HEAD=$SN_HEAD"
    echo "MAC0_TAIL=$MAC0_TAIL"
    echo "MAC0_HEAD=$MAC0_HEAD"
     
    if [ $MAC0_TAIL == "0x00000000" ] && [ $MAC0_HEAD == "0x00000000" ]; then
        echo "Cannot get correct data from OTP address 0x0, try to read from OTP address 0x29"
        # write the address of header into OTPC_MR
        /sbin/devmem 0xeff00004 32 0x00290000
        # OTPC_CR.READ set to 1 to enable user area
        /sbin/devmem 0xeff00000 32 0x00000040
        # check OTPC_ISR.EOR if 0 to wait for start reading
        /sbin/devmem 0xeff0001c
        sleep 1
        # read the header of the packet
        /sbin/devmem 0xeff00020
        # start address of payload
        /sbin/devmem 0xeff00008 32 0x00000000
     
        # start reading value from OTP
        SN_TAIL=`/sbin/devmem 0xeff00024`
        SN_HEAD=`/sbin/devmem 0xeff00024`
        MAC0_TAIL=`/sbin/devmem 0xeff00024`
        MAC0_HEAD=`/sbin/devmem 0xeff00024`
        echo "SN_TAIL=$SN_TAIL"
        echo "SN_HEAD=$SN_HEAD"
        echo "MAC0_TAIL=$MAC0_TAIL"
        echo "MAC0_HEAD=$MAC0_HEAD"
    fi
     
    NONE_MAC0=false
    NONE_MAC1=false
    ## analyse eth0 MAC address
    if [ $MAC0_TAIL == "0x00000000" ] && [ $MAC0_HEAD == "0x00000000" ]; then
        echo "Cannot get correct MAC address form OTP"
        echo "Use Random MAC Address"
        MAC0_ADDR_RAND=`echo $RANDOM|md5sum`
        MAC0_ADDR_1=00
        MAC0_ADDR_2=1F
        MAC0_ADDR_3=55
        MAC0_ADDR_4=`echo $MAC0_ADDR_RAND|cut -c 1-2`
        MAC0_ADDR_5=`echo $MAC0_ADDR_RAND|cut -c 3-4`
        MAC0_ADDR_6=`echo $MAC0_ADDR_RAND|cut -c 5-6`
    else
        case ${#MAC0_TAIL} in
            2)
            MAC0_OPT_SECTOR_TAIL="0x00000000"
            ;;
            3)
            MAC0_OPT_SECTOR_TAIL="0x0000000"`echo $MAC0_TAIL | cut -b 3`
            ;;
            4)
            MAC0_OPT_SECTOR_TAIL="0x000000"`echo $MAC0_TAIL | cut -b 3-4`
            ;;
            5)
            MAC0_OPT_SECTOR_TAIL="0x00000"`echo $MAC0_TAIL | cut -b 3-5`
            ;;
            6)
            MAC0_OPT_SECTOR_TAIL="0x0000"`echo $MAC0_TAIL | cut -b 3-6`
            ;;
            7)
            MAC0_OPT_SECTOR_TAIL="0x000"`echo $MAC0_TAIL | cut -b 3-7`
            ;;
            8)
            MAC0_OPT_SECTOR_TAIL="0x00"`echo $MAC0_TAIL | cut -b 3-8`
            ;;
            9)
            MAC0_OPT_SECTOR_TAIL="0x0"`echo $MAC0_TAIL | cut -b 3-9`
            ;;
            10)
            MAC0_OPT_SECTOR_TAIL=$MAC0_TAIL
            ;;
            *)
            NONE_MAC0=true
            ;;
        esac
        case ${#MAC0_HEAD} in
            2)
            MAC0_OPT_SECTOR_HEAD="0x0000"
            ;;
            3)
            MAC0_OPT_SECTOR_HEAD="0x000"`echo $MAC0_HEAD | cut -b 3`
            ;;
            4)
            MAC0_OPT_SECTOR_HEAD="0x00"`echo $MAC0_HEAD | cut -b 3-4`
            ;;
            5)
            MAC0_OPT_SECTOR_HEAD="0x0"`echo $MAC0_HEAD | cut -b 3-5`
            ;;
            6)
            MAC0_OPT_SECTOR_HEAD="0x0000"`echo $MAC0_HEAD | cut -b 3-6`
            ;;
            7)
            MAC0_OPT_SECTOR_HEAD="0x000"`echo $MAC0_HEAD | cut -b 3-7`
            ;;
            8)
            MAC0_OPT_SECTOR_HEAD="0x00"`echo $MAC0_HEAD | cut -b 3-8`
            ;;
            9)
            MAC0_OPT_SECTOR_HEAD="0x0"`echo $MAC0_HEAD | cut -b 3-9`
            ;;
            10)
            MAC0_OPT_SECTOR_HEAD=$MAC0_HEAD
            ;;
            *)
            NONE_MAC0=true
            ;;
        esac
     
        MAC0_ADDR_1=`echo ${MAC0_OPT_SECTOR_HEAD} | cut -b 7-8`
        MAC0_ADDR_2=`echo ${MAC0_OPT_SECTOR_HEAD} | cut -b 9-10`
        MAC0_ADDR_3=`echo ${MAC0_OPT_SECTOR_TAIL} | cut -b 3-4`
        MAC0_ADDR_4=`echo ${MAC0_OPT_SECTOR_TAIL} | cut -b 5-6`
        MAC0_ADDR_5=`echo ${MAC0_OPT_SECTOR_TAIL} | cut -b 7-8`
        MAC0_ADDR_6=`echo ${MAC0_OPT_SECTOR_TAIL} | cut -b 9-10`
     
        echo "Got correct MAC address form OTP"
     
    fi
     
    ## set eth0 MAC address
    echo "eth0 MAC: $MAC0_ADDR_1:$MAC0_ADDR_2:$MAC0_ADDR_3:$MAC0_ADDR_4:$MAC0_ADDR_5:$MAC0_ADDR_6"
    /sbin/ifconfig eth0 down
    /sbin/ifconfig eth0 hw ether "$MAC0_ADDR_1:$MAC0_ADDR_2:$MAC0_ADDR_3:$MAC0_ADDR_4:$MAC0_ADDR_5:$MAC0_ADDR_6"
    /sbin/ifconfig eth0 up
     
    ## get SN from OTP
    if [ $SN_TAIL == "0x00000000" ] && [ $SN_HEAD == "0x00000000" ]; then
        echo "Cannot get correct SN form OTP"
    else
        echo "Got correct SN form OTP"
        SN_1=`printf "%08x" ${SN_TAIL}`
        SN_2=`printf "%03x" ${SN_HEAD}`
        echo "SN: ${SN_2}${SN_1}"
        echo "${SN_2}${SN_1}" > /images/data/SN
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    没有做什么特别的操作,就是操作一堆寄存器。。。

  • 相关阅读:
    2023年钙拮抗剂原料药分析:高血压患者数量上升推动需求增长[图]
    Mybatis generator
    Mybatis动态xml中sql语句拼接参数#和$使用
    【ARM】Linux内核驱动之I2C线驱动,测试MPU6050
    Pytorch中关于forward函数的理解与用法
    现在学Oracle是49年入国军么?
    1.摄像机几何
    DS森林叶子编码/森林转二叉树 【数据结构】
    小白跟做江科大32单片机之定时器
    基于FPGA的图像一维FFT变换IFFT逆变换verilog实现,包含tb测试文件和MATLAB辅助验证
  • 原文地址:https://blog.csdn.net/weixin_40407893/article/details/132907146