一只鱼 さんのプロフィールEE小站フォトブログリストその他 ![]() | ヘルプ |
|
4月26日 我的串行外设控制协议,以及在2407上运行的串口界面程序。(续)这个BLOG有BUG,在简体中文下显示的每个Blog的行数不能太多。我把程序分开来贴。
serial.h如下: #ifdef _SERIAL_C_ #define SERIAL_RX_BUFFER_LENGTH 0x80 #define SERIAL_TX_BUFFERA_LENGTH 0x40 #define SERIAL_TX_BUFFERB_LENGTH 0x40 #define SERIAL_TEMP_RX_BUFFER_LENGTH 0x04 #define SERIAL_TEMP_TX_BUFFER_LENGTH 0x04 #define SERIAL_TOKEN 0x55 #define SERIAL_TOKEN_SENT 0x05 #define SERIAL_DATALENGTH_SENT 0x06 #define SERIAL_DATA_SENT 0x07 #define SERIAL_PACKAGE_SENT 0x08 #define SERIAL_COMMAND_QUEUE_LENGTH 0x10 #define SERIAL_PACKAGE_OK 0x00 #define SERIAL_ONLY_TOKEN 0x01 #define SERIAL_ZERO_PACKAGE 0x02 #define SERIAL_PACKAGE_TOO_LARGE 0x03 #define SERIAL_PACKAGE_NOT_COMPLETE 0x04 #define SERIAL_CHECKSUM_MISMATCH 0x05 #define ERR_NONE 0x00 #define ERR_NOT_ENOUGH_MEMORY 0x06 #define ERR_NO_PACKAGE_TO_SEND 0x07 #define ERR_SERIAL_NOT_READY 0x08 #define ERR_NO_COMMAND 0x09 struct SERIAL_RX_STRUCT { unsigned int buf_pos; unsigned int buf[SERIAL_RX_BUFFER_LENGTH]; unsigned int flag_parsing_data; unsigned int temp_buf_pos; unsigned int temp_buf[SERIAL_TEMP_RX_BUFFER_LENGTH]; }; struct SERIAL_TX_STRUCT { unsigned int bufa_write_pos; unsigned int bufa_read_pos; unsigned int bufa[SERIAL_TX_BUFFERA_LENGTH]; unsigned int bufb_write_pos; unsigned int bufb_read_pos; unsigned int bufb[SERIAL_TX_BUFFERB_LENGTH]; unsigned int buf_on_duty; unsigned int is_sending; }; struct SERIAL_COMMAND_STRUCT { unsigned int cmd_id; unsigned int cmd; unsigned int parameter_a; unsigned int parameter_b; unsigned int parameter_c; }; struct SERIAL_COMMAND_QUEUE_STRUCT { unsigned int length; struct SERIAL_COMMAND_STRUCT item[SERIAL_COMMAND_QUEUE_LENGTH]; }; struct SERIAL_STRUCT { struct SERIAL_RX_STRUCT rx; struct SERIAL_TX_STRUCT tx; struct SERIAL_COMMAND_QUEUE_STRUCT cmd_queue; }; struct SERIAL_STRUCT serial; #else extern int serial_is_rx_buffer_empty(); extern void serial_parse_rx_data(); extern int serial_is_tx_buffer_empty(); extern void serial_package_tx_data(unsigned char cmd_id, unsigned char cmd_status, unsigned char data_a, unsigned char data_b, unsigned char data_c); extern int serial_tx_trigger(); extern int serial_read_command(); #endif serial.c如下: #ifndef _SERIAL_C_ #define _SERIAL_C_ #endif #include "basdef.h" #include "serial.h" void serial_initiate() { serial.rx.buf_pos = 0; serial.rx.flag_parsing_data = 0; serial.rx.temp_buf_pos = 0; serial.tx.bufa_write_pos = 0; serial.tx.bufa_read_pos = 0; serial.tx.bufb_write_pos = 0; serial.tx.bufb_read_pos = 0; serial.tx.buf_on_duty = 'A'; serial.tx.is_sending = 0; serial.cmd_queue.length = 0; //跟CPU有关的串口初始化程序,移植请修改这里 sys_regs.SCSR1.bits.SCI_CLKEN = 1; sci_regs.SCICCR.all = 0; sci_regs.SCICCR.bits.SCI_CHAR = 7; sci_regs.SCICTL2.all = 0; sci_regs.SCICTL2.bits.TX_INT_ENA = 1; sci_regs.SCICTL2.bits.RX_BK_INT_ENA = 1; sci_regs.SCIHBAUD = (unsigned char)((global_vars.crystal_freq * global_vars.osc_prescale / (global_vars.sci_baudrate * 8) - 1) / 256); sci_regs.SCILBAUD = (unsigned char)(((unsigned int)(global_vars.crystal_freq * global_vars.osc_prescale / (global_vars.sci_baudrate * 8) - 1)) % 256); sci_regs.SCIPRI.all = 0; sci_regs.SCIPRI.bits.SCIRX_PRIORITY = 1; sci_regs.SCIPRI.bits.SCITX_PRIORITY = 1; sci_regs.SCICTL1.all = 0; sci_regs.SCICTL1.bits.SW_RESET = 1; sci_regs.SCICTL1.bits.RXENA = 1; sci_regs.SCICTL1.bits.TXENA = 1; gpio_regs.MCRA.all = 0; gpio_regs.MCRA.bits.IOPA0_or_SCITXD = 1; gpio_regs.MCRA.bits.IOPA1_or_SCIRXD = 1; cpu_regs.IMR.bits.INT5_mask = 1; EINT(); } //这是放在串口接收和发送中断里的两个handler void serial_rx_handler(unsigned char serial_buf) { if (!serial.rx.flag_parsing_data) { if (serial.rx.buf_pos < SERIAL_RX_BUFFER_LENGTH) serial.rx.buf[serial.rx.buf_pos++] = serial_buf; } else { if (serial.rx.temp_buf_pos < SERIAL_TEMP_RX_BUFFER_LENGTH) serial.rx.temp_buf[serial.rx.temp_buf_pos++] = serial_buf; } } void serial_tx_handler() { if (serial.tx.buf_on_duty == 'A') if (serial.tx.bufa_read_pos < serial.tx.bufa_write_pos) sci_regs.SCITXBUF = serial.tx.bufa[serial.tx.bufa_read_pos++]; else { serial.tx.bufa_read_pos = 0; serial.tx.bufa_write_pos = 0; serial.tx.is_sending = 0; } else if (serial.tx.buf_on_duty == 'B') if (serial.tx.bufb_read_pos < serial.tx.bufb_write_pos) sci_regs.SCITXBUF = serial.tx.bufb[serial.tx.bufb_read_pos++]; else { serial.tx.bufb_read_pos = 0; serial.tx.bufb_write_pos = 0; serial.tx.is_sending = 0; } } //这是串口接收中断服务程序,可以看出serial_rx_handler()和serial_tx_handler()的用法,移植请修改这里 void interrupt INT5_handler() { switch (sys_regs.PIVR) { case 6: //接收中断 serial_rx_handler(sci_regs.SCIRXBUF); break; case 7: //发送中断 serial_tx_handler(); break; }; cpu_regs.IFR.bits.INT5_flag = 1; } unsigned int serial_parse_rx_data() { unsigned int i = 0, j, k, sum, pos, length, new_pos = 0; unsigned int package_status = SERIAL_PACKAGE_OK; unsigned int increase; struct SERIAL_COMMAND_STRUCT temp_cmd; serial.rx.flag_parsing_data = 1; while (i < serial.rx.buf_pos && !new_pos) { if (serial.rx.buf[i++] == SERIAL_TOKEN) { package_status = SERIAL_PACKAGE_OK; if (i >= serial.rx.buf_pos) { package_status = SERIAL_ONLY_TOKEN; goto finish_pre_check; } if (!serial.rx.buf[i]) { package_status = SERIAL_ZERO_PACKAGE; goto finish_pre_check; } if (serial.rx.buf[i] > SERIAL_RX_BUFFER_LENGTH - 3) { package_status = SERIAL_PACKAGE_TOO_LARGE; goto finish_pre_check; } if (serial.rx.buf[i] + i + 2 > serial.rx.buf_pos) { package_status = SERIAL_PACKAGE_NOT_COMPLETE; goto finish_pre_check; } sum = 0; for (j = i + 1; j <= i + serial.rx.buf[i]; j++) sum += serial.rx.buf[j]; if ((sum & 0x00ff) != serial.rx.buf[j]) package_status = SERIAL_CHECKSUM_MISMATCH; finish_pre_check: switch (package_status) { case SERIAL_PACKAGE_OK: length = serial.rx.buf[i]; pos = i; if (serial.cmd_queue.length < SERIAL_COMMAND_QUEUE_LENGTH) while (++pos <= i + length) { // Add command parsing here if (!((serial.rx.buf[pos] & 0x00c0) ^ 0x00c0)) { increase = 0; temp_cmd.cmd_id = serial.rx.buf[pos]; temp_cmd.cmd = 0; temp_cmd.parameter_a = 0; temp_cmd.parameter_b = 0; temp_cmd.parameter_c = 0; if (pos + 1 <= i + length) { temp_cmd.cmd = serial.rx.buf[pos + 1]; increase++; } else goto finish_package_parsing; if (temp_cmd.cmd >= 0x30) if (pos + 2 <= i + length) { increase++; temp_cmd.parameter_a = serial.rx.buf[pos + 2]; } else goto finish_package_parsing; if (temp_cmd.cmd >= 0x60) if (pos + 3 <= i + length) { temp_cmd.parameter_b = serial.rx.buf[pos + 3]; increase++; } else goto finish_package_parsing; if (temp_cmd.cmd >= 0x90) if (pos + 4 <= i + length) { temp_cmd.parameter_c = serial.rx.buf[pos + 4]; increase++; } else goto finish_package_parsing; serial.cmd_queue.item[serial.cmd_queue.length].cmd = temp_cmd.cmd; serial.cmd_queue.item[serial.cmd_queue.length].cmd_id = temp_cmd.cmd_id; serial.cmd_queue.item[serial.cmd_queue.length].parameter_a = temp_cmd.parameter_a; serial.cmd_queue.item[serial.cmd_queue.length].parameter_b = temp_cmd.parameter_b; serial.cmd_queue.item[serial.cmd_queue.length].parameter_c = temp_cmd.parameter_c; serial.cmd_queue.length++; pos += increase; finish_package_parsing: ; } }; i += (length + 2); break; case SERIAL_ONLY_TOKEN: case SERIAL_PACKAGE_NOT_COMPLETE: if (i - 1) { // Copy incomplete package to the head of the buffer for (j = i - 1, k = 0; j < serial.rx.buf_pos; j++, k++) serial.rx.buf[k] = serial.rx.buf[j]; new_pos = k; } else new_pos = serial.rx.buf_pos; break; case SERIAL_ZERO_PACKAGE: i++; break; case SERIAL_PACKAGE_TOO_LARGE: break; case SERIAL_CHECKSUM_MISMATCH: break; } } } for (i = 0; i < serial.rx.temp_buf_pos; i++) serial.rx.buf[i + new_pos] = serial.rx.temp_buf[i]; serial.rx.buf_pos = i + new_pos; if (i) serial.rx.temp_buf_pos = 0; serial.rx.flag_parsing_data = 0; return(ERR_NONE); } int serial_package_tx_data(unsigned char cmd_id, unsigned char cmd_status, unsigned char data_a, unsigned char data_b, unsigned char data_c) { if (serial.tx.buf_on_duty == 'A') { if ((cmd_status < 0x30) && (serial.tx.bufb_write_pos + 5 < SERIAL_TX_BUFFERB_LENGTH)) { serial.tx.bufb[serial.tx.bufb_write_pos++] = SERIAL_TOKEN; serial.tx.bufb[serial.tx.bufb_write_pos++] = 0x02; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_id; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_status; serial.tx.bufb[serial.tx.bufb_write_pos++] = (unsigned char)(cmd_id + cmd_status); } else if ((cmd_status < 0x60) && (serial.tx.bufb_write_pos + 6 < SERIAL_TX_BUFFERB_LENGTH)) { serial.tx.bufb[serial.tx.bufb_write_pos++] = SERIAL_TOKEN; serial.tx.bufb[serial.tx.bufb_write_pos++] = 0x03; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_id; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_status; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_a; serial.tx.bufb[serial.tx.bufb_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a); } else if ((cmd_status < 0x90) && (serial.tx.bufb_write_pos + 7 < SERIAL_TX_BUFFERB_LENGTH)) { serial.tx.bufb[serial.tx.bufb_write_pos++] = SERIAL_TOKEN; serial.tx.bufb[serial.tx.bufb_write_pos++] = 0x04; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_id; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_status; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_a; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_b; serial.tx.bufb[serial.tx.bufb_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a + data_b); } else if (serial.tx.bufb_write_pos + 8 < SERIAL_TX_BUFFERB_LENGTH) { serial.tx.bufb[serial.tx.bufb_write_pos++] = SERIAL_TOKEN; serial.tx.bufb[serial.tx.bufb_write_pos++] = 0x04; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_id; serial.tx.bufb[serial.tx.bufb_write_pos++] = cmd_status; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_a; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_b; serial.tx.bufb[serial.tx.bufb_write_pos++] = data_c; serial.tx.bufb[serial.tx.bufb_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a + data_b + data_c); } else return(ERR_NOT_ENOUGH_MEMORY); return(ERR_NONE); }; if (serial.tx.buf_on_duty == 'B') { if ((cmd_status < 0x30) && (serial.tx.bufa_write_pos + 5 < SERIAL_TX_BUFFERA_LENGTH)) { serial.tx.bufa[serial.tx.bufa_write_pos++] = SERIAL_TOKEN; serial.tx.bufa[serial.tx.bufa_write_pos++] = 0x02; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_id; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_status; serial.tx.bufa[serial.tx.bufa_write_pos++] = (unsigned char)(cmd_id + cmd_status); } else if ((cmd_status < 0x60) && (serial.tx.bufa_write_pos + 6 < SERIAL_TX_BUFFERA_LENGTH)) { serial.tx.bufa[serial.tx.bufa_write_pos++] = SERIAL_TOKEN; serial.tx.bufa[serial.tx.bufa_write_pos++] = 0x03; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_id; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_status; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_a; serial.tx.bufa[serial.tx.bufa_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a); } else if ((cmd_status < 0x90) && (serial.tx.bufa_write_pos + 7 < SERIAL_TX_BUFFERA_LENGTH)) { serial.tx.bufa[serial.tx.bufa_write_pos++] = SERIAL_TOKEN; serial.tx.bufa[serial.tx.bufa_write_pos++] = 0x04; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_id; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_status; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_a; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_b; serial.tx.bufa[serial.tx.bufa_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a + data_b); } else if (serial.tx.bufa_write_pos + 8 < SERIAL_TX_BUFFERA_LENGTH) { serial.tx.bufa[serial.tx.bufa_write_pos++] = SERIAL_TOKEN; serial.tx.bufa[serial.tx.bufa_write_pos++] = 0x04; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_id; serial.tx.bufa[serial.tx.bufa_write_pos++] = cmd_status; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_a; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_b; serial.tx.bufa[serial.tx.bufa_write_pos++] = data_c; serial.tx.bufa[serial.tx.bufa_write_pos++] = (unsigned char)(cmd_id + cmd_status + data_a + data_b + data_c); } else return(ERR_NOT_ENOUGH_MEMORY); return(ERR_NONE); } } int serial_is_rx_buffer_empty() { if (serial.rx.buf_pos) return(0); else return(1); } int serial_is_tx_buffer_empty() { if (serial.tx.bufa_write_pos || serial.tx.bufb_write_pos) return(0); else return(1); } int serial_tx_trigger() { if (!serial.tx.is_sending) { if (serial.tx.buf_on_duty == 'A' && !serial.tx.bufa_write_pos && serial.tx.bufb_write_pos) { serial.tx.buf_on_duty = 'B'; //移植请修改这里,发送触发 sci_regs.SCITXBUF = serial.tx.bufb[serial.tx.bufb_read_pos++]; serial.tx.is_sending = 1; return (ERR_NONE); } if (serial.tx.buf_on_duty == 'B' && !serial.tx.bufb_write_pos && serial.tx.bufa_write_pos) { serial.tx.buf_on_duty = 'A'; //移植请修改这里,发送触发 sci_regs.SCITXBUF = serial.tx.bufa[serial.tx.bufa_read_pos++]; serial.tx.is_sending = 1; return (ERR_NONE); } } } int serial_read_command(unsigned int * cmd, unsigned int * cmd_id, unsigned int * parameter_a, unsigned int * parameter_b, unsigned int * parameter_c) { unsigned int i; if (serial.cmd_queue.length) { * cmd = serial.cmd_queue.item[0].cmd; * cmd_id = serial.cmd_queue.item[0].cmd_id; * parameter_a = serial.cmd_queue.item[0].parameter_a; * parameter_b = serial.cmd_queue.item[0].parameter_b; * parameter_c = serial.cmd_queue.item[0].parameter_c; for (i = 0; i < serial.cmd_queue.length - 1; i++) { serial.cmd_queue.item[i].cmd = serial.cmd_queue.item[i+1].cmd; serial.cmd_queue.item[i].cmd_id = serial.cmd_queue.item[i+1].cmd_id; serial.cmd_queue.item[i].parameter_a = serial.cmd_queue.item[i+1].parameter_a; serial.cmd_queue.item[i].parameter_b = serial.cmd_queue.item[i+1].parameter_b; serial.cmd_queue.item[i].parameter_c = serial.cmd_queue.item[i+1].parameter_c; }; serial.cmd_queue.length--; return (ERR_NONE); } else return (ERR_NO_COMMAND); } 4月23日 我的串行外设控制协议,以及在2407上运行的串口界面程序。相信很多做机电控制的人都遇到这个问题:怎样写一个相当稳定的上位机和下位机的通信界面,我给大家提供一个。
这个串口界面的基本思想是使用双接收和发送缓冲,避免在解析接收到的数据和发送数据的同时,新的数据写入缓冲对界面稳定性的影响。
本界面已经通过2小时连续数据包轰炸式的压力测试(串口通信速度115200),对一般干扰、错误数据包的抵抗能力尚可,已经完成的测试是轰炸30分钟不出错,唯一会造成问题的干扰是连续出现的数据包头令牌,大约在50~100个连续出现的令牌后界面崩溃。
另外附加于这个界面的协议如下:
1.通信方式
2.数据包基本格式
3.暂且叫做Lxz Serial Command Protocol 0.3 (LSCP 0.3)的指令格式
下面是相应的程序了,这个程序针对TMS320LF2407A写的,其中还使用了我过去写的2407BT,能使用结构体的方式访问寄存器。一共有2个文件:serial.h和serial.c,在开发环境中只需要将serial.c加到编译文件列表里,然后在使用到下述几个函数的文件中包含上serial.h就可以了。有兴趣的话可以把它移植到各种CPU上,移植需要修改的代码位置我用红色注出来了。
可以调用的函数申明(包含在serial.h)中:
extern void serial_initiate();
extern int serial_is_rx_buffer_empty();
extern void serial_parse_rx_data(); extern int serial_is_tx_buffer_empty(); extern void serial_package_tx_data(unsigned char cmd_id, unsigned char cmd_status, unsigned char data_a, unsigned char data_b, unsigned char data_c);
extern int serial_tx_trigger(); extern int serial_read_command(unsigned int * cmd, unsigned int * cmd_id, unsigned int * parameter_a, unsigned int * parameter_b, unsigned int * parameter_c); serial_initiate() 用来初始化;
serial_is_rx_buffer_empty() 用来判断接收缓冲是否有数据;
serial_parse_rx_data() 用来以LSCP 0.3 (就是上面写的那个命令协议) 的标准解析串口缓冲里的数据;
serial_is_tx_buffer_empty() 用来判断发送缓冲是否有数据;
serial_package_tx_data() 用来以LSCP 0.3的标准封装送到发送缓冲里的数据;
serial_tx_trigger() 用来触发串口发送;
serial_read_command() 用来读取以LSCP 0.3标准解析出来的串口命令; 注意,串口会自动接收数据,但是不会自动解析和自动触发数据发送(一旦触发之后就会连续发送缓冲中的数据),你需要在程序的主循环里加入判断缓冲是否为空、解析和触发发送的指令,例如这样的一个main.c
#ifndef _MAIN_C_
#define _MAIN_C_ #endif #include "basdef.h"
#include "serial.h" void initiate()
{ global_vars.crystal_freq = 10000000; global_vars.osc_prescale = 4; global_vars.sci_baudrate = 115200; pre_initiate(); sys_regs.SCSR1.bits.CLKPS = 0; cpu_regs.IMR.all = 0; cpu_regs.IFR.all = 0xffff; serial_initiate(); } void main()
{ unsigned int i; unsigned int cmd, cmd_id, parameter_a, parameter_b, parameter_c; initiate();
while(1) { if (!serial_is_rx_buffer_empty()) serial_parse_rx_data(); while (!serial_read_command(&cmd, &cmd_id, & parameter_a, & parameter_b, & parameter_c)) serial_package_tx_data(cmd_id, cmd, parameter_a, parameter_b, parameter_c); if (!serial_is_tx_buffer_empty()) serial_tx_trigger(); }; } 程序请看下一篇日志。 4月6日 S3C2410的布线。之前写了那么多关于操作系统的东东,一直没有涉及到作为一个硬件工程师的实质——PCB的制作。
花了大约五天才把S3C2410的SDRAM部分搞定,有些吐血的感觉。说说我的布线:线路板尺寸为30mm*100mm左右的长条形,线长匹配在5mm范围内,主要的SDRAM控制信号做阻抗匹配。关于阻抗和线长匹配的内容请看“高速PCB资料”。
出来的板子大概是这样的,第一次画6层板,也不知道这样的能不能加工。
图中的蛇形线用途就是平衡各个NET的长度。从CPU出来到SDRAM的拓扑结构大约是CPU输出分支接2个SDRAM。这种分支结构在Protel里好像是没有办法自动做线长匹配的(应该是我比较笨,希望高手看到了予以指教),或者以后有空了学学专业的PCB软件,看看别的行不行。 但是事实上,这块板子似乎没有必要做这么复杂的事情,我的大牛师兄的板子是这样的: 呵呵,他的拓扑结构是CPU->SDRAM1->SDRAM2,没有阻抗匹配。但是这样走线少,布线容易的多;再提供一个数据,他的最长和最短NET失配长度差达到37mm。但是人家的板子就是好使。我有个同学做了个CycloneII+SRAM的板,读写时钟也很高,啥匹配没有,就是好使。看来课本是不是应该改改了,哈哈。 下面来抱怨下三星这个白痴公司,这是Analog Device的ADSPBF531的电源管脚分布
这是Altera的EP1C6F256的电源管脚分布 这是三星的S3C2410的电源管脚分布
希望我的原理图和封装都没有画错……真是无语了,一点也不为PCB工程师枉死的脑细胞考虑。看来韩国人毕竟是科技的暴发户,细节上完全比不上美国公司。不过回头来想想中国……我们连像样的单片机都生产不了,学IC设计的兄弟们,加油吧! 今天到这里。 4月2日 MC33035和有刷/无刷电机控制。这篇文章主要留给我的师弟们看的。
首先,无刷直流电机的工作原理。无刷电机的优点、原理请你自己百度下。原理一句话概括就是用电子换向器换向的类似同步电机的电机。
无刷电机有转子位置传感器,电子换向器可以知道转子和绕组的相对位置,而进行换向。但是有的无刷电机没有转子位置传感器,这是通过定子线圈的反电动势来确定转子的位置的,有专用的控制芯片。本文使用的是比较常用的有转子位置传感器的无刷电机控制芯片MC33035。关于它的资料也很多,33035自己的DATASHEET上就有一套用模拟电压输入控制电机的图,就是这个:
按照这个图进行开环控制有问题,调速的范围很窄。可以看10脚的波形,这是个由MC33035内部振荡器产生的上不挨VCC下不挨GND的三角波,而11、12脚后MC33035内含的的那个运放被接成了电压跟随,11脚输入电压的范围是0~6V左右,实际上只在10脚的三角波幅值覆盖的范围(大约为2~4V)内有调速效果,因此,调速的范围很窄。
由于我们实验室一直有用DSP产生PWM控制电机的传统,呵呵。所以我采用了这样的连接方法:
注意这只是我完整原理图的一部分,有些连接如9脚的没有画;R29和R30起到的是“跳线”的作用,如果需要22脚电平为低,请在PCB上焊下R29保留R30。11脚和13脚为PWM输入,12脚接大约1V电压,这样就可以PWM信号输入调速了。请注意PWM的频率和电机的低速稳定性有关系,频率请看我的程序。其他东西不需要太多解释,原理图就能明白。如果需要模拟电压方式的调速,建议使用MC33039进行闭环调速,具体的电路可以参考张明辉师兄的。如果想用33035控制有刷电机也是可以的,这就需要把33035的霍尔传感器三个输入依次固定接为“高、低、低”,在A相和C相的构成H桥上连接有刷电机的绕组就可以了。这样接的原因请看33035的DATASHEET里的那个真值表。不过这样用还不如使用专用的MC33887呢。
另外有一个需要注意的地方是选择器件的时候一定要考虑MOSFET的Drain-Source和Gate-Source极限电压和供电电压的大小关系,否则会导致MOSFET烧毁;为了购买和选型方便,可以在力源www.icbase.com网站上寻找你需要的MOSFET器件。 |
|
|