引言
电动轨道车控制系统一般分为多个子系统,子系统受一个主控器控制。这样的结构设计繁琐,编程复杂。采用μC/OSIII操作系统、STM32F103RC微控制器、12864液晶屏、PVC按键、无线串口模块、锂电池等实现具有人机界面、无线串口功能的总线主控器。该控制器具有人机界面、轨道车主控器、遥控接收器、遥控面板等多种功能,并且能在不同项目中使用。μC/OSIII从官网上下载,文件名为Micrium_uCEvalSTM32F107_uCOSIII,版本为 V1.29.01.00。
1 硬件结构
从系统接口看,该控制器具有显示、按键、无线收发、串口、485总线等接口,作遥控面板时用电池供电,内部具有电池和充电电路。硬件框图如图1所示。

图1 多功能控制器硬件框图
图1中无线串口用模块实现系统遥控功能,显示器用并口连接。键盘部分有或门电路,可实现按键中断。电源电路中的外部电压、电池电压可被控制器测量,当测量得到无外部电压输入时,控制器自动切换到遥控面板模式。485总线接口用于和其他子系统通信。
2 系统移植
STM32F1XX固件库最新版本为V3.5.0。μC/OSIII支持的库文件版本为V2.1.0。为了μC/OSIII能够在最新的固件库上运行,首先用新固件库替换原有固件库代码并改变入口头文件名,然后有两处代码需要作修改。一是芯片时钟初始化函数,函数名为“BSP_Init”,修改为:
voidBSP_Init (void){
BSP_IntInit();
SystemInit ();//用本语句代替原有的芯片时钟初始化语句
……//此处和原函数相同,在此省略
}
二是操作系统时钟初始化函数,函数名为“OS_CPU_SysTickInit”,修改为:
voidOS_CPU_SysTickInit (CPU_INT32Ucnts){
SysTick_Config(cnts);
}
这两处都是因为原有固件库需要多条语句才能实现时钟初始化,新版本提供专门的初始化函数。
3 驱动编写
3.1 键盘驱动的编写
μC/OSIII具有任务信号量的功能,该信号量随着任务的创建而创建。键盘驱动有键盘扫描任务实现,按键触发外部中断,外部中断发送任务信号量到键盘扫描任务,键盘扫描任务扫描键盘并把键值保持在全局变量中。代码如下:
staticvoidAppTask_keyboard (void *p_arg){
OS_ERRerr;
OS_SEM_CTRctr;
CPU_TSts;
unsigned char key1,key;
(void) &p_arg;
while(DEF_ON){
APP_TRACE_INFO(("%s\\r\\n",__FUNCTION__));
ctr=OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,&ts,&err);//等待按键中断发送的信号
ctr=ctr;
OSTimeDlyHMSM(0, 0, 0, 10, OS_OPT_TIME_HMSM_STRICT,&err);//系统延时,用于按键去抖动
key=GET_keyboard_value();//读取键值
do{
OSTimeDlyHMSM(0, 0, 0, 10,
OS_OPT_TIME_HMSM_STRICT,
&err);
key1 = GET_keyboard_value();
}while(key1 != 0xFF); //等待按键释放
put_RVCIDT_value8(&RVCIDT[15],(~key)); //把键值保存到全局变量中
}
}
3.2 液晶显示器printf函数
液晶显示器一般是输入一个字符的编码和位置然后显示出来,不能实现字符串格式化显示。实现printf,首先使用格式化函数“vsnprintf”得到格式化后的字符串,然后该字符串被打印到屏幕上。具体函数如下:
voidLCD12864_Printf (CPU_CHAR*format, …){
ucharbuf_str[BSP_SER_PRINTF_STR_BUF_SIZE + 1u]; //获取内存
va_listv_args;
va_start(v_args, format);
(void)vsnprintf((char*)&buf_str[0],(size_t) sizeof(buf_str),(char const *) format,v_args);//调用格式化函数
va_end(v_args);
LCD12864_print_string(buf_str);//把格式化后的字符串打印到屏幕上
}
程序中va_list是在C语言中解决变参问题的一组宏, “va_start(v_args, format);”得到第一个可变参数地址,这样把自定义的变参传递到库函数的变参中。
函数“LCD12864_print_string(buf_str);”为自定义函数,因为CH12864液晶屏内的逻辑行和显示行不同。逻辑行只有两行,起始地址分别为0x80和0x90,为显示行的一三行和二四行。在打印字符串时,每打印一显示行就要重新切换位置,保证显示是连续的。
该显示器显示中文字符的起始位置只能是偶数地址,奇数地址显示中文字符会显示乱码。
3.3 串口接收驱动
在μC/OSIII代码中有串口接收部分的驱动,但经过测试,当无操作系统的模块连续发送时,该驱动接收出现漏接或乱码的现象。其原因为该驱动每接收一个字符都要进行一次系统调用,在这个调用过程中发送的字符无法接收。改变原驱动用信号量同步的办法,使用消息通信和结束字符判定的方法即可完整接收到无操作系统模块连续发送来的字符串。
需要修改两个函数,一是中断接收函数:
voidBSP_Ser_ISR_Handler (void){
…… //和源代码相同,没有修改
BSP_OS_SemPost(&BSP_SerRxWait); //发送信号量
BSP_SerRxINTStr[i] =BSP_SerRxData; //把接收到的字符保存到数组
i++;
if((BSP_SerRxINTStr[i-2]=='\\r')||(BSP_SerRxINTStr[i-1]=='\\n')||(i>=99)){//用if结束字符判定,并且进行数组边界检测
BSP_SerRxINTStr[i] = \\0; //用\\0代替结束字符
BSP_OS_QPost (&BSP_SerRX_Q,BSP_SerRxINTStr,i);//发送消息
i=0; //从头开始重新接收
}
…… //和源代码相同,没有修改
}
该中断函数把每个字符先保存在数组中,当出现结束字符时发送消息到接收函数。
二是串口接收函数:
void*BSP_Ser_RdStr_no_echo (BSP_OS_Q_SIZE*p_len){
char *p_str;
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开串口接收中断
p_str=BSP_OS_QWait(&BSP_SerRX_Q,p_len,0);//挂起等待接收完成
USART_ITConfig(UART5, USART_IT_RXNE, DISABLE);//关闭接收中断
return p_str;//返回接收到的字符串
}
该函数每次接收完成后关中断,未完成可以挂起并由中断函数进行连续接收,保证了接收速度。
4 软件结构
多功能控制器可能作为手控器、遥控接收器、遥控器使用。根据外部电源状态和按键信号改变程序执行的模式来实现一机多用。
作为手控器时,每个用户界面作为一个任务。每个界面任务需要建立一些子任务协助处理,这些子任务受任务信号量和任务消息控制,当获得信号量或者消息时任务执行,执行完成后挂起。子任务根据用户操作把相关指令发往系统其他单元,各个单元执行多功能控制器的命令并返回用户需要查看的参数。系统单元一般工作在状态机的模式,根据多功能控制器的命令进入不同的运行状态。轨道运输车的电机控制单元就可工作在前进、倒车、停车三种状态,这三种状态可加上速度参数。电机控制器收到状态和参数后自动执行并返回电量功率等参数给用户界面。
作为遥控接收器时,多功能控制器作为一个数据中继器使用,主要任务是首先从无线串口接收数据,然后核对该数据的确认码。核对通过则通过485总线发给系统其他单元,否则丢弃该数据。当系统单元返回数据时,多功能控制器在数据上添加确认码,然后从无线串口对外发送。
作为遥控器时,该模式和手控器工作过程大致一样。不同的是通过无线串口通信,发射时添加确认码,接收时核对确认码。
结语
多功能控制器通信接口多样,具有人机界面,配有μC/OSIII操作系统,有完善的硬件驱动,可在分布式控制系统中完成多种任务。