别人写的你参考一下:半年前接触STM32,刚开始MCU用的32F1,库用的标准外设库35,写过一些简单的东西。再后来发现ST还有一个软件叫做STM32CUBEMX,可以自动的生成初始化程序,对于我这个32新手来说无疑是天降福音!终于不用为繁琐的配置而苦恼了(其实就是自己对各项配置不熟,而且没有自己积累的程序可以CtrlC+CtrlV)。虽然CUBE用的是ST新出的HAL库,与以前的标准外设库完全不兼容,甚至基本的I/O *** 作都变了,会让习惯了标准外设库的人很苦恼。但是我对标准外设库也不是很熟,而且CUBE的界面化设计真的让配置工程变得很方便,再加上它还有一个类似于FPGA的引脚分配界面,让资源分配,PCB布局布线也方便了不少,于是我选择了用CUBE,用HAL库。很早就开始的写32的朋友有不少,他们也试过HAL库,可最后无一例外都选择了继续使用标准外设库。他们表示完全不习惯HAL库,另外HAL库不太好,毕竟是自动生成的配置,没有自己手动配置的来得熟悉来得透彻,谁知道软件是怎么给你配置的工程。另外CUBE就是给那些不会写32的人用的(ST的官方的说法似乎也是HAL是为了方便做嵌入式相关且对底层不熟的人设计的,但想不通他为何要把两个库做得不兼容)。前面一直在画PCB,调PCB,做机械之类的,没有写程序。最近又开始写32,现在用的MCU是32F4,库是HAL/F4库160。可是我发现我连GPIO的上拉输出都实现不了,无论如何I/O始终默认输出低电平( *** 作I/O可以实现电平跳变),这个问题我昨天查了一天,从库到最底层的寄存器都看了,可没发现什么问题。周围用HAL库的就我一个。。。有些无奈了,难道HAL库真有什么问题吗?如果真有这么明显的问题,ST官方肯定早就发现了。已经下好了标准外设库,打算换标准外设库,工程从头到尾都自己配置,这样出了问题也更方便找。可是我始终有一点想不明白,既然ST官方在推HAL库,那肯定也有他的道理,我们也应该勇于接受新事物,为何身边的朋友却都不愿意接纳HAL库。
连接并口设备,打开并口设备和应用程序,进行并口设置。
1、连接并口设备:将并口设备与计算机的并口接口连接好,并确保连接稳定。
2、打开并口设备和应用程序:在计算机中打开需要使用并口设备的应用程序,例如打印机程序,并打开并口设备本身。
3、进行并口设置:在应用程序中进行相关的并口设置,例如设置打印机的打印纸张大小、颜色等。
在一些工业测控领域会用到串口与上位机进行通信,以获取实时的数据和控制信息。那么上位机和串口是如何进行通信的呢?本文是基于z-stack-143-121的Utilities/SerialApp的例子。
OSAL_SerialAppc是应用于 *** 作系统的接口,先不用看,首先看应用程序SerialAppc里的外部函数:extern void SerialApp_Init( byte task_id );
extern UINT16 SerialApp_ProcessEvent( byte task_id, UINT16 events );系统主函数就是调用这两个函数来实现串口功能的。按照TI的编程风格,后面带init(initial)的一定是初始化函数,完成应用的一些变量的初始化和一些功能的配置。后面带ProcessEvent则是进程的扫描函数。
串口应用执行的流程:串口接收到数据包或者其他应用程序有串口发送请求 对串口的task对应的event置位-> SerialApp_ProcessEvent( uint8 task_id, UINT16 events )轮训事件 ->SerialApp_ProcessMSGCmd,SerialApp_ProcessZDOMsgs,SerialApp_HandleKey, 这三个函数完成相应函数功能具体调用。
void SerialApp_Init( uint8 task_id )
{
halUARTCfg_t uartConfig;
SerialApp_MsgID = 0x00;
SerialApp_SeqRx = 0xC3;
SerialApp_TaskID = task_id;
SerialApp_DstAddrendPoint = 0;//
SerialApp_DstAddraddrshortAddr = 0;//
SerialApp_DstAddraddrMode = (afAddrMode_t)AddrNotPresent;//
SerialApp_RspDstAddrendPoint = 0;//
SerialApp_RspDstAddraddrshortAddr = 0;//
SerialApp_RspDstAddraddrMode = (afAddrMode_t)AddrNotPresent;//
afRegister( (endPointDesc_t )&SerialApp_epDesc );//端口初始化,注册端口
RegisterForKeys( task_id );//注册按键时间
uartConfigconfigured = TRUE; // 2430 don't care
uartConfigbaudRate = SERIAL_APP_BAUD;//设置串口通信的波特率
uartConfigflowControl = TRUE;
uartConfigflowControlThreshold = SERIAL_APP_THRESH;
uartConfigrxmaxBufSize = SERIAL_APP_RX_MAX;//最大接收字节
uartConfigtxmaxBufSize = SERIAL_APP_TX_MAX;//最大发送字节
uartConfigidleTimeout = SERIAL_APP_IDLE; // 2430 don't care
uartConfigintEnable = TRUE; // 2430 don't care
#if SERIAL_APP_LOOPBACK
uartConfigcallBackFunc = rxCB_Loopback;
#else
uartConfigcallBackFunc = rxCB;
#endif
HalUARTOpen (SERIAL_APP_PORT, &uartConfig);//打开串口
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "SerialApp2", HAL_LCD_LINE_2 );
#endif
ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
}
串口任务的初始化函数,完成功能:初始化TaskID,注册endpoint,对串口进行设置,打开串口。注册设备对象消息。
在来看看UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )串口进程轮询函数。
{
if ( events & SYS_EVENT_MSG )
{
afIncomingMSGPacket_t MSGpkt;
while ( (MSGpkt = (afIncomingMSGPacket_t )osal_msg_receive(
SerialApp_TaskID )) )
{
switch ( MSGpkt->hdrevent )
{
case ZDO_CB_MSG://zdo层接收到消息
SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t )MSGpkt );
break;
case KEY_CHANGE:
SerialApp_HandleKeys( ((keyChange_t )MSGpkt)->state,
((keyChange_t )MSGpkt)->keys );
break;
case AF_INCOMING_MSG_CMD//接收到命令,然后执行 这里就要说说了,zigbee协议信息的传递有两种方式:消息和命令,消息长度不限,命令的大小则严格规定
SerialApp_ProcessMSGCmd( MSGpkt );://执行进来消息命令的回调函数
break;
default:
break;
}
osal_msg_deallocate( (uint8 )MSGpkt ); // Release the memory
}
// Return unprocessed events
return ( events ^ SYS_EVENT_MSG );
}//系统消息事件
if ( events & SERIALAPP_MSG_SEND_EVT )
{
SerialApp_SendData( otaBuf, otaLen );
return ( events ^ SERIALAPP_MSG_SEND_EVT );
}//串口发送请求,这里是指串口通过CC2430发送到灵位一个无线设备
if ( events & SERIALAPP_MSG_RTRY_EVT )
{
if ( --rtryCnt )
{
AF_DataRequest( &SerialApp_DstAddr,
(endPointDesc_t )&SerialApp_epDesc,
SERIALAPP_CLUSTERID1, otaLen, otaBuf,
&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT,
SERIALAPP_MSG_RTRY_TIMEOUT );
}
else
{
FREE_OTABUF();
}
return ( events ^ SERIALAPP_MSG_RTRY_EVT );
}//重发事件,这里搞了很长时间才搞明白是什么意思,如果callback返回没有发送成功的话,cc2430就会重发信息,rtryCnt是重发的次数。
if ( events & SERIALAPP_RSP_RTRY_EVT )
{
afStatus_t stat = AF_DataRequest( &SerialApp_RspDstAddr,
(endPointDesc_t )&SerialApp_epDesc,
SERIALAPP_CLUSTERID2,
SERIAL_APP_RSP_CNT, rspBuf,
&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
if ( stat != afStatus_SUCCESS )
{
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT,
SERIALAPP_RSP_RTRY_TIMEOUT );
}
return ( events ^ SERIALAPP_RSP_RTRY_EVT );
}//回应重发事件,说实在的这个协议栈搞这么多冬冬挺烦人的,这也是为了防止阻塞丢包嘛!~
#if SERIAL_APP_LOOPBACK
if ( events & SERIALAPP_TX_RTRY_EVT )
{
if ( rxLen )
{
if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
{
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
SERIALAPP_TX_RTRY_TIMEOUT );
}
else
{
rxLen = 0;
}
}
return ( events ^ SERIALAPP_TX_RTRY_EVT );
}
#endif
return ( 0 ); // Discard unknown events
}
由上面的程序可以看出,在串口Task中定义了五个事件:SYS_EVENT_MSG,SERIALAPP_MSG_SEND_EVT,SERIALAPP_MSG_RTRY_EVT,SERIALAPP_RSP_RTRY_EVT,SERIALAPP_TX_RTRY_EVT,其中,SYS_EVENT_MSG是系统时间,每个任务中都有,它是完成系统任务之间信息的交换。
SERIALAPP_MSG_SEND_EVT //数据发送
SERIALAPP_MSG_RTRY_EVT //数据重发
SERIALAPP_RSP_RTRY_EVT //回应重发
框架程序读懂了,接下来就是实现我的串口功能了。我要实现的串口功能很简单,协调器,也就是采集器采集到的数据通过串口传给上位机,上位机的指令通过串口传给采集器,然后采集器再发给目标终端。
现在要把串口任务加到我们的工程里面去了。
怎么加进去呢?首先,把SerialApp。c和SerialApp。h加到工程的souce文件夹里面,然后
分三步:第一,把初始化函数SerialApp_Init(),加入到osalInitTasks( void )里面, *** 作系统的初始化进程
osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 )osal_mem_alloc( sizeof( uint16 ) tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) tasksCnt));
macTaskInit( taskID++ );
nwk_init( taskID++ );
Hal_Init( taskID++ );
#if defined( MT_TASK )
MT_TaskInit( taskID++ );
#endif
APS_Init( taskID++ );
ZDApp_Init( taskID++ );
SAPI_Init( taskID++);
SerialApp_Init(taskID);
}在这里 *** 作系统会分配任务ID
第二步,把串口轮询函数SerialApp_ProcessEvent()的函数名,加入任务数组
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
ZDApp_event_loop,
SAPI_ProcessEvent,
SerialApp_ProcessEvent
};
第三步:修改执行程序
在修改执行程序时,首先我们要定义好自己的事件,和相应数据、命令、数据结构。再定义相应的功能实现函数。
先定义事件1、coordinater接收到传感器数据事件,要上传给上位机
事件二2、coordinator接收到上位机发送来的命令
U盘在出现问题前是因为前期的 *** 作导致的,有可能是数据丢失了,不能正常使用,只要物理没有被损坏,桌面右下角还能看到USB设备符号,找不到盘符等问题。我倒有一个法可以解决。就是给U盘重新设置驱动,(注意:这样的 *** 作只能使U盘能够正常使用,之前所放的文件数据是无法找回的)可以尝试用磁盘驱动做为U盘驱动,这样就能正常看到U盘盘符,但是还是不够使用的,现在的U盘显示应该容量为0,不能储存和使用,然后找一个和主控型号一样的量产工具进行再次量产,量产成功就OK,一切正常能使用,量产失败,那就再找量产工具吧,和主控型号差不远的量产工具也是可以的,只要能量产成功就可以了。因为之前我有个U盘也是被我非法 *** 作把U盘数据丢失了,拿给专业的维修点进行维修连他们都说没有其他法维修,我就自己尝试着修复“不能维修的U盘”结果为了修复这个U盘,电脑系统被重装过好多次,因为给U盘设置驱动需要一个一个的试,导致了电脑蓝屏,死机等问题。试过之后只有磁盘驱动符合U盘做驱动,我狂汗~!然后我就按照以上的方法把我的哪个U盘修复了,用到现在一点问题都没有,这是我自己亲身体验过来的实际 *** 作现在拿来和大家分享,希望对大家有帮助。
微软认识到了这一点并尝试做一个很小的底层,以隐藏不同机器间的差异,这一层被称为硬件抽象层HAL。
HAL的作用是将 *** 作系统的其余部分表示为抽象的硬件设备,特别是去除了真正硬件所富含的瑕疵和特质。这些设备表现为 *** 作系统的其它部分和设备可以使用的独立于机器的服务的形式(函数调用和宏)。通过使用HAL服务和间接硬件寻址,当移植到新的硬件上时,驱动程序和核心只需做很少的改动。移植HAL本身是直接的,因为所有的机器相关代码都集中在一个地方,并且移植的目标是充分定义的,即实现所有的HAL服务。
选择HAL中的服务是和主板上的芯片相关的,因为这些芯片从一个机器到另一个机器的变化是具有可预见限度的。换句话说,设计它是为了隐藏不同厂商主板之间的差别,而不是X86和Alpha之间的差别。HAL服务包括对设备寄存器的访问、总线独立的设备寻址、中断处理和复位、DMA传输、定时器和实时时钟的控制、底层的自旋锁(Spin Lock)和多处理机同步、BIOS接口以及CMOS配置内存。HAL没有提供对特殊I/O设备(如键盘、鼠标、硬盘和内存管理单元)的抽象或服务。
举一个例子来说明硬件抽象层的功能。考虑内存映射I/O和I/O端口的对比。一些机器具有前者,一些机器具有后者。驱动程序该怎样编写?是否使用内存映射呢?强制选择会使驱动程序无法移植到另一种实现方式的机器上,为此,硬件抽象层专为驱动程序的编写者提供了三个读设备寄存器的函数和另外三个写寄存器的函数:
uc=READ_PORT_UCHAR(port); WRITE_PORT_UCHAR(port, uc)
us=READ_PORT_USHORT(port); WRITE_PORT_USHORT(port, us)
ul=READ_PORT_ULONG(port); WRITE_PORT_LONG(port, ul)
这些函数分别读写无符号8位、16位、32位的证书到特定的端口。由HAL决定是否需要内存映射I/O,这样,一个驱动程序可以不被修改而在具有不同设备寄存器实现的机器间移植。
驱动程序常由于各种原因而访问特定的I/O设备。在这个硬件层上,一个设备的某个总线上会有一个或多个地址。由于现代计算机常有多种总线(PCI、PCI-E、SCSI、USB等),很可能两个或更多设备具有相同的总线地址,因此需要通过某种方式来区分它们。HAL提供了一个服务,该服务通过将总线相连的设备地址映射到系统范围内的逻辑地址来识别设备。这样,驱动程序就不需要知道哪条总线上有哪个设备了。这些逻辑地址与 *** 作系统为用户程序提供的指向文件和其他系统资源的句柄是类似的。这种机制也使总线结构的属性和寻址方式对于高层不可见。
中断也存在类似的问题——它们也是总线相关的。同样,在这里,HAL为系统范围内的中断提供命名服务,并允许驱动程序以可移植的方法将中断服务例程和中断联系起来而不用知道哪个中断向量对应于哪条总线。此外,中断请求级别管理也在HAL处理。
HAL提供的另一项服务是以一种设备独立的方式设置并管理DMA传输。系统范围内的DMA引擎与特定I/O卡上的DMA引擎都可以 *** 作。对设备的访问是通过其逻辑地址进行的。HAL还实现了软件的分散、聚集(scatter/gather)(对非连续的物理存储块进行写或读)。
此外,HAL还以一种可移植的方式管理时钟与定时器。时间记录以100ns为单位(起始于1601年1月1日),这样就比以2s为单位(起始于1980年1月1日)的MS-DOS事件记录精确得多,HAL还为许多发生于171819世纪的计算机相关事件的记录提供了致贺词。这种时间服务将驱动程序从始终运行的实际频率中分离出来。
内核组件(Kernel Component)有时需要在非常低的层次上同步,特别是为了避免多处理机系统中的竞争状态。HAL提供了一些原子方法来管理这种同步,如自旋锁——一个CPU仅仅等待一个由其他CPU占用的系统资源被释放,尤其是在资源只被几条机器指令所占用的情况下。
最后,当系统启动以后,HAL与BIOS进行对话,并检查CMOS配置内存(如果有的话),以查明该系统包含了哪些总线和I/O设备,以及他们是如何配置的。之后这个信息会被存入注册表,这样,其他系统组件就能够查询它,而不必了解BIOS或配置内存如何工作。
由于HAL高度依赖于机器,它必须与其所装入的系统完全匹配,因此,Windows的安装光盘上提供了许多种版本的HAL。系统安装时,选择一种合适的HAL并以haldll为名复制到硬盘上的系统目录windows\system32或winnt\system32下。之后所有的启动都使用该版本的HAL,删除这个文件将导致系统无法启动。
以上就是关于STM32HAL库写CAN通信程序最近遇到了难题,有谁有具体例子不全部的内容,包括:STM32HAL库写CAN通信程序最近遇到了难题,有谁有具体例子不、hal如何对并口 *** 作、如何建立cc2430串口与上位机通信的任务等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)