正点原子

个性签名:我的淘宝:http://shop62103354.taobao.com

    1. 【原创】 ALIENTEK推出4.3'电容触摸屏模块,5点触控,800*480,单片机直驱 0/1616 【stm32/stm8】 2014-06-23
    2. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十一... 1/4020 【stm32/stm8】 2013-05-17
    3. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十章... 1/2422 【stm32/stm8】 2013-05-03
    4. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十九... 0/1476 【stm32/stm8】 2013-05-01
    5. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十八... 2/2984 【stm32/stm8】 2013-04-29
    6. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七... 4/4939 【stm32/stm8】 2013-04-26
    7. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十六... 1/2617 【stm32/stm8】 2013-04-24
    8. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十五... 4/2653 【stm32/stm8】 2013-04-22
    9. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十四... 0/2116 【stm32/stm8】 2013-04-20
    10. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十三... 0/2367 【stm32/stm8】 2013-04-19
    11. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十二... 1/1914 【stm32/stm8】 2013-04-16
    12. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十一... 3/2241 【stm32/stm8】 2013-04-14
    13. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十章... 0/2314 【stm32/stm8】 2013-04-13
    14. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十九... 1/3436 【stm32/stm8】 2013-04-11
    15. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十八... 7/3545 【stm32/stm8】 2013-04-10
    16. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十七... 0/2443 【stm32/stm8】 2013-04-08
    17. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十六... 4/3429 【stm32/stm8】 2013-04-06
    18. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十五... 0/2798 【stm32/stm8】 2013-04-05
    19. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十四... 3/3061 【stm32/stm8】 2013-04-04
    20. 【原创】 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十三... 0/1468 【stm32/stm8】 2013-04-03
    1. stm32+dm9000网络专题 141/13300 【stm32/stm8】 2015-02-06
      学习下。谢谢分享
    2. hanskying666 发表于 2014-11-8 23:20 很不错,F4强大的处理能力!
      是的,软件视频。
    3. dcexpert 发表于 2014-11-8 22:51 内容很丰富。
      恩,搞了差不多一年了,呵呵。
    4. 另外,内存管理,不一定要外部扩展ram,单独的stm32自己内存都够用了。
    5. 我们的图片编解码都是用的内存管理实现,还有fatfs也是。 你不用的话,这些地方都得修改一下。
    6. 死机了。
    7. int main(void){                 Stm32_Clock_Init(9);    //系统时钟设置       uart_init(72,9600);      //串口初始化为9600       delay_init(72);                  //延时初始化       LED_Init();                 //初始化与LED连接的硬件接口       LCD_Init();                  //初始化LCD       usmart_dev.init(72);      //初始化USMART       BEEP_Init();                //蜂鸣器初始化           KEY_Init();                  //按键初始化       TPAD_Init(72);             //初始化TPAD      FSMC_SRAM_Init();    //初始化外部SRAM       mem_init(SRAMIN);     //初始化内部内存池       mem_init(SRAMEX);    //初始化外部内存池     tp_dev.init();       ucos_load_main_ui();       OSInit();                           //初始化UCOSII      OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务       OSStart();         }                           //开始任务void start_task(void *pdata){    OS_CPU_SR cpu_sr=0; u8 err;                                  pdata = pdata;             msg_key=OSMboxCreate((void*)0);    //创建消息邮箱       q_msg=OSQCreate(&MsgGrp[0],256); //创建消息队列      flags_key=OSFlagCreate(0,&err);       //创建信号量集                    OSStatInit();                                      //初始化统计任务.这里会延时1秒钟左右      OS_ENTER_CRITICAL();                  //进入临界区(无法被中断打断)         OSTaskCreate(led_task,(void *)0,(OS_STK*)&LED_TASK_STK[LED_STK_SIZE-1],LED_TASK_PRIO);                                                 OSTaskCreate(touch_task,(void *)0,(OS_STK*)&TOUCH_TASK_STK[TOUCH_STK_SIZE-1],TOUCH_TASK_PRIO);                                          OSTaskCreate(qmsgshow_task,(void *)0,(OS_STK*)&QMSGSHOW_TASK_STK[QMSGSHOW_STK_SIZE-1],QMSGSHOW_TASK_PRIO);                                       OSTaskCreate(main_task,(void *)0,(OS_STK*)&MAIN_TASK_STK[MAIN_STK_SIZE-1],MAIN_TASK_PRIO);                                        OSTaskCreate(flags_task,(void *)0,(OS_STK*)&FLAGS_TASK_STK[FLAGS_STK_SIZE-1],FLAGS_TASK_PRIO);                                     OSTaskCreate(key_task,(void *)0,(OS_STK*)&KEY_TASK_STK[KEY_STK_SIZE-1],KEY_TASK_PRIO);                                       OSTaskSuspend(START_TASK_PRIO);       //挂起起始任务.       OS_EXIT_CRITICAL();                            //退出临界区(可以被中断打断)}//LED任务void led_task(void *pdata){       u8 t;       while(1)       {              t++; delay_ms(10);              if(t==8)LED0=1;                 //LED0灭              if(t==100) {t=0; LED0=0; }  //LED0亮                   }                                                            }//触摸屏任务void touch_task(void *pdata){                 while(1)       {              tp_dev.scan(0);                            if(tp_dev.sta&TP_PRES_DOWN)        //触摸屏被按下              {                        if(tp_dev.x<120&&tp_dev.y<lcddev.height&&tp_dev.y>220)                     {                                                                                                                       TP_Draw_Big_Point(tp_dev.x,tp_dev.y,BLUE);  //画图                                delay_ms(2);                                        }              }else delay_ms(10);       //没有按键按下的时候       }}    //队列消息显示任务void qmsgshow_task(void *pdata){       u8 *p; u8 err;        while(1)       {              p=OSQPend(q_msg,0,&err);//请求消息队列              LCD_ShowString(5,170,240,16,16,p);//显示消息             myfree(SRAMIN,p); delay_ms(500);             }                                                            }//主任务void main_task(void *pdata){                                                     u32 key=0; u8 err;       u8 tmr2sta=1; //软件定时器2开关状态        u8 tmr3sta=0; //软件定时器3开关状态       u8 flagsclrt=0;       //信号量集显示清零倒计时         tmr1=OSTmrCreate(10,10,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr1_callback,0,"tmr1",&err);            //100ms执行一次       tmr2=OSTmrCreate(10,20,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr2_callback,0,"tmr2",&err);            //200ms执行一次       tmr3=OSTmrCreate(10,10,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr3_callback,0,"tmr3",&err);            //100ms执行一次       OSTmrStart(tmr1,&err);                                 //启动软件定时器1       OSTmrStart(tmr2,&err);                                 //启动软件定时器2      while(1)       {              key=(u32)OSMboxPend(msg_key,10,&err);              if(key)              {                     flagsclrt=51;//500ms后清除                     OSFlagPost(flags_key,1<<(key-1),OS_FLAG_SET,&err);//设置信号量为1              }              if(flagsclrt)//倒计时              {                     flagsclrt--;                     if(flagsclrt==1)LCD_Fill(140,162,239,162+16,WHITE);//清除显示              }              switch(key)              {                     case 1: LED1=!LED1; break;//控制DS1                     case 2://控制软件定时器3                               tmr3sta=!tmr3sta;                            if(tmr3sta)OSTmrStart(tmr3,&err);                             else OSTmrStop(tmr3,OS_TMR_OPT_NONE,0,&err);//关闭软件定时器3                           break;                     case 3: LCD_Fill(0,221,119,lcddev.height,WHITE); break;//清除                     case 4://校准                            OSTaskSuspend(TOUCH_TASK_PRIO);            //挂起触摸屏任务                                    OSTaskSuspend(QMSGSHOW_TASK_PRIO);    //挂起队列信息显示任务                           OSTmrStop(tmr1,OS_TMR_OPT_NONE,0,&err);     //关闭软件定时器1                            if(tmr2sta)OSTmrStop(tmr2,OS_TMR_OPT_NONE,0,&err);//关闭定时器2                               TP_Adjust();                                 OSTmrStart(tmr1,&err);                                   //重新开启软件定时器1                            if(tmr2sta)OSTmrStart(tmr2,&err);             //重新开启软件定时器2                           OSTaskResume(TOUCH_TASK_PRIO);      //解挂                           OSTaskResume(QMSGSHOW_TASK_PRIO);//解挂                            ucos_load_main_ui();                                //重新加载主界面                                    break;                     case 5://软件定时器2 开关                            tmr2sta=!tmr2sta;                            if(tmr2sta)OSTmrStart(tmr2,&err);                          //开启软件定时器2                            else                            {                                                                    OSTmrStop(tmr2,OS_TMR_OPT_NONE,0,&err);//关闭软件定时器2                                  LCD_ShowString(148,262,240,16,16,"TMR2 STOP");                            }                            break;                  }               delay_ms(10);       }}              //信号量集处理任务void flags_task(void *pdata){           u16 flags;       u8 err;                                                                  while(1)       {              flags=OSFlagPend(flags_key,0X001F,OS_FLAG_WAIT_SET_ANY,0,&err);//等待信号量             if(flags&0X0001)LCD_ShowString(140,162,240,16,16,"KEY0 DOWN  ");              if(flags&0X0002)LCD_ShowString(140,162,240,16,16,"KEY1 DOWN  ");              if(flags&0X0004)LCD_ShowString(140,162,240,16,16,"KEY2 DOWN  ");              if(flags&0X0008)LCD_ShowString(140,162,240,16,16,"KEY_UP DOWN");              if(flags&0X0010)LCD_ShowString(140,162,240,16,16,"TPAD DOWN  ");              BEEP=1; delay_ms(50); BEEP=0;              OSFlagPost(flags_key,0X001F,OS_FLAG_CLR,&err);//全部信号量清零      }}                     //按键扫描任务void key_task(void *pdata){           u8 key;                                                                 while(1)       {              delay_ms(10); key=KEY_Scan(0);                  if(key==0) if(TPAD_Scan(0))key=5;              if(key)OSMboxPost(msg_key,(void*)key);//发送消息                    }}本章test.c的代码有点多,因为我们创建了7个任务,3个软件定时器及其回调函数,所以,整个代码有点多,我们创建的7个任务为:start_task、led_task、touch_task、qmsgshow_task 、flags_task 、main_task和key_task,优先级分别是10和7~2,堆栈大小除了main_task是128,其他都是64。我们还创建了3个软件定时器tmr1、tmr2和tmr3,tmr1用于显示CPU使用率和内存使用率,每100ms执行一次;tmr2用于在LCD的右下角区域不停的显示各种颜色,每200ms执行一次;tmr3用于定时向队列发送消息,每100ms发送一次。本章,我们依旧使用消息邮箱msg_key在按键任务和主任务之间传递键值数据,我们创建信号量集flags_key,在主任务里面将按键键值通过信号量集传递给信号量集处理任务flags_task,实现按键信息的显示以及发出按键提示音。本章,我们还创建了一个大小为256的消息队列q_msg,通过软件定时器tmr3的回调函数向消息队列发送消息,然后在消息队列显示任务qmsgshow_task里面请求消息队列,并在LCD上面显示得到的消息。消息队列还用到了动态内存管理。在主任务main_task里面,我们实现了60.2节介绍的功能:KEY0控制LED1亮灭;KEY1控制软件定时器tmr3的开关,间接控制队列信息的发送;KEY2清除触摸屏输入;WK_UP用于触摸屏校准,在校准的时候,要先挂起触摸屏任务、队列消息显示任务,并停止软件定时器tmr1和tmr2,否则可能对校准时的LCD显示造成干扰;TPAD按键用于控制软件定时器tmr2的开关,间接控制屏幕显示。软件设计部分就为大家介绍到这里。60.4 下载验证在代码编译成功之后,我们通过下载代码到战舰STM32开发板上,可以看到LCD显示界面如图60.4.1所示: http://www.openedv.com/upload/2013/5/3/a7bc4ef04313653e811d2c3b57c5e0ca_702.png 图60.4.1 初始界面       从图中可以看出,默认状态下,CPU使用率为21%左右。比上一章多出很多,这主要是key_task里面增加了触摸按键TPAD的检测,而TPAD检测是一个比较耗资源(主要是延时不是2ms的倍数)的过程,另外不停的刷屏(tmr2)也需要一定资源。通过按KEY0,可以控制DS1的亮灭;通过按KEY1则可以启动tmr3控制消息队列发送,可以在LCD上面看到Q和MEM的值慢慢变大(说明队列消息在增多,占用内存也随着消息增多而增大),在QUEUE MSG区,开始显示队列消息,再按一次KEY1停止tmr3,此时可以看到Q和MEM逐渐减小。当Q值变为0的时候,QUEUE MSG也停止显示(队列为空)。通过KEY2按键,清除TOUCH区域的输入。通过WK_UP按键,可以进行触摸屏校准。通过TPAD按键,可以启动/停止tmr2,从而控制屏幕的刷新。在TOUCH区域,可以输入手写内容。任何按键按下,蜂鸣器都会发出“滴”的一声,提示按键被按下,同时在FLAGS区域显示按键信息。
    8. 图来了
    9. 这里,我们需要了解uIP是把网页数据(源文件)存放在data_index_html,通过将这里面的数据发送给电脑浏览器,浏览器就会显示出我们所设计的界面了。当用户在网页上面操作的时候,浏览器就会发送消息给WEB服务器,服务器根据收到的消息内容,判断用户所执行的操作,然后发送新的页面到浏览器,这样用户就可以看到操作结果了。本章,我们实现的WEB服界面如图57.3.2所示:http://file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image009.png图57.3.2 WEB服务器界面图中两个按键分别控制DS0和DS1的亮灭,然后还显示了STM32芯片的温度和RTC时间等信息。 控制DS0,DS1亮灭我们是通过发送不同的页面请求来实现的,这里我们采用的是Get方法(科普找百度),将请求参数放到URL里面,然后WEB服务器根据URL的参数来相应内容,这样实际上STM32就是从URL获取控制参数,以控制DS0和DS1的亮灭。uIP在得到Get请求后判断URL内容,然后做出相应控制,最后修改data_index_html里面的部分内容(比如指示灯图标的变化,以及提示文字的变化等),再将data_index_html发送给浏览器,显示新的界面。显示STM32温度和RTC时间是通过刷新实现的,uIP每次得到来自浏览器的请求就会更新data_index_html里面的温度和时间等信息,然后将data_index_html发送给浏览器,这样达到更新温度和时间的目的。但是这样我们需要手动刷新,比较笨,所以我们在网页源码里面加入了自动刷新的控制代码,每10秒钟刷新一次,这样就不需要手动刷新了。handle_input函数实现了我们所说的这一切功能,另外请注意data_index_html是存放在httpd-fsdata.c(该文件通过include的方式包含进工程里面)里面的一个数组,并且由于该数组的内容需要不停的刷新,所以我们定义它为sram数据,data_index_html里面的数据,则是通过一个工具软件:amo的编程小工具集合V1.2.6.exe,将网页源码转换而来,该软件在光盘有提供,如果想自己做网页的朋友,可以通过该软件转换。WEB服务器就为大家介绍这么多。接下来看看TCP服务器appcall函数:tcp_server_demo_appcall,该函数在tcp_server_demo.c里面实现,该函数代码如下:u8 tcp_server_databuf[200];       //发送数据缓存       u8 tcp_server_sta;                        //服务端状态//[7]:0,无连接;1,已经连接;//[6]:0,无数据;1,收到客户端数据//[5]:0,无数据;1,有数据需要发送 //这是一个TCP 服务器应用回调函数。//该函数通过UIP_APPCALL(tcp_demo_appcall)调用,实现Web Server的功能.//当uip事件发生时,UIP_APPCALL函数会被调用,根据所属端口(1200),确定是否执行该函数。//例如 : 当一个TCP连接被创建时、有新的数据到达、数据已经被应答、数据需要重发等事件void tcp_server_demo_appcall(void){      struct tcp_demo_appstate *s = (struct tcp_demo_appstate *)&uip_conn->appstate;       if(uip_aborted())tcp_server_aborted();         //连接终止      if(uip_timedout())tcp_server_timedout();     //连接超时          if(uip_closed())tcp_server_closed();            //连接关闭              if(uip_connected())tcp_server_connected();  //连接成功                if(uip_acked())tcp_server_acked();                     //发送的数据成功送达        //接收到一个新的TCP数据包        if (uip_newdata())//收到客户端发过来的数据       {              if((tcp_server_sta&(1<<6))==0)//还未收到数据              {                     if(uip_len>199) ((u8*)uip_appdata)[199]=0;                     strcpy((char*)tcp_server_databuf,uip_appdata);                                        tcp_server_sta|=1<<6;//表示收到客户端数据              }       }else if(tcp_server_sta&(1<<5))//有数据需要发送       {              s->textptr=tcp_server_databuf;              s->textlen=strlen((const char*)tcp_server_databuf);              tcp_server_sta&=~(1<<5);//清除标记       }          //当需要重发、新数据到达、数据包送达、连接建立时,通知uip发送数据        if(uip_rexmit()||uip_newdata()||uip_acked()||uip_connected()||uip_poll())       {              tcp_server_senddata();       }}该函数通过uip_newdata()判断是否接收到客户端发来的数据,如果是,则将数据拷贝到tcp_server_databuf缓存区,并标记收到客户端数据。当有数据要发送(KEY0按下)的时候,将需要发送的数据通过tcp_server_senddata函数发送出去。最后,我们看看TCP客户端appcall函数:tcp_client_demo_appcall,该函数代码同TCP服务端代码十分相似,该函数在tcp_server_demo.c里面实现,代码如下:u8 tcp_client_databuf[200];        //发送数据缓存       u8 tcp_client_sta;                        //客户端状态//[7]:0,无连接;1,已经连接;//[6]:0,无数据;1,收到客户端数据//[5]:0,无数据;1,有数据需要发送//这是一个TCP 客户端应用回调函数。//该函数通过UIP_APPCALL(tcp_demo_appcall)调用,实现Web Client的功能.//当uip事件发生时,UIP_APPCALL函数会被调用,根据所属端口(1400),确定是否执行该函数。//例如 : 当一个TCP连接被创建时、有新的数据到达、数据已经被应答、数据需要重发等事件void tcp_client_demo_appcall(void){                    struct tcp_demo_appstate *s = (struct tcp_demo_appstate *)&uip_conn->appstate;       if(uip_aborted())tcp_client_aborted();          //连接终止               if(uip_timedout())tcp_client_timedout();      //连接超时          if(uip_closed())tcp_client_closed();             //连接关闭              if(uip_connected())tcp_client_connected();   //连接成功                if(uip_acked())tcp_client_acked();               //发送的数据成功送达       //接收到一个新的TCP数据包        if (uip_newdata())       {              if((tcp_client_sta&(1<<6))==0)//还未收到数据              {                     if(uip_len>199) ((u8*)uip_appdata)[199]=0;                             strcpy((char*)tcp_client_databuf,uip_appdata);                                                                  tcp_client_sta|=1<<6;//表示收到客户端数据              }                                   }else if(tcp_client_sta&(1<<5))//有数据需要发送       {              s->textptr=tcp_client_databuf;              s->textlen=strlen((const char*)tcp_client_databuf);              tcp_client_sta&=~(1<<5);//清除标记       }         //当需要重发、新数据到达、数据包送达、连接建立时,通知uip发送数据        if(uip_rexmit()||uip_newdata()||uip_acked()||uip_connected()||uip_poll())       {              tcp_client_senddata();       }                                                                              }该函数也是通过uip_newdata()判断是否接收到服务端发来的数据,如果是,则将数据拷贝到tcp_client_databuf缓存区,并标记收到服务端数据。当有数据要发送(KEY2按下)的时候,将需要发送的数据通过tcp_client_senddata函数发送出去。uIP通过clock-arch里面的clock_time获取时间节拍,我们通过在timerx.c里面初始化定时器6,用于提供clock_time时钟节拍,每10ms加1,这里代码就不贴出来了,请大家查看光盘源码。最后在test.c里面,我们要实现好几个函数,但是这里仅贴出main函数以及uip_polling函数,该部分如下:#define BUF ((struct uip_eth_hdr *)&uip_buf[0])int main(void){                 u8 key;       u8 tcnt=0;       u8 tcp_server_tsta=0XFF;       u8 tcp_client_tsta=0XFF;      uip_ipaddr_t ipaddr;             Stm32_Clock_Init(9);           //系统时钟设置       uart_init(72,9600);             //串口初始化为9600       delay_init(72);                         //延时初始化        LED_Init();                        //初始化与LED连接的硬件接口       LCD_Init();                       //初始化LCD       KEY_Init();                         //初始化按键       RTC_Init();                          //初始化RTC       Adc_Init();                          //初始化ADC            usmart_dev.init(72);             //初始化USMART              POINT_COLOR=RED;         //设置为红色            LCD_ShowString(60,10,200,16,16,"WarShip STM32");           LCD_ShowString(60,30,200,16,16,"ENC28J60 TEST");          LCD_ShowString(60,50,200,16,16,"ATOM@ALIENTEK");      while(tapdev_init())       //初始化ENC28J60错误       {                                                                       LCD_ShowString(60,70,200,16,16,"ENC28J60 Init Error!"); delay_ms(200);                    LCD_Fill(60,70,240,86,WHITE);//清除之前显示       };                  uip_init();                            //uIP初始化            LCD_ShowString(60,70,200,16,16,"KEY0:Server Send Msg");        LCD_ShowString(60,90,200,16,16,"KEY2:Client Send Msg");           LCD_ShowString(60,110,200,16,16,"IP:192.168.1.16");           LCD_ShowString(60,130,200,16,16,"MASK:255.255.255.0");                     LCD_ShowString(60,150,200,16,16,"GATEWAY:192.168.1.1");                        LCD_ShowString(30,200,200,16,16,"TCP RX:");                                                             LCD_ShowString(30,220,200,16,16,"TCP TX:");                                                                   LCD_ShowString(30,270,200,16,16,"TCP RX:");                                                             LCD_ShowString(30,290,200,16,16,"TCP TX:");                                                             POINT_COLOR=BLUE;                uip_ipaddr(ipaddr, 192,168,1,16); //设置本地设置IP地址       uip_sethostaddr(ipaddr);                                        uip_ipaddr(ipaddr, 192,168,1,1); //设置网关IP地址(其实就是你路由器的IP地址)       uip_setdraddr(ipaddr);                                             uip_ipaddr(ipaddr, 255,255,255,0);//设置网络掩码       uip_setnetmask(ipaddr);       uip_listen(HTONS(1200));                  //监听1200端口,用于TCP Server       uip_listen(HTONS(80));                            //监听80端口,用于Web Server      tcp_client_reconnect();                   //尝试连接到TCP Server端,用于TCP Client       while (1)       {              uip_polling(); //处理uip事件,必须插入到用户程序的循环体中                key=KEY_Scan(0);              if(tcp_server_tsta!=tcp_server_sta)//TCP Server状态改变              {                                                                                                                            if(tcp_server_sta&(1<<7))LCD_ShowString(30,180,200,16,16,"TCP ServerConnected   ");                     else LCD_ShowString(30,180,200,16,16,"TCP Server Disconnected");                    if(tcp_server_sta&(1<<6))     //收到新数据                     {                            LCD_Fill(86,200,240,216,WHITE);     //清除之前显示                            LCD_ShowString(86,200,154,16,16,tcp_server_databuf);                         printf("TCP Server RX:%s\r\n",tcp_server_databuf);//打印数据                            tcp_server_sta&=~(1<<6);           //标记数据已经被处理                       }                     tcp_server_tsta=tcp_server_sta;              }              if(key==KEY_RIGHT)//TCP Server 请求发送数据              {                     if(tcp_server_sta&(1<<7))     //连接还存在                     {                            sprintf((char*)tcp_server_databuf,"TCP Server OK %d\r\n",tcnt);                                 LCD_Fill(86,220,240,236,WHITE);                                 //清除之前显示                            LCD_ShowString(86,220,154,16,16,tcp_server_databuf);   //显示发送数据                            tcp_server_sta|=1<<5;//标记有数据需要发送                            tcnt++;                     }              }              if(tcp_client_tsta!=tcp_client_sta)//TCP Client状态改变              {                                                                                                                            if(tcp_client_sta&(1<<7))LCD_ShowString(30,250,200,16,16,"TCP Client Connected   ");                     else LCD_ShowString(30,250,200,16,16,"TCP Client Disconnected");                    if(tcp_client_sta&(1<<6))     //收到新数据                     {                            LCD_Fill(86,270,240,286,WHITE);     //清除之前显示                            LCD_ShowString(86,270,154,16,16,tcp_client_databuf);                         printf("TCP Client RX:%s\r\n",tcp_client_databuf);//打印数据                            tcp_client_sta&=~(1<<6);            //标记数据已经被处理                       }                     tcp_client_tsta=tcp_client_sta;              }              if(key==KEY_LEFT)//TCP Client 请求发送数据              {                     if(tcp_client_sta&(1<<7))     //连接还存在                     {                            sprintf((char*)tcp_client_databuf,"TCP Client OK %d\r\n",tcnt);                                   LCD_Fill(86,290,240,306,WHITE);            //清除之前显示                            LCD_ShowString(86,290,154,16,16,tcp_client_databuf);//显示发送数据                            tcp_client_sta|=1<<5;//标记有数据需要发送                            tcnt++;                     }              }              delay_ms(1);       }  }//uip事件处理函数//必须将该函数插入用户主循环,循环调用.void uip_polling(void){       u8 i;       static struct timer periodic_timer, arp_timer;       static u8 timer_ok=0;            if(timer_ok==0)//仅初始化一次       {              timer_ok = 1;              timer_set(&periodic_timer,CLOCK_SECOND/2); //创建1个0.5秒的定时器               timer_set(&arp_timer,CLOCK_SECOND*10);    //创建1个10秒的定时器        }                                 uip_len=tapdev_read();  //从网络读取一个IP包,得到数据长度.uip_len在uip.c中定义       if(uip_len>0)                      //有数据       {                 //处理IP数据包(只有校验通过的IP包才会被接收)               if(BUF->type == htons(UIP_ETHTYPE_IP))//是否是IP包?               {                     uip_arp_ipin();       //去除以太网头结构,更新ARP表                     uip_input();         //IP包处理                     //当上面的函数执行后,如果需要发送数据,则全局变量 uip_len > 0                     //需要发送的数据在uip_buf, 长度是uip_len  (这是2个全局变量)                                     if(uip_len>0)//需要回应数据                     {                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求                            tapdev_send();//发送数据到以太网                     }              }else if (BUF->type==htons(UIP_ETHTYPE_ARP))//处理arp报文,是否是ARP包?              {                     uip_arp_arpin();                    //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0                     //需要发送的数据在uip_buf, 长度是uip_len(这是2个全局变量)                    if(uip_len>0)tapdev_send();//需要发送数据,则通过tapdev_send发送                   }       }       else if(timer_expired(&periodic_timer))      //0.5秒定时器超时       {              timer_reset(&periodic_timer);             //复位0.5秒定时器               //轮流处理每个TCP连接, UIP_CONNS缺省是40个                for(i=0;i<UIP_CONNS;i++)              {                     uip_periodic(i);      //处理TCP通信事件                      //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0                     //需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)                    if(uip_len>0)                     {                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求                            tapdev_send();//发送数据到以太网                     }              }#if UIP_UDP  //UIP_UDP               //轮流处理每个UDP连接, UIP_UDP_CONNS缺省是10个              for(i=0;i<UIP_UDP_CONNS;i++)              {                     uip_udp_periodic(i);      //处理UDP通信事件                    //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0                     //需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)                     if(uip_len > 0)                     {                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求                            tapdev_send();//发送数据到以太网                     }              }#endif               //每隔10秒调用1次ARP定时器函数 用于定期ARP处理,ARP表10秒更新一次,//旧的条目会被抛弃              if(timer_expired(&arp_timer))              {                     timer_reset(&arp_timer);                     uip_arp_timer();              }       }}其中main函数相对比较简单,先初始化网卡(ENC28J60)和uIP等,然后设置IP地址(192.168.1.16)及监听端口(1200和80),就开始轮询uip_polling函数,实现uIP事件处理,同时扫描按键,实现数据发送处理。当有收到数据的时候,将其显示在LCD上,同时通过串口发送到电脑。注意,这里main函数调用的tcp_client_reconnect函数,用于本地(STM32)TCP Client去连接外部服务端,该函数设置服务端IP地址为192.168.1.103(就是你电脑的IP地址),连接端口为1400,只要没有连上,该函数就会不停的尝试连接。uip_polling函数,第一次调用的时候创建两个定时器,当收到包的时候(uip_len>0),先区分是IP包还是ARP包,针对不同的包做不同处理,对我们来说主要是通过uip_input处理IP包,实现数据处理。当没有收到包的时候(uip_len=0),通过定时器定时处理各个TCP/UDP连接以及ARP表处理。 软件设计部分就为大家介绍到这里。57.4 下载验证在代码编译成功之后,我们通过下载代码到战舰STM32开发板上(假设网络模块已经连接上开发板),LCD显示如图57.4.1所示界面:http://file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image010.jpg图57.4.1 初始界面可以看到,此时TCP Server和TCP Client都是没有连接的,我们打开:网络调试助手V3.7.exe这个软件(该软件在光盘有提供),然后选择TCP Server,设置本地IP地址为:192.168.1.103(默认就是),设置本地端口为1400,点击连接按钮,就会收到开发板发过来的消息,此时我们按开发板的KEY2,就会发送数据给网络调试助手,同时也可以通过网络调试助手发送数据到STM32开发板。如图57.4.2所示:http://file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image011.jpg图57.4.2 STM32 TCP Client测试    在连接成功建立的时候,会在战舰STM32开发板上面显示TCP Client的连接状态,然后如果收到来自电脑TCP Server端的数据,也会在LCD上面显示,并打印到串口。这是我们实现的TCP Client功能。    如果我们在网络调试助手,选择协议类型为TCP Client,然后设置服务器IP地址为192.168.1.16(就是我们STM32开发板设置的IP地址),然后设置服务器端口为1200,点击连接,同样可以收到开发板发过来的消息,此时我们按开发板的KEY0按键,就可以发送数据到网络调试助手,同时网络调试助手也可以发送数据到我们的开发板。如图57.4.3所示:http://file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image012.jpg图57.4.3 STM32 TCP Server测试在连接成功建立的时候,会在战舰STM32开发板上面显示TCP Server的连接状态,然后如果收到来自电脑TCP Client端的数据,便会在LCD上面显示,并打印到串口。这是我们实现的TCP Server功能。       最后,我们测试WEB服务器功能。打开浏览器,输入http://192.168.1.16  ,就可以看到如下界面,如图57.4.4所示:http://file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image013.jpg图57.4.4 STM32 WEB Server测试       此时,我们点击网页上的DS0状态反转和DS1状态反转按钮,就可以控制DS0和DS1的亮灭了。同时在该界面还显示了STM32的温度和RTC时间,每次刷新的时候,进行数据更新,另外浏览器每10秒钟会自动刷新一次,以更新时间和温度信息。
    10. //得到角度//x,y,z:x,y,z方向的重力加速度分量(不需要单位,直接数值即可)//dir:要获得的角度.0,与Z轴的角度;1,与X轴的角度;2,与Y轴的角度.//返回值:角度值.单位0.1°.short ADXL345_Get_Angle(float x,float y,float z,u8 dir){       float temp,res=0;        switch(dir)       {              case 0://与自然Z轴的角度                    temp=sqrt((x*x+y*y))/z;                    res=atan(temp);                    break;              case 1://与自然X轴的角度                    temp=x/sqrt((y*y+z*z));                    res=atan(temp);                    break;             case 2://与自然Y轴的角度                    temp=y/sqrt((x*x+z*z));                    res=atan(temp);                    break;      }       return res*1800/3.14;} 该部分代码总共有8个函数,这里我们仅介绍其中4个。首先是ADXL345_Init函数,该函数用来初始化ADXL345,和前面我们提到的步骤差不多,不过本章我们而是采用查询的方式来读取数据的,所以在这里并没有开启中断。另外3个偏移寄存器,都默认设置为0。其次,我们介绍ADXL345_RD_XYZ函数,该函数用于从ADXL345读取数据,通过该函数可以读取ADXL345的转换结果,得到三个轴的加速度值(仅是数值,并没有转换单位)。接着,我们介绍ADXL345_AUTO_Adjust函数,该函数用于ADXL345的校准,ADXL345有偏移校准的功能,该功能的详细介绍请参考ADXL345数据手册的第29页,偏移校准部分。这里我们就不细说了,如果不进行校准的话,ADXL345的读数可能会有些偏差,通过校准,我们可以讲这个偏差减少甚至消除。最后,我们看看ADXL345_Get_Angle函数,该函数根据ADXL345的读值,转换为与自然坐标系的角度。计算公式如下: http://www.openedv.com/upload/2013/3/22/37bbcc10a35aa62db986a4945fbc4a28_262.jpg 其中Ax,Ay,Az分别代表从ADXL345读到的X,Y,Z方向的加速度值。通过该函数,我们只需要知道三个方向的加速度值,就可以将其转换为对应的弧度值,再通过弧度角度转换,就可以得到角度值了。其他函数,我们就不介绍了,也比较简单。保存adxl345.c,然后把该文件加入HARDWARE组下。接下来打开adxl345.h在该文件里面加入如下代码:#ifndef __ADXL345_H#define __ADXL345_H#include "myiic.h"   #define DEVICE_ID            0X00      //器件ID,0XE5#define THRESH_TAP         0X1D          //敲击阀值……省略部分寄存器定义#define FIFO_STATUS         0X39 //0X0B TO OX1F Factory Reserved     //如果ALT ADDRESS脚(12脚)接地,IIC地址为0X53(不包含最低位).//如果接V3.3,则IIC地址为0X1D(不包含最低位).//开发板接V3.3,所以转为读写地址后,为0X3B和0X3A(如果接GND,则为0XA7和0XA6)  #define ADXL_READ    0X3B#define ADXL_WRITE   0X3Au8 ADXL345_Init(void);                                                              //初始化ADXL345void ADXL345_WR_Reg(u8 addr,u8 val);                                      //写ADXL345寄存器u8 ADXL345_RD_Reg(u8 addr);                                                   //读ADXL345寄存器void ADXL345_RD_XYZ(short *x,short *y,short *z);                      //读取一次值void ADXL345_RD_Avval(short *x,short *y,short *z);                    //读取平均值void ADXL345_AUTO_Adjust(char *xval,char *yval,char *zval);     //自动校准void ADXL345_Read_Average(short *x,short *y,short *z,u8 times);//连续读取times次,取平均short ADXL345_Get_Angle(float x,float y,float z,u8 dir);#endif上面的代码省略了部分寄存器的定义,其他部分比较简单,我们不作介绍。保存adxl345.h,然后在test.c里面修改代码如下://x,y:开始显示的坐标位置//num:要显示的数据//mode:0,显示加速度值;1,显示角度值;void Adxl_Show_Num(u16 x,u16 y,short num,u8 mode){       if(mode==0)   //显示加速度值       {              if(num<0)              {                     LCD_ShowChar(x,y,'-',16,0);               //显示负号                     num=-num;                                       //转为正数              }else LCD_ShowChar(x,y,' ',16,0);                     //去掉负号              LCD_ShowNum(x+8,y,num,4,16);                     //显示值                   }else             //显示角度值       {              if(num<0)              {                     LCD_ShowChar(x,y,'-',16,0);               //显示负号                     num=-num;                                       //转为正数              }else LCD_ShowChar(x,y,' ',16,0);                     //去掉负号             LCD_ShowNum(x+8,y,num/10,2,16);         //显示整数部分                              LCD_ShowChar(x+24,y,'.',16,0);                //显示小数点              LCD_ShowNum(x+32,y,num%10,1,16);     //显示小数部分                       }}                                                          int main(void){                   u8 key;       u8 t=0;           short x,y,z;               short angx,angy,angz;            Stm32_Clock_Init(9);           //系统时钟设置       uart_init(72,9600);             //串口初始化为9600       delay_init(72);                         //延时初始化        LED_Init();                        //初始化与LED连接的硬件接口       LCD_Init();                       //初始化LCD       usmart_dev.init(72);             //初始化USMART              KEY_Init();                         //按键初始化       POINT_COLOR=RED;//设置字体为红色        LCD_ShowString(60,50,200,16,16,"WarShip STM32");           LCD_ShowString(60,70,200,16,16,"3D TEST");        LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");       LCD_ShowString(60,110,200,16,16,"2012/9/12");      LCD_ShowString(60,130,200,16,16,"KEY0:Auto Adjust");      while(ADXL345_Init()) //3D加速度传感器初始化           {              LCD_ShowString(60,150,200,16,16,"ADXL345 Error");              delay_ms(200);              LCD_Fill(60,150,239,150+16,WHITE);             delay_ms(200);       }                                                                LCD_ShowString(60,150,200,16,16,"ADXL345 OK");      LCD_ShowString(60,170,200,16,16,"X VAL:");       LCD_ShowString(60,190,200,16,16,"Y VAL:");       LCD_ShowString(60,210,200,16,16,"Z VAL:");      LCD_ShowString(60,230,200,16,16,"X ANG:");       LCD_ShowString(60,250,200,16,16,"Y ANG:");       LCD_ShowString(60,270,200,16,16,"Z ANG:");       POINT_COLOR=BLUE;//设置字体为红色        while(1)       {                                   if(t%10==0)//每100ms读取一次              {                     //得到X,Y,Z轴的加速度值(原始值)                     ADXL345_Read_Average(&x,&y,&z,10);   //读取X,Y,Z三个方向的加速度值                      Adxl_Show_Num(60+48,170,x,0);             //显示加速度原始值                     Adxl_Show_Num(60+48,190,y,0);                     Adxl_Show_Num(60+48,210,z,0);                    //得到角度值,并显示                     angx=ADXL345_Get_Angle(x,y,z,1);                         angy=ADXL345_Get_Angle(x,y,z,2);                        angz=ADXL345_Get_Angle(x,y,z,0);                      Adxl_Show_Num(60+48,230,angx,1);        //显示角度值                     Adxl_Show_Num(60+48,250,angy,1);                     Adxl_Show_Num(60+48,270,angz,1);              }              key=KEY_Scan(0);              if(key==KEY_UP)              {                     LED1=0;//绿灯亮,提示校准中                     ADXL345_AUTO_Adjust((char*)&x,(char*)&y,(char*)&z);//自动校准                     LED1=1;//绿灯灭,提示校准完成              }             delay_ms(10);              t++;              if(t==20)              {                     t=0;                     LED0=!LED0;              }       }}此部分代码除了main函数,还有一个Adxl_Show_Num函数,该函数用于数据显示,因为在ILI93xx.c里面,没有提供可以显示小数和负数的函数,所以我们这里编写了该函数,来实现小数和负数的显示,以满足本章要求。其他部分,我们就不多说了。至此,我们的软件设计部分就结束了。34.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图34.4.1所示的内容: http://www.openedv.com/upload/2013/3/22/3a16eba3861a0e26d38c415c57c4c673_94.jpg 图34.4.1 程序运行时LCD显示内容 可以看到,X方向和Z方向的角度有些大(最佳值是0),所以我们按下WK_UP键,进行一次校准(注意校准的时候保持开发板水平,并且稳定),校准后如图34.4.2所示: http://www.openedv.com/upload/2013/3/22/7b9be654314d4cac8f68bff05e84a2b6_908.png 图34.4.2 校准后 可以看到,校准后,比未校准前好了很多,此时我们移动开发板到不同角度,可以看到X、Y、Z的数值和角度也跟着变化。      
    11. 30.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹下新建一个CAN的文件夹,然后新建一个can.c和can.h的文件保存在CAN文件夹下,并将CAN文件夹加入头文件包含路径。打开can.c文件,输入如下代码:#include "can.h"#include "led.h"#include "delay.h"#include "usart.h"//CAN初始化//tsjw:重新同步跳跃时间单元.范围:1~3;//tbs2:时间段2的时间单元.范围:1~8;//tbs1:时间段1的时间单元.范围:1~16;//brp :波特率分频器.范围:1~1024;(实际要加1,也就是1~1024) tq=(brp)*tpclk1//注意以上参数任何一个都不能设为0,否则会乱.//波特率=Fpclk1/((tbs1+tbs2+1)*brp);//mode:0,普通模式;1,回环模式;//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Normal_Init(1,8,7,5,1);//则波特率为:36M/((8+7+1)*5)=450Kbps//返回值:0,初始化OK;//    其他,初始化失败;u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode){       u16 i=0;      if(tsjw==0||tbs2==0||tbs1==0||brp==0)return 1;       tsjw-=1;//先减去1.再用于设置       tbs2-=1;       tbs1-=1;       brp-=1;       RCC->APB2ENR|=1<<2;    //使能PORTA时钟           GPIOA->CRH&=0XFFF00FFF;       GPIOA->CRH|=0X000B8000;//PA11 RX,PA12 TX推挽输出             GPIOA->ODR|=3<<11;                                       RCC->APB1ENR|=1<<25;//使能CAN时钟 CAN使用的是APB1的时钟(max:36M)       CAN->MCR=0x0000;    //退出睡眠模式(同时设置所有位为0)       CAN->MCR|=1<<0;             //请求CAN进入初始化模式       while((CAN->MSR&1<<0)==0)       {              i++;              if(i>100)return 2;//进入初始化模式失败       }       CAN->MCR|=0<<7;             //非时间触发通信模式       CAN->MCR|=0<<6;             //软件自动离线管理       CAN->MCR|=0<<5;             //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)       CAN->MCR|=1<<4;             //禁止报文自动传送       CAN->MCR|=0<<3;             //报文不锁定,新的覆盖旧的       CAN->MCR|=0<<2;             //优先级由报文标识符决定       CAN->BTR=0x00000000;//清除原来的设置.       CAN->BTR|=mode<<30;      //模式设置 0,普通模式;1,回环模式;       CAN->BTR|=tsjw<<24; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位       CAN->BTR|=tbs2<<20; //Tbs2=tbs2+1个时间单位       CAN->BTR|=tbs1<<16; //Tbs1=tbs1+1个时间单位       CAN->BTR|=brp<<0;         //分频系数(Fdiv)为brp+1                                          //波特率:Fpclk1/((Tbs1+Tbs2+1)*Fdiv)       CAN->MCR&=~(1<<0);      //请求CAN退出初始化模式       while((CAN->MSR&1<<0)==1)       {              i++;              if(i>0XFFF0)return 3;//退出初始化模式失败       }       //过滤器初始化       CAN->FMR|=1<<0;                    //过滤器组工作在初始化模式       CAN->FA1R&=~(1<<0);             //过滤器0不激活       CAN->FS1R|=1<<0;            //过滤器位宽为32位.       CAN->FM1R|=0<<0;           //过滤器0工作在标识符屏蔽位模式       CAN->FFA1R|=0<<0;          //过滤器0关联到FIFO0       CAN->sFilterRegister[0].FR1=0X00000000;//32位ID       CAN->sFilterRegister[0].FR2=0X00000000;//32位MASK       CAN->FA1R|=1<<0;            //激活过滤器0       CAN->FMR&=0<<0;                  //过滤器组进入正常模式#if CAN_RX0_INT_ENABLE      //使用中断接收       CAN->IER|=1<<1;               //FIFO0消息挂号中断允许.           MY_NVIC_Init(1,0,USB_LP_CAN_RX0_IRQChannel,2);//组2#endif       return 0;}  //id:标准ID(11位)/扩展ID(11位+18位)       //ide:0,标准帧;1,扩展帧//rtr:0,数据帧;1,远程帧//len:要发送的数据长度(固定为8个字节,在时间触发模式下,有效数据为6个字节)//*dat:数据指针.//返回值:0~3,邮箱编号.0XFF,无有效邮箱.u8 Can_Tx_Msg(u32 id,u8 ide,u8 rtr,u8 len,u8 *dat){              u8 mbox;         if(CAN->TSR&(1<<26))mbox=0;                     //邮箱0为空       else if(CAN->TSR&(1<<27))mbox=1;        //邮箱1为空       else if(CAN->TSR&(1<<28))mbox=2;        //邮箱2为空       else return 0XFF;                                      //无空邮箱,无法发送       CAN->sTxMailBox[mbox].TIR=0;              //清除之前的设置       if(ide==0)      //标准帧       {              id&=0x7ff;//取低11位stdid              id<<=21;                }else              //扩展帧       {              id&=0X1FFFFFFF;//取低32位extid              id<<=3;                                                                    }       CAN->sTxMailBox[mbox].TIR|=id;                  CAN->sTxMailBox[mbox].TIR|=ide<<2;             CAN->sTxMailBox[mbox].TIR|=rtr<<1;       len&=0X0F;//得到低四位       CAN->sTxMailBox[mbox].TDTR&=~(0X0000000F);       CAN->sTxMailBox[mbox].TDTR|=len;                 //设置DLC.       //待发送数据存入邮箱.       CAN->sTxMailBox[mbox].TDHR=(((u32)dat[7]<<24)|                                                        ((u32)dat[6]<<16)|                                                       ((u32)dat[5]<<8)|                                                        ((u32)dat[4]));       CAN->sTxMailBox[mbox].TDLR=(((u32)dat[3]<<24)|                                                        ((u32)dat[2]<<16)|                                                       ((u32)dat[1]<<8)|                                                        ((u32)dat[0]));       CAN->sTxMailBox[mbox].TIR|=1<<0; //请求发送邮箱数据       return mbox;}//获得发送状态.//mbox:邮箱编号;//返回值:发送状态. 0,挂起;0X05,发送失败;0X07,发送成功.u8 Can_Tx_Staus(u8 mbox){           u8 sta=0;                                       switch (mbox)       {              case 0:                     sta |= CAN->TSR&(1<<0);                 //RQCP0                     sta |= CAN->TSR&(1<<1);                 //TXOK0                     sta |=((CAN->TSR&(1<<26))>>24);    //TME0                     break;              case 1:                     sta |= CAN->TSR&(1<<8)>>8;           //RQCP1                     sta |= CAN->TSR&(1<<9)>>8;           //TXOK1                     sta |=((CAN->TSR&(1<<27))>>25);    //TME1                          break;              case 2:                     sta |= CAN->TSR&(1<<16)>>16;        //RQCP2                     sta |= CAN->TSR&(1<<17)>>16;        //TXOK2                     sta |=((CAN->TSR&(1<<28))>>26);    //TME2                     break;              default:                     sta=0X05;//邮箱号不对,肯定失败.              break;       }       return sta;}//得到在FIFO0/FIFO1中接收到的报文个数.//fifox:0/1.FIFO编号;//返回值:FIFO0/FIFO1中的报文个数.u8 Can_Msg_Pend(u8 fifox){       if(fifox==0)return CAN->RF0R&0x03;       else if(fifox==1)return CAN->RF1R&0x03;       else return 0;}//接收数据//fifox:邮箱号//id:标准ID(11位)/扩展ID(11位+18位)       //ide:0,标准帧;1,扩展帧//rtr:0,数据帧;1,远程帧//len:接收到的数据长度(固定为8个字节,在时间触发模式下,有效数据为6个字节)//dat:数据缓存区void Can_Rx_Msg(u8 fifox,u32 *id,u8 *ide,u8 *rtr,u8 *len,u8 *dat){              *ide=CAN->sFIFOMailBox[fifox].RIR&0x04;//得到标识符选择位的值       if(*ide==0)//标准标识符       {              *id=CAN->sFIFOMailBox[fifox].RIR>>21;       }else          //扩展标识符       {              *id=CAN->sFIFOMailBox[fifox].RIR>>3;       }       *rtr=CAN->sFIFOMailBox[fifox].RIR&0x02;    //得到远程发送请求值.       *len=CAN->sFIFOMailBox[fifox].RDTR&0x0F;//得到DLC      //*fmi=(CAN->sFIFOMailBox[FIFONumber].RDTR>>8)&0xFF;//得到FMI       //接收数据       dat[0]=CAN->sFIFOMailBox[fifox].RDLR&0XFF;       dat[1]=(CAN->sFIFOMailBox[fifox].RDLR>>8)&0XFF;       dat[2]=(CAN->sFIFOMailBox[fifox].RDLR>>16)&0XFF;       dat[3]=(CAN->sFIFOMailBox[fifox].RDLR>>24)&0XFF;          dat[4]=CAN->sFIFOMailBox[fifox].RDHR&0XFF;       dat[5]=(CAN->sFIFOMailBox[fifox].RDHR>>8)&0XFF;       dat[6]=(CAN->sFIFOMailBox[fifox].RDHR>>16)&0XFF;       dat[7]=(CAN->sFIFOMailBox[fifox].RDHR>>24)&0XFF;         if(fifox==0)CAN->RF0R|=0X20;//释放FIFO0邮箱       else if(fifox==1)CAN->RF1R|=0X20;//释放FIFO1邮箱  }#if CAN_RX0_INT_ENABLE      //使能RX0中断//中断服务函数                      void USB_LP_CAN1_RX0_IRQHandler(void){       u8 rxbuf[8];       u32 id;       u8 ide,rtr,len;          Can_Rx_Msg(0,&id,&ide,&rtr,&len,rxbuf);    printf("id:%d\r\n",id);    printf("ide:%d\r\n",ide);    printf("rtr:%d\r\n",rtr);    printf("len:%d\r\n",len);    printf("rxbuf[0]:%d\r\n",rxbuf[0]);    printf("rxbuf[1]:%d\r\n",rxbuf[1]);    printf("rxbuf[2]:%d\r\n",rxbuf[2]);    printf("rxbuf[3]:%d\r\n",rxbuf[3]);    printf("rxbuf[4]:%d\r\n",rxbuf[4]);    printf("rxbuf[5]:%d\r\n",rxbuf[5]);    printf("rxbuf[6]:%d\r\n",rxbuf[6]);    printf("rxbuf[7]:%d\r\n",rxbuf[7]);}#endif //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)     //len:数据长度(最大为8)                              //msg:数据指针,最大为8个字节.//返回值:0,成功;//            其他,失败;u8 Can_Send_Msg(u8* msg,u8 len){           u8 mbox;       u16 i=0;                                                            mbox=Can_Tx_Msg(0X12,0,0,len,msg);       while((Can_Tx_Staus(mbox)!=0X07)&&(i<0XFFF))i++;//等待发送结束       if(i>=0XFFF)return 1;                                             //发送失败?       return 0;                                                                 //发送成功;}//can口接收数据查询//buf:数据缓存区; //返回值:0,无数据被收到;//            其他,接收的数据长度;u8 Can_Receive_Msg(u8 *buf){                                  u32 id;       u8 ide,rtr,len;       if(Can_Msg_Pend(0)==0)return 0;                     //没有接收到数据,直接退出              Can_Rx_Msg(0,&id,&ide,&rtr,&len,buf);   //读取数据    if(id!=0x12||ide!=0||rtr!=0)len=0;                //接收错误              return len;     }此部分代码总共8个函数,我们挑其中几个比较重要的函数简单介绍下,首先是:CAN_Mode_Init函数。该函数用于CAN的初始化,该函数带有5个参数,可以设置CAN通信的波特率和工作模式等,在该函数中,我们就是按30.1节末尾的介绍来初始化的,本章中,我们设计滤波器组0工作在32位标识符屏蔽模式,从设计值可以看出,该滤波器是不会对任何标识符进行过滤的,因为所有的标识符位都被设置成不需要关心,这样设计,主要是方便大家实验。    第二个函数,Can_Tx_Msg函数。该函数用于CAN报文的发送,该函数先查找空的发送邮箱,然后设置标识符ID等信息,最后写入数据长度和数据,并请求发送,实现一次报文的发送。    第三个函数,Can_Msg_Pend函数。该函数用于查询接收FIFOx(x=0/1)是否为空,如果返回0,则表示FIFOx空,如果为其他值,则表示FIFOx有数据。    第四个函数,Can_Rx_Msg函数。该函数用于CAN报文的接收,该函数先读取标识符,然后读取数据长度,并读取接收到的数据,最后释放邮箱数据。       can.c里面,还包含了中断接收的配置,通过can.h的CAN_RX0_INT_ENABLE宏定义,来配置是否使能中断接收,本章我们不开启中断接收的。其他函数我们就不一一介绍了,都比较简单,大家自行理解即可。保存can.c,并把该文件加入HARDWARE组下面,然后我们打开can.h在里面输入如下代码:#ifndef __CAN_H#define __CAN_H #include "sys.h"        //CAN接收RX0中断使能#define CAN_RX0_INT_ENABLE      0                                 //0,不使能;1,使能.       u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode);  //CAN初始化u8 Can_Tx_Msg(u32 id,u8 ide,u8 rtr,u8 len,u8 *dat);                //发送数据u8 Can_Msg_Pend(u8 fifox);                                                  //查询邮箱报文void Can_Rx_Msg(u8 fifox,u32 *id,u8 *ide,u8 *rtr,u8 *len,u8 *dat);//接收数据u8 Can_Tx_Staus(u8 mbox);                                                 //返回发送状态u8 Can_Send_Msg(u8* msg,u8 len);                                       //发送数据u8 Can_Receive_Msg(u8 *buf);                                              //接收数据#endif  其中CAN_RX0_INT_ENABLE用于设置是否使能中断接收,本章我们不用中断接收,故设置为0。保存can.h。最后,我们在test.c里面,修改main函数如下:int main(void){                  u8 key;       u8 i=0,t=0;       u8 cnt=0;       u8 canbuf[8];       u8 res;       u8 mode=1;//CAN工作模式;0,普通模式;1,环回模式      Stm32_Clock_Init(9);    //系统时钟设置       uart_init(72,9600);      //串口初始化为9600       delay_init(72);                  //延时初始化       LED_Init();                 //初始化与LED连接的硬件接口       LCD_Init();                  //初始化LCD       usmart_dev.init(72);      //初始化USMART              KEY_Init();                  //按键初始化                   CAN_Mode_Init(1,8,7,5,mode);//CAN初始化,波特率450Kbps         POINT_COLOR=RED;//设置字体为红色       LCD_ShowString(60,50,200,16,16,"WarShip STM32");          LCD_ShowString(60,70,200,16,16,"CAN TEST");           LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");       LCD_ShowString(60,110,200,16,16,"2012/9/11");       LCD_ShowString(60,130,200,16,16,"LoopBack Mode");         LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息                  POINT_COLOR=BLUE;//设置字体为蓝色         LCD_ShowString(60,170,200,16,16,"Count:");                 //显示当前计数值        LCD_ShowString(60,190,200,16,16,"Send Data:");           //提示发送的数据        LCD_ShowString(60,250,200,16,16,"Receive Data:");              //提示接收到的数据                 while(1)       {              key=KEY_Scan(0);              if(key==KEY_RIGHT)//KEY0按下,发送一次数据              {                     for(i=0;i<8;i++)                     {                            canbuf=cnt+i;//填充发送缓冲区                            if(i<4)LCD_ShowxNum(60+i*32,210,canbuf,3,16,0X80);     //显示数据                            else LCD_ShowxNum(60+(i-4)*32,230,canbuf,3,16,0X80);   //显示数据                    }                     res=Can_Send_Msg(canbuf,8);//发送8个字节                     if(res)LCD_ShowString(60+80,190,200,16,16,"Failed");   //提示发送失败                     else LCD_ShowString(60+80,190,200,16,16,"OK    ");   //提示发送成功                     }else if(key==KEY_UP)//WK_UP按下,改变CAN的工作模式              {                            mode=!mode;                     CAN_Mode_Init(1,8,7,5,mode);//CAN模式初始化,波特率450Kbps                    POINT_COLOR=RED;//设置字体为红色                     if(mode==0)//普通模式,需要2个开发板                     {                            LCD_ShowString(60,130,200,16,16,"Nnormal Mode ");                            }else //回环模式,一个开发板就可以测试了.                     {                           LCD_ShowString(60,130,200,16,16,"LoopBack Mode");                     }                    POINT_COLOR=BLUE;//设置字体为蓝色              }                         key=Can_Receive_Msg(canbuf);              if(key)//接收到有数据              {                                       LCD_Fill(60,270,130,310,WHITE);//清除之前的显示                    for(i=0;i<key;i++)                     {                                                                                            if(i<4)LCD_ShowxNum(60+i*32,270,canbuf,3,16,0X80);     //显示数据                            else LCD_ShowxNum(60+(i-4)*32,290,canbuf,3,16,0X80);   //显示数据                    }              }              t++;              delay_ms(10);              if(t==20)              {                     LED0=!LED0;//提示系统正在运行                         t=0;                     cnt++;                     LCD_ShowxNum(60+48,170,cnt,3,16,0X80);     //显示数据              }                     }}此部分代码,我们主要关注下CAN_Mode_Init(1,8,7,5,mode);该函数用于设置波特率和CAN的模式,根据前面的波特率计算公式,我们知道这里的波特率被初始化为450Kbps。mode参数用于设置CAN的工作模式(普通模式/环回模式),通过WK_UP按键,可以随时切换模式。cnt是一个累加数,一旦KEY_RIGHT(KEY0)按下,就以这个数位基准连续发送5个数据。当CAN总线收到数据的时候,就将收到的数据直接显示在LCD屏幕上。30.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,得到如图30.4.1所示: http://www.openedv.com/upload/2013/3/18/e3550c4ff32d244ac5f00b7b3cb93779_412.png 图30.4.1 程序运行效果图伴随DS0的不停闪烁,提示程序在运行。默认我们是设置的环回模式,此时,我们按下KEY0就可以在LCD模块上面看到自发自收的数据(如上图所示),如果我们选择普通模式(通过WK_UP按键切换),就必须连接两个开发板的CAN接口,然后就可以互发数据了。如图30.4.2所示:http://www.openedv.com/upload/2013/3/18/a55eaca572e14f00ad24a691415323cc_222.jpg 图30.4.2 CAN普通模式数据收发测试上图中,左侧的图片来自开发板A,发送了8个数据,右侧来自开发板B,收到了来自开发板A的8个数据。
    12. my style.呵呵。
    13. 谢谢支持
    14. 一篇有关哈佛的文章,让我很感慨 29/8100 工作这点儿事 2010-10-23
      央视的采访,你也信? 4点多还在学习,骗鬼啊. 你一天可以,难道你就不要休息?某非哈弗的人都用了印度神油? 用电脑子想想,别跟着央视的瞎扯淡....
    15. 团购已结束,现在只能以正常价格购买了,呵呵。
    16. 不买仿真器,肯定就仿真不了了,呵呵。 在线下载程序是可以的,我们通过USB串口下载代码,所以只要您的电脑有USB,就可以下载代码的。 关于调试,也不一定要仿真器,可以通过串口打印信息来查看运行状态,其实这样更能锻炼你的能力。
    17. 开发板的资料,将集中在这个帖子: http://bbs.eeworld.com.cn/viewth ... &extra=page%3D1 如果你有什么问题或者建议,都可以在这个帖子里面提出。
    18. 求助单片机万年历 28/11167 电子竞赛 2009-04-19
      DDDDDDDDDDDDDDDDDDDDDD
    19. 双核单片机LS2051光影棒的原理图 50/21303 DIY/开源硬件专区 2008-12-11
      电子工程师.下载还要啥币.郁闷... ================================= 我没有要芯币啊 不是免费的么
    20. 给大家一个GL850A的USB HUB的图 69/31557 单片机 2008-06-26
      拜托,HUB要什么启动,插上就行了。
    1. 正点原子 MiniFly四轴飞行器功能演示 目前已上传1课时 2018-03-09
  • TA暂时无记录哦~
  • TA暂时无记录哦~

统计信息

已有32965人来访过

  • 芯币:310
  • 好友:4
  • 主题:79
  • 回复:20
  • 课时:1
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


xgz 2018-8-19
原子哥,我用stm32f407加载fatfs对8G的sd卡读写成功,但换用128G的sd卡时,可以检测到sd卡,即读不了卡的大小,也写不了数据,下载fatfs0.12b版,但不会写底层程序 . . . . . .
哥不是说读写128G的sd卡没问题吗?能发个例程吗?先谢了 !!!
找到HAL库函数版,问题已解决。
梦由心生 2018-2-7
开发板中定时器中断控制IO口输出频率是正确的,但是在自己焊接的板子上频率输出为什么是不正确的
wateras1 2014-8-23
写的不错
wateras1 2014-8-23
写的不错
_Stone_ 2013-8-10
原子哥.. stm32 TIM2计数器清零失败....  发帖在阿莫了..
http://www.amobbs.com/thread-5546087-1-1.html
能不能帮忙解决一下....
googleyang123 2012-6-4
原子哥,我在做fat32的实验 FileInfo=&a ;                        temp=1;                             Get_File_Info(fcluster, FileInfo, T_FILE, &temp);        printf("\r\n File Name: %s", a .F_Name); 为什么独到的中文文件名是乱码,英文文件名正常, 但是在你的例程里可以读到中文文件名 ...
hansmmm 2012-5-24
您好!请教朋友stm32问题,朋友向我推荐您。我这里有一个困惑,还希望您能指点一下,感激不尽。例程中的can_normal程序中,在can初始化里面有一句CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);使能挂号中断。在NVIC中却是 NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;然后在中断服务函数中是与USB_LP_CAN1_RX0_IRQn相关的中断函数。小弟有点疑惑,CAN_IT_FMP0和USB_LP_CAN1_RX0_IRQn这两个中断的异同点是什么?在具体接收数据中又是如何相互配合工作的呢?
查看全部