一只鱼 さんのプロフィール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.数据包基本格式
[令牌] [数据包长度] [数据1] ... [数据n] [校验和]
 
[令牌] = 0x55
[数据包长度] = 0x01 ~ 0xFF
[校验和] = ( [数据1] + [数据2] + ... + [数据n] ) 取除以0x100的余数
例如:
0x55 0x01 0x01 0x01 就是一个合法的数据包
0x55 0x02 0x01 0x02 0x01 就是一个校验和错误的数据包
3.暂且叫做Lxz Serial Command Protocol 0.3 (LSCP 0.3)的指令格式
上位机发往下位机的指令格式:
 
[指令ID] [指令] [[参数1] [参数2] [参数3]]
 
[指令ID] 为上位机为某个指令分配的ID,指令ID的范围在0xC0 ~ 0xFF,之后我会解释使用指令ID的意图。
[指令] 为表示者某项操作的一个数,范围为0x00 ~ 0xFF
[参数1] 、[参数2] 、[参数3] 为执行某个指令所必需的参数,范围为0x00 ~ 0xFF。
不同的指令参数个数是不同的,[指令]在0x00到0x2F之间时,无参数;[指令]在0x30到0x5F之间时,有且只有1个参数,以此类推,最多3个参数。
 
下位机发往上位机的指令格式:
 
[返回的指令ID] [指令执行状态] [参数1] [参数2] [参数3]
[返回的指令ID] 为与某个上位机已经发出的指令对应的,上位机分配的指令ID。
[指令执行状态] 为向上位机返回的表示某个指令执行状态的一个数,范围为0x00 ~ 0xFF。
[参数1] 、[参数2] 、[参数3] 为执行某个指令返回的状态所必需的参数,范围为0x00 ~ 0xFF。
不同的执行状态返回参数个数是不同的,这和上述指令参数的情况相同。
 
数据包和命令协议合起来,举些例子:
 
上位机 -> 下位机: 0x55 0x02 0xC0 0x01 0xC1 为一个指令ID为0xC0的没有参数的控制指令。
下位机 -> 上位机: 0x55 0x05 0xC8 0x92 0x0A 0x1F 0x88 0x0B为一个返回指令ID为0xC8的返回执行状态为0x92的含有三个参数0x0A,0x1F,0x88的控制指令的返回。
 
说说使用指令ID的意义:指令ID就是一个上位机为了检测指令执行情况而挂的“号”,对于那种必须在一定时限内对上位机回复的下位机指令,判断接收到的指令ID可以用来监测指令执行情况。当然指令ID的意义不仅仅在此,对于可以同时执行的N条指令,上位机可以一口气发出去,让下位机执行,就算其中有一条是有时限的指令,也只要监测是否在一定时间内有相应的指令ID接收到即可,不用让上位机等下位机。
下面是相应的程序了,这个程序针对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器件。