这个模块主要提供对外部设备的控制。这里是电机设备。
电机设备和MCU的通信,通过UART完成。
对这个控制模块的封装,一方面是数据,一方面是操作集。
这里关注几个关键点。
先来看数据的封装
class MotorMcu
{
public:
struct UartLiteDev* uart_;
SystemManager* sys_;
...
};
首先,定义索引类的成员变量。
这里面,由于需要使用UART进行操作,所以需要关联一个UART结构体对象,这里没有使用内嵌方式,因为UART是整板的资源,所以使用了索引关联的方式。
这里,由于MotorMCU的对象,是system manager对象的一个成员对象,而且system manager具有最大化的索引范围,所以,这里以索引关联的方式,关联了一个上级system manager对象,形成了源端引用。
然后,定义复杂结构的成员变量,
class MotorMcu
{
public:
struct MCU2FPGACommand mcu_cmd_;
...
};
这里面,内嵌了一个MCU2FPGACommand结构体。这个结构体将命令块封装成结构体。配套的,需要为这个结构体设计相应的操作集。
struct MCU2FPGACommand
{
struct MCU80msCommand mcu80ms_cmd;
struct MCUTestCommand mcutest_cmd;
struct MCUIrisCommand mcuiris_cmd;
};
void mcu2fpga_cmd_init(struct MCU2FPGACommand* cmd);
void mcu2fpga_cmd_parse(struct MCU2FPGACommand* mcu_cmd);
void mcu2fpga_cmd_demux(struct RingBuffer* ring, struct MCU2FPGACommand* mcu_cmd);
这里面又是由三个结构体对象构成。分别定义为
struct MCU80msCommand
{
int fresh;
u8 data[13];
int action;
u16 zoom_motor_value;
u16 focus_motor_value;
u8 zoom_limit;
u8 focus_limit;
u8 optics_fog_close_limit;
u8 optics_fog_open_limit;
};
void mcu80ms_cmd_parse(struct MCU80msCommand* mcu80ms_cmd);
struct MCUTestCommand
{
int fresh;
u8 data[10];
int action;
u8 filter_ok;
u8 iris_ok;
u8 zoom_ok;
u8 focus_ok;
};
void mcutest_cmd_parse(struct MCUTestCommand* mcutest_cmd);
struct MCUIrisCommand
{
int fresh;
u8 data[10];
int action;
u8 iris_limit_mode;
};
void mcuiris_cmd_parse(struct MCUIrisCommand* mcuiris_cmd);
另一个需要通信的命令块,也要封装成结构体,及其对应的操作集,
struct FPGA2MCUCommand
{
u8 data[19];
int len;
};
void fpga2mcu_cmd_init(struct FPGA2MCUCommand* cmd);
void fpga2mcu_set_zoom_field(struct FPGA2MCUCommand* cmd, u8 zoom_mode, u8 zoom_speed);
void fpga2mcu_set_focus_field(struct FPGA2MCUCommand* cmd, u8 focus_mode, u8 focus_speed);
void fpga2mcu_set_filter_field(struct FPGA2MCUCommand* cmd, u8 filter_mode, u8 filter_speed);
void fpga2mcu_set_fov_value(struct FPGA2MCUCommand* cmd, u16 fov_value);
void fpga2mcu_set_focus_value(struct FPGA2MCUCommand* cmd, u16 focus_value);
void fpga2mcu_set_iris_field(struct FPGA2MCUCommand* cmd, u8 iris_mode);
void fpga2mcu_cmd_calc_crc(struct FPGA2MCUCommand* cmd);
定义完了命令块,之后需要定义常量参数,
在C语言中,通常使用宏来定义常量,
在C++中,可以用const的方式来定义,这样封装性和可读性更好,说明这些常量是隶属于某个类的。如果用宏来定义,则是属于全局的。这样,不可避免的需要定义超长的名字,以避免和其他位置的同名常量发生命名冲突。
class MotorMcu
{
public:
...
//固定参数
const float max_optics_focus = 275;
const float min_optics_focus = 27.5;
const float max_optics_fov = 42;
const float min_optics_fov = 4.2;
const u32 min_focus_value = 30000;
const u32 max_focus_value = 60000;
const u32 min_zoom_value = 3000;//3000
const u32 max_zoom_value = 30000;//30000
...
};
定义完了常量后,需要定义特定用途的基本类型成员变量(非结构体对象)。
class MotorMcu
{
public:
...
//mcu反馈
u16 zoom_motor_value_;//0.1mm
u16 focus_motor_value_;//16384~65536
u8 zoom_limit_;//变倍到达指定位置
u8 focus_limit_;//调焦到达指定位置
u8 optics_fog_close_limit_;//光学透雾关到位
u8 optics_fog_open_limit_;//光学透雾开到位
u8 focus_direct_mode_;
u8 filter_ok_;
u8 iris_ok_;
u8 zoom_ok_;
u8 focus_ok_;
u8 iris_limit_mode_;
封装好了数据,就要封装相关的操作集。
+++++++++++++++++++++++++++++++++++++++++++++++++++
1)构建函数,
MotorMcu::MotorMcu(struct UartLiteDev* uart, SystemManager* sys) : uart_(uart), sys_(sys), focus_direct_mode_(0)
{
mcu2fpga_cmd_init(&mcu_cmd_);
}
2)SET系列
void MotorMcu::set_zoom_up(int speed)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_zoom_field(&cmd, 1, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_zoom_down(int speed)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_zoom_field(&cmd, 2, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_zoom_value(int speed, int value)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_zoom_field(&cmd, 3, (u8)speed);
fpga2mcu_set_fov_value(&cmd, (u16)value);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_focus_far(int speed)
{
plog("set_focus_far: %d\r\n", speed);
u8 focus_mode = (focus_direct_mode_ == 0) ? 1 : 2;
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_focus_field(&cmd, focus_mode, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_focus_near(int speed)
{
plog("set_focus_near: %d\r\n", speed);
u8 focus_mode = (focus_direct_mode_ != 0) ? 1 : 2;
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_focus_field(&cmd, focus_mode, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_focus_value(int speed, int value)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_focus_field(&cmd, 3, (u8)speed);
fpga2mcu_set_focus_value(&cmd, value);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_filter_light(int speed)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_filter_field(&cmd, 1, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_filter_ir(int speed)
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_filter_field(&cmd, 2, (u8)speed);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_motor_stop()
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
void MotorMcu::set_motor_selftest()
{
struct FPGA2MCUCommand cmd;
fpga2mcu_cmd_init(&cmd);
fpga2mcu_set_iris_field(&cmd, 4);
fpga2mcu_cmd_calc_crc(&cmd);
uart_lite_send(uart_, cmd.data, cmd.len);
}
可以看出,这几个函数的作用,就是填充命令块,然后通过串口将命令块发送出去。
3)GET系列
u32 MotorMcu::get_optics_focus() {
return zoom_motor_value_;
}
int MotorMcu::get_optics_focus_state() {
u32 now_focus = zoom_motor_value_;
u32 almost_min_focus = min_zoom_value * 110 / 100;
u32 almost_max_focus = max_zoom_value * 90 / 100;
if (now_focus < almost_min_focus) {
return 0;
} else if (now_focus < almost_max_focus) {
return 1;
} else {
return 2;
}
}
可以看出,这几个函数的作用,就是获取成员变量值,或者根据成员变量值经过计算后,返回相应的状态值。
4)ACTION系列
void MotorMcu::mcu2fpga_cmd_action(struct MCU2FPGACommand* mcu_cmd)
{
struct MCU80msCommand* mcu80ms_cmd = &mcu_cmd->mcu80ms_cmd;
struct MCUTestCommand* mcutest_cmd = &mcu_cmd->mcutest_cmd;
struct MCUIrisCommand* mcuiris_cmd = &mcu_cmd->mcuiris_cmd;
mcu80ms_cmd_action(mcu80ms_cmd);
mcutest_cmd_action(mcutest_cmd);
mcuiris_cmd_action(mcuiris_cmd);
}
void MotorMcu::mcu80ms_cmd_action(struct MCU80msCommand* mcu80ms_cmd)
{
if (!mcu80ms_cmd->action)
return;
mcu80ms_cmd->action = 0;
zoom_motor_value_ = mcu80ms_cmd->zoom_motor_value;
focus_motor_value_ = mcu80ms_cmd->focus_motor_value;
zoom_limit_ = mcu80ms_cmd->zoom_limit;
focus_limit_ = mcu80ms_cmd->focus_limit;
optics_fog_close_limit_ = mcu80ms_cmd->optics_fog_close_limit;
optics_fog_open_limit_ = mcu80ms_cmd->optics_fog_open_limit;
}
void MotorMcu::mcutest_cmd_action(struct MCUTestCommand* mcutest_cmd)
{
if (!mcutest_cmd->action)
return;
mcutest_cmd->action = 0;
filter_ok_ = mcutest_cmd->filter_ok;
iris_ok_ = mcutest_cmd->iris_ok;
zoom_ok_ = mcutest_cmd->zoom_ok;
focus_ok_ = mcutest_cmd->focus_ok;
}
void MotorMcu::mcuiris_cmd_action(struct MCUIrisCommand* mcuiris_cmd)
{
if (!mcuiris_cmd->action)
return;
mcuiris_cmd->action = 0;
iris_limit_mode_ = mcuiris_cmd->iris_limit_mode;
}
可以看出,这几个函数的作用,就是从命令块中取出数据,并赋值给对应的成员变量。
5)DEMUX,功能级函数
void MotorMcu::demux(struct RingBuffer* ring, struct MCU2FPGACommand* mcu_cmd)
{
unsigned char temp;
unsigned char buff[35];
struct MCU80msCommand* mcu80ms_cmd = &mcu_cmd->mcu80ms_cmd;
struct MCUTestCommand* mcutest_cmd = &mcu_cmd->mcutest_cmd;
struct MCUIrisCommand* mcuiris_cmd = &mcu_cmd->mcuiris_cmd;
unsigned char* pdata = (unsigned char*)&buff[0];
int timeout = 1000;
u32 start_time = sys_->get_timestamp();
while (ring_buffer_valid_len(ring) >= 10)
{
u32 end_time = sys_->get_timestamp();
int dlt_time = (end_time > start_time) ? (end_time - start_time) : (start_time - end_time);
if (dlt_time > timeout) {
ring_buffer_pop(ring, &temp);
break;
}
ring_buffer_get_value(ring, 0, &buff[0]);
ring_buffer_get_value(ring, 1, &buff[1]);
ring_buffer_get_value(ring, 2, &buff[2]);
ring_buffer_get_value(ring, 3, &buff[3]);
ring_buffer_get_value(ring, 4, &buff[4]);
ring_buffer_get_value(ring, 5, &buff[5]);
if ((pdata[0] == 0x66) && (pdata[1] == 0xAA))
{
if (pdata[5] == 0x02)
{
if (ring_buffer_valid_len(ring) >= 13)
{
for (int i = 0; i < 13; i++)
{
ring_buffer_pop(ring, &mcu80ms_cmd->data[i]);
}
mcu80ms_cmd->fresh = 1;
break;
}
}
else if (pdata[5] == 0x03)
{
for (int i = 0; i < 10; i++)
{
ring_buffer_pop(ring, &mcutest_cmd->data[i]);
}
mcutest_cmd->fresh = 1;
break;
}
else if (pdata[5] == 0x04)
{
for (int i = 0; i < 10; i++)
{
ring_buffer_pop(ring, &mcuiris_cmd->data[i]);
}
mcuiris_cmd->fresh = 1;
break;
}
else
{
ring_buffer_pop(ring, &temp);
}
}
else
{
ring_buffer_pop(ring, &temp);
}
}
}
通过直接操作UART接收的FIFO,进行命令块的数据提取。
6)接收处理,业务级函数
void MotorMcu::recv_loop()
{
//解析mcu的反馈信息
demux(&uart_->recv_buff, &mcu_cmd_);
mcu2fpga_cmd_parse(&mcu_cmd_);
//执行mcu的反馈
mcu2fpga_cmd_action(&mcu_cmd_);
}
首先从FIFO中提取数据到mcu_cmd_结构体对象中,
然后解析mcu_cmd,
然后根据解析后的mcu_cmd,执行action.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
补充:
对定义的结构体对象,定义的对应的操作集,是全局的。
1)MCUCommand
void mcu2fpga_cmd_init(struct MCU2FPGACommand* cmd)
{
memset(cmd, 0, sizeof(struct MCU2FPGACommand));
}
void mcu2fpga_cmd_parse(struct MCU2FPGACommand* mcu_cmd)
{
struct MCU80msCommand* mcu80ms_cmd = &mcu_cmd->mcu80ms_cmd;
struct MCUTestCommand* mcutest_cmd = &mcu_cmd->mcutest_cmd;
struct MCUIrisCommand* mcuiris_cmd = &mcu_cmd->mcuiris_cmd;
mcu80ms_cmd_parse(mcu80ms_cmd);
mcutest_cmd_parse(mcutest_cmd);
mcuiris_cmd_parse(mcuiris_cmd);
}
void mcu80ms_cmd_parse(struct MCU80msCommand* mcu80ms_cmd)
{
int ret;
if (!mcu80ms_cmd->fresh)
return;
mcu80ms_cmd->fresh = 0;
mcu80ms_cmd->action = 0;
ret = check_xor_crc(&mcu80ms_cmd->data[2], 9, mcu80ms_cmd->data[11]);
if (ret < 0)
return;
unsigned char* pdata = (unsigned char*)&mcu80ms_cmd->data[0];
mcu80ms_cmd->action = 1;
mcu80ms_cmd->zoom_motor_value = (((unsigned short)pdata[7]) << 8) + ((unsigned short)pdata[6]);
mcu80ms_cmd->focus_motor_value = (((unsigned short)pdata[8]) << 8) + ((unsigned short)pdata[9]);
mcu80ms_cmd->zoom_limit = pdata[10] & 0x1;
mcu80ms_cmd->focus_limit = (pdata[10] >> 1) & 0x1;
mcu80ms_cmd->optics_fog_close_limit = (pdata[10] >> 2) & 0x1;
mcu80ms_cmd->optics_fog_open_limit = (pdata[10] >> 3) & 0x1;
}
void mcutest_cmd_parse(struct MCUTestCommand* mcutest_cmd)
{
int ret;
if (!mcutest_cmd->fresh)
return;
mcutest_cmd->fresh = 0;
mcutest_cmd->action = 0;
ret = check_xor_crc(&mcutest_cmd->data[2], 6, mcutest_cmd->data[8]);
if (ret < 0)
return;
unsigned char* pdata = (unsigned char*)&mcutest_cmd->data[0];
mcutest_cmd->action = 1;
mcutest_cmd->filter_ok = pdata[7] & 0x1;
mcutest_cmd->iris_ok = (pdata[7] >> 1) & 0x1;
mcutest_cmd->zoom_ok = (pdata[7] >> 2) & 0x1;
mcutest_cmd->focus_ok = (pdata[7] >> 3) & 0x1;
}
void mcuiris_cmd_parse(struct MCUIrisCommand* mcuiris_cmd)
{
int ret;
if (!mcuiris_cmd->fresh)
return;
mcuiris_cmd->fresh = 0;
mcuiris_cmd->action = 0;
ret = check_xor_crc(&mcuiris_cmd->data[2], 6, mcuiris_cmd->data[8]);
if (ret < 0)
return;
unsigned char* pdata = (unsigned char*)&mcuiris_cmd->data[0];
mcuiris_cmd->action = 1;
mcuiris_cmd->iris_limit_mode = pdata[7];
}
2)FPGA2MCUCommad
void fpga2mcu_cmd_init(struct FPGA2MCUCommand* cmd)
{
memset(cmd->data, 0, sizeof(cmd->data));
cmd->data[0] = 0x55;
cmd->data[1] = 0xAA;
cmd->data[2] = 0x02;
cmd->data[3] = 0x0D;
cmd->data[4] = 0x8F;
cmd->data[5] = 0x0B;
cmd->data[18] = 0xF0;
cmd->len = 19;
}
void fpga2mcu_set_zoom_field(struct FPGA2MCUCommand* cmd, u8 zoom_mode, u8 zoom_speed)
{
cmd->data[6] = zoom_mode;
cmd->data[7] = zoom_speed;
}
void fpga2mcu_set_focus_field(struct FPGA2MCUCommand* cmd, u8 focus_mode, u8 focus_speed)
{
cmd->data[8] = focus_mode;
cmd->data[9] = focus_speed;
}
void fpga2mcu_set_filter_field(struct FPGA2MCUCommand* cmd, u8 filter_mode, u8 filter_speed)
{
cmd->data[10] = filter_mode;
cmd->data[11] = filter_speed;
}
void fpga2mcu_set_fov_value(struct FPGA2MCUCommand* cmd, u16 fov_value)
{
cmd->data[12] = (fov_value >> 8) & 0xff;
cmd->data[13] = fov_value & 0xff;
}
void fpga2mcu_set_focus_value(struct FPGA2MCUCommand* cmd, u16 focus_value)
{
cmd->data[14] = (focus_value >> 8) & 0xff;
cmd->data[15] = focus_value & 0xff;
}
void fpga2mcu_set_iris_field(struct FPGA2MCUCommand* cmd, u8 iris_mode)
{
cmd->data[16] = iris_mode;
}
void fpga2mcu_cmd_calc_crc(struct FPGA2MCUCommand* cmd)
{
cmd->data[17] = calc_xor_crc(&cmd->data[2], 15);
}
++++++++++++++++++++++++++++++++++++++++++++++++++
这几个结构体中,使用了共享变量,用于各个函数之间的通信和同步。
这里使用的是标志变量,flag。
这里面,使用了两个flag,
fresh,action。
在parse函数中,会检查fresh的状态,决定是否执行后续代码,如果可以执行后续代码,则清除fresh的状态。这是一个原始的PV通信方式。
parse函数对fresh执行P操作,同步在fresh的状态上。
在parse函数中,会设置action的状态,从而使得其他的函数能够检查action的状态,然后决定是否执行。
parse函数对action执行V操作,是同步在action的状态上的函数,能够得以执行。
void mcuiris_cmd_parse(struct MCUIrisCommand* mcuiris_cmd)
{
int ret;
if (!mcuiris_cmd->fresh)
return;
mcuiris_cmd->fresh = 0;
...
mcuiris_cmd->action = 0;
...
mcuiris_cmd->action = 1;
...
}
在action函数中,会检查action的状态,决定是否执行后续代码,如果可以执行后续代码,则清除action的状态。这是一个原始的PV通信方式。
action函数对action执行P操作,同步在fresh的状态上。
void MotorMcu::mcuiris_cmd_action(struct MCUIrisCommand* mcuiris_cmd)
{
if (!mcuiris_cmd->action)
return;
mcuiris_cmd->action = 0;
...
}
至此,我们看到,对于action标志,有函数执行V操作,作为上游同步者,也有函数执行P操作,作为下游同步者。
但是,对于fresh表示,有函数执行P操作,作为下游同步者,那么哪个函数执行V操作,作为上游同步者?
答案是demux。
void MotorMcu::demux(struct RingBuffer* ring, struct MCU2FPGACommand* mcu_cmd)
{
...
while (ring_buffer_valid_len(ring) >= 10)
{
if ((pdata[0] == 0x66) && (pdata[1] == 0xAA))
{
if (pdata[5] == 0x02)
{
if (ring_buffer_valid_len(ring) >= 13)
{
...
mcu80ms_cmd->fresh = 1;
break;
}
}
else if (pdata[5] == 0x03)
{
...
mcutest_cmd->fresh = 1;
break;
}
else if (pdata[5] == 0x04)
{
...
mcuiris_cmd->fresh = 1;
break;
}
...
}
...
}
}
可以看到,demux中会设置fresh的值为1。