spi_init();<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> reset_init(); // use s5x_isp_frequency spi_conf(s5x_isp_frequency); // toggle reset reset_output(); reset_set(); delay_ms(100); reset_input(); delay_ms(30); reset_output(); reset_set(); delay_ms(10); // enter into program mode command cmd_buf[0] = 0xAC; cmd_buf[1] = 0x53; cmd_buf[2] = 0x00; cmd_buf[3] = 0x00; tmp8 = 0; // ret[3] should be 0x69 spi_io(cmd_buf, 4, &tmp8, 3, 1); if((ERROR_OK != commit()) || (tmp8 != 0x69)) { LOG_ERROR("fail to enter into program mode, try a slower speed\n"); ret = ERROR_FAIL; goto leave_program_mode; } |
按照算法,应该基本都看得懂,这里只是简单的作一下说明:
1.所有这些调用(spi_ini,reset_init,spi_conf,reset_output,reset_input,delay_ms,spi_io等),都是宏定义,通过宏来把应用层的函数格式转换成编程器统一驱动层的函数格式。代码如下:
#define spi_init() p->spi_init() #define spi_fini() p->spi_fini() #define spi_conf(speed) p->spi_config((speed), SPI_CPOL_LOW, SPI_CPHA_1EDGE, SPI_MSB_FIRST) #define spi_io(out, outlen, in, inpos, inlen) p->spi_io((out), (in), (outlen), (inpos), (inlen)) #define reset_init() p->gpio_init() #define reset_fini() p->gpio_fini() #define reset_output() p->gpio_config(GPIO_SRST, 1) #define reset_input() p->gpio_config(GPIO_SRST, 0) #define reset_set() p->gpio_out(GPIO_SRST, GPIO_SRST) #define reset_clr() reset_input() #define delay_ms(ms) p->delayms((ms) | 0x8000) #define delay_us(us) p->delayus((us) & 0x7FFF) #define commit() p->peripheral_commit() |
其中,p是传递给目标芯片的编程函数的编程器数据结构的指针,vsprog理论上是可以支持一些其他的编程器的,需要增加对应的驱动和数据结构即可。这里的宏就是调用编程器的数据结构里的对应函数,并做参数转换。
2.有一些函数需要返回数据的,是通过设置返回数据的大小,起始位置,并提供数据指针来实现的。比如这里的spi_io,第一和第二个参数是发送数据的指针和数据的字节大小,第三个参数是接收数据的指针(不接收就设置为NULL),第四个参数是指从返回数据的第n个字节开始复制,第五个参数是复制的字节数。这样spi_io(cmd_buf, 4, &tmp8, 3, 1);就是发送4个进入编程模式的命令,并接收第四个字节(序号是3,一个字节)的返回数据,并放入tmp8变量。这里需要注意的是,这些函数调用并不涉及到实际的操作,而只是记录下要发送的命令,在调用commit的时候,才会发送命令并接收执行结果(这样可以得到最高的USB带宽的利用率)。所以,需要保证调用commit时候,传递的指针可访问(如果在一个子函数中,传递一个auto变量的指针(位于堆栈上),而在子函数返回后才调用commit,这样就出问题了,因为在执行实际的操作的时候,会把返回的数据复制到未知的堆栈上)。
3.S51的复位是高电平有效,所以输出高电平是使芯片复位。而释放并不是输出低电平,而是把复位控制信号设置为输入。
对于高层的代码,一切似乎都很简单,但是对于底层的驱动代码,就可能不是非常容易理解了。当然,如果对底层不感兴趣的话,可以直接忽略,而只是看一下接口部分提供的函数即可(在编程器的数据结构中定义的,可以被应用层调用的函数)。而且,我也不确定,我是否可以清楚地描述。
1.编程器的数据结构(programmer_info_t,位于programmer.h文件中),对于一些基本的应用,编程器的数据接口定义了应用层需要调用的函数。比如,对于SPI接口:
// spi RESULT (*spi_init)(void); RESULT (*spi_fini)(void); RESULT (*spi_config)(uint16 kHz, uint8 cpol, uint8 cpha, uint8 first_bit); RESULT (*spi_io)(uint8 *out, uint8 *in, uint16 len, uint16 inpos, uint16 inlen); |
这里,spi_config的kHZ是SPI频率,cpol和cpha可以组合成SPI的4种模式,first_bit用于定义MSB_First还是LSB_First。spi_io前面已经说明过了(可能和应用层调用的方式不同,所以通过宏来转换了)。
2.数据结构的初始化
编程器的数据结构由编程器的驱动自己初始化,数据结构位于最前面的3个成员需要在结构定义时初始化:
programmer_info_t programmers_info[] = { // versaloon { VERSALOON_STRING, // name versaloon_check_argument, // check_argument versaloon_init_capability, // init_capability }, { NULL } }; |
versaloon_check_argument用来解析传递给编程器的命令行参数(注意不能和其他命令行参数的名字(一个字节)冲突),versaloon_init_capability用于初始化编程器的各种能力:
RESULT versaloon_init_capability(void *p) { ((programmer_info_t *)p)->init = versaloon_init; ((programmer_info_t *)p)->fini = versaloon_fini; ((programmer_info_t *)p)->interfaces = (SPI | GPIO | ISSP | JTAG); // SPI ((programmer_info_t *)p)->spi_init = versaloon_spi_init; ((programmer_info_t *)p)->spi_fini = versaloon_spi_fini; ((programmer_info_t *)p)->spi_config = versaloon_spi_config; ((programmer_info_t *)p)->spi_io = versaloon_spi_io; |
其中,interfaces为该编程器支持的硬件接口(USB_TO_DELAY不算,因为这个延时也可以在上位机实现)。这样,主程序就可以判断需要的功能是否都具备。其他就是各个接口函数指针的初始化了。
3.命令的收集
USB_TO_XXX之所以可以简化很多操作,是因为由于协议通用,所以很多代码都可以通用。比如:
RESULT usbtospi_init(void) { return usbtoxxx_init_command(USB_TO_SPI, &usbtospi_num_of_interface); } RESULT usbtospi_io(uint8 interface_index, uint8 *out, uint8 *in, uint16 outlen, uint16 inpos, uint16 inlen) { #ifdef PARAM_CHECK if(interface_index > 7) { LOG_BUG("invalid inteface_index %d\n", interface_index); return ERROR_FAIL; } #endif return usbtoxxx_inout_command(USB_TO_SPI, interface_index, out, outlen, outlen, in, inpos, inlen, 1); } |
想不到这2个函数竟然可以这么简单吧?
usbtoxxx_init_command设置对应的接口的USB_TO_XXX_INIT命令,该命令返回一个字节的数据,表明当前编程器支持的该种接口的数量,这里把这个数量放在usbtospi_num_of_interface变量中。
usbtoxxx_inout_command也类似,对应接口的USB_TO_XXX_INOUT命令。
更加想不到的是,这些usbtoxxx_xxxx_command实质上都是调用同一个函数(usbtoxxx_add_command):
RESULT usbtoxxx_add_command(uint8 type, uint8 cmd, uint8 *cmdbuf, uint16 cmdlen, uint16 retlen, uint8 *wantbuf, uint16 wantpos, uint16 wantlen, uint8 collect); #define usbtoxxx_init_command(type, interface_num) usbtoxxx_add_command((type), (USB_TO_XXX_INIT), &null_char, 1, 1, interface_num, 0, 1, 0) #define usbtoxxx_fini_command(type) usbtoxxx_add_command((type), (USB_TO_XXX_FINI), &null_char, 1, 0, NULL, 0, 0, 0) #define usbtoxxx_conf_command(type, port, cmdbuf, cmdlen) usbtoxxx_add_command((type), (USB_TO_XXX_CONFIG | (port)), (cmdbuf), (cmdlen), 0, NULL, 0, 0, 0) #define usbtoxxx_inout_command(type, port, cmdbuf, cmdlen, retlen, wantbuf, wantpos, wantlen, c) usbtoxxx_add_command((type), (USB_TO_XXX_IN_OUT | (port)), (cmdbuf), (cmdlen), retlen, wantbuf, wantpos, wantlen, (c)) #define usbtoxxx_in_command(type, port, cmdbuf, cmdlen, retlen, wantbuf, wantpos, wantlen, c) usbtoxxx_add_command((type), (USB_TO_XXX_IN | (port)), (cmdbuf), (cmdlen), retlen, wantbuf, wantpos, wantlen, (c)) #define usbtoxxx_out_command(type, port, cmdbuf, cmdlen, c) usbtoxxx_add_command((type), (USB_TO_XXX_OUT | (port)), (cmdbuf), (cmdlen), 0, NULL, 0, 0, (c)) #define usbtoxxx_poll_command(type, port, cmdbuf, cmdlen) usbtoxxx_add_command((type), (USB_TO_XXX_POLL | (port)), (cmdbuf), (cmdlen), 0, NULL, 0, 0, 0) |
for(i = 0; i < versaloon_pending_idx; i++) { // get result data if((versaloon_pending_result.want_data_size > 0) && (versaloon_pending_result.data_buffer != NULL)) { memcpy(versaloon_pending_result.data_buffer, versaloon_buf + usbtoxxx_buffer_index + versaloon_pending_result.want_data_pos, versaloon_pending_result.want_data_size); } usbtoxxx_buffer_index += versaloon_pending_result.actual_data_size; } |
5.组合命令
为了高效的操作,有些命令是可以支持组合命令的,比如:
spi_io的几次连续的调用,是可以组合成一个单一的USB_TO_XXX_INOUT命令的。usbtoxxx_add_command的最后一个参数collect提供了这个支持,如果命令支持组合功能,并且调用时希望使用这个功能,就传递1。经测试,这个功能对速度有一定的影响。并且,目前SPI、ISSP、JTAG接口都支持这个功能。
用户131114 2008-11-17 13:03