3072|4

603

帖子

1

TA的资源

纯净的硅(中级)

楼主
 

晒设计方案+马里奥大叔--听我指挥【实战】 [复制链接]

本帖最后由 sjtitr 于 2014-2-20 00:51 编辑

依然,首先回顾构想贴:
晒设计方案+马里奥大叔--听我指挥

本辑内容,核心内容是讲述如何使用鸡腿,以及少量的I2C的使用。到此为止,算是实现了NES模拟器的基本目标。不能玩的游戏机那还算不上游戏机。能控制了,才有点游戏机的模样。

但是截至本辑结束,这个模拟器也没有发出声音……事实上,声音一部分,已经在上一辑里通过吊丝听音乐的方式大概讲解了,模拟器这部分就不再讨论有关声音的问题啦。
之所以如此热衷于马里奥大叔系列,不仅因为作为一个忠诚的吊丝,要随时把技术和玩捏在一起,更是因为,大叔他肩负着我童年的美好回忆。

废话少说,节目开始。

我们接着NES模拟器继续,有了前面的基础,模拟器已经可以正常运转,只是还没有接受用户输入信息。

这里,我们使用wii的左手手柄来作为输入设备,我们通常称呼这个家伙为鸡腿。


鸡腿连接我们的板子,需要接四根线,两根电源,两根I2C通信线。

在F4上,恰好触摸屏也是I2C通信的,所以我们可以借用这部分内容。在示例工程中,我们选择Touch_Panel工程进行初步的调试。

I2C,这个就不多说了,尤其是STM32的I2C,更是坑人不浅呢。不过在这里我仍然坚持使用硬件I2C。

参考关于触摸屏接口的I2C,我们来实现鸡腿的I2C通信。

首先要了解如何利用鸡腿来工作。

到网络上找资料,于是找到如下的内容,请重点观赏后者
http://wiibrew.org/wiki/Wiimote/Extension_Controllers
http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck

为了懒得啃E文的童鞋,我来总结一下吧。
1.鸡腿的8位Slave地址(包括读写标志位),是0xA4。
2.鸡腿需要初始化,方法是先向0xF0写0x55,然后再向0xFB写0x00。
3.读取鸡腿的信息,是从0x00处读取6个字节,这6个字节,我们现在只需要前两个和最后一个。前两个分别表示x轴和y轴,最后一个的低两位表示鸡腿上的两个按键状态。

然后把鸡腿连在板子上,接线规则是,-+dc四根线分别接在GND、3V、PC9、PA8。

于是可以开始写代码啦。

初始化:

  1. static void delay(volatile uint32_t nCount)
  2. {
  3.   volatile uint32_t index = 0;
  4.   for(index = nCount; index != 0; index--)
  5.   {
  6.   }
  7. }

  8. uint8_t IOE_Chuck_Set(uint8_t reg, uint8_t val)
  9. {
  10.   I2C_GenerateSTART(IOE_I2C, ENABLE);

  11.   /* Test on EV5 and clear it */
  12.   IOE_TimeOut = TIMEOUT_MAX;
  13.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_SB))
  14.   {
  15.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  16.   }

  17.   /* Transmit the slave address and enable writing operation */
  18.   I2C_Send7bitAddress(IOE_I2C, NUNCHUCK_ADDR, I2C_Direction_Transmitter);
  19.   
  20.   /* Test on EV6 and clear it */
  21.   IOE_TimeOut = TIMEOUT_MAX;
  22.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_ADDR))
  23.   {
  24.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  25.   }
  26.   
  27.   /* Read status register 2 to clear ADDR flag */
  28.   IOE_I2C->SR2;
  29.   
  30.   /* Test on EV8_1 and clear it */
  31.   IOE_TimeOut = TIMEOUT_MAX;
  32.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE))
  33.   {
  34.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  35.   }
  36.   
  37.   /* Transmit the first address for r/w operations */
  38.   I2C_SendData(IOE_I2C, reg);
  39.   
  40.   /* Test on EV8 and clear it */
  41.   IOE_TimeOut = TIMEOUT_MAX;
  42.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE))
  43.   {
  44.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  45.   }
  46.   
  47.   /* Prepare the register value to be sent */
  48.   I2C_SendData(IOE_I2C, val);
  49.   
  50.   /* Test on EV8_2 and clear it */
  51.   IOE_TimeOut = TIMEOUT_MAX;
  52.   while ((!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE)) || (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_BTF)))
  53.   {
  54.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  55.   }
  56.   
  57.   /* End the configuration sequence */
  58.   I2C_GenerateSTOP(IOE_I2C, ENABLE);
  59.   
  60.   /* All configuration done */
  61.   return IOE_OK;  
  62. }
  63.    
  64. void IOE_Chuck_Init(void)
  65. {
  66.   delay(50000);
  67.   ioe_err = IOE_Chuck_Set(0xf0, 0x55);
  68.   delay(50000);
  69.   ioe_err = IOE_Chuck_Set(0xfb, 0x00);
  70.   delay(50000);
  71. }
复制代码

以上代码,我自己写的比较少,基本都是找示例扒过来的。注意到里面有许多的delay,这个东西不是鸡腿控制说明里面提的,而是我根据实验,发现我手里的破鸡腿,需要“慢”一点来访问,才能正常工作,最后加上的延时操作,否则,可能初始化就会不成功,进而从鸡腿得到的数据都是0xFF。事实上,我还有另外一个鸡腿,不需要这样那样的延时,也可以工作得很好,所以我觉得是破鸡腿的问题,但是作为分享,我把这些延时也拿出来,提醒各位控制鸡腿的时候可能需要“慢”一点哦。

接下来就是读取鸡腿数据啦:
  1. uint8_t IOE_Chuck_Seek(uint8_t reg)
  2. {
  3.   I2C_GenerateSTART(IOE_I2C, ENABLE);

  4.   /* Test on EV5 and clear it */
  5.   IOE_TimeOut = TIMEOUT_MAX;
  6.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_SB))
  7.   {
  8.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  9.   }

  10.   /* Transmit the slave address and enable writing operation */
  11.   I2C_Send7bitAddress(IOE_I2C, NUNCHUCK_ADDR, I2C_Direction_Transmitter);
  12.   
  13.   /* Test on EV6 and clear it */
  14.   IOE_TimeOut = TIMEOUT_MAX;
  15.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_ADDR))
  16.   {
  17.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  18.   }
  19.   
  20.   /* Read status register 2 to clear ADDR flag */
  21.   IOE_I2C->SR2;
  22.   
  23.   /* Test on EV8_1 and clear it */
  24.   IOE_TimeOut = TIMEOUT_MAX;
  25.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE))
  26.   {
  27.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  28.   }
  29.   
  30.   /* Transmit the first address for r/w operations */
  31.   I2C_SendData(IOE_I2C, reg);
  32.   
  33.   /* Test on EV8 and clear it */
  34.   IOE_TimeOut = TIMEOUT_MAX;
  35.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE))
  36.   {
  37.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  38.   }
  39.   
  40.   /* End the configuration sequence */
  41.   I2C_GenerateSTOP(IOE_I2C, ENABLE);
  42.   
  43.   /* All configuration done */
  44.   return IOE_OK;  
  45. }

  46. uint8_t IOE_Chuck_Read(uint8_t *buf)
  47. {
  48.   /* Send START condition a second time */  
  49.   I2C_GenerateSTART(IOE_I2C, ENABLE);
  50.   
  51.   /* Test on EV5 and clear it */
  52.   IOE_TimeOut = TIMEOUT_MAX;
  53.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_SB))
  54.   {
  55.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  56.   }
  57.   
  58.   /* Send IO Expander address for read */
  59.   I2C_Send7bitAddress(IOE_I2C, NUNCHUCK_ADDR, I2C_Direction_Receiver);
  60.   
  61.   /* Test on EV6 and clear it */
  62.   IOE_TimeOut = TIMEOUT_MAX;
  63.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_ADDR))
  64.   {
  65.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  66.   }

  67.   /* Disable Acknowledgement and set Pos bit */
  68.   I2C_AcknowledgeConfig(IOE_I2C, DISABLE);      
  69.   I2C_NACKPositionConfig(IOE_I2C, I2C_NACKPosition_Next);
  70.   
  71.   /* Read status register 2 to clear ADDR flag */
  72.   IOE_I2C->SR2;

  73.   /* Test on EV7 and clear it */
  74.   IOE_TimeOut = TIMEOUT_MAX;
  75.   while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_BTF))
  76.   {
  77.     if (IOE_TimeOut-- == 0) return(IOE_FAILURE);
  78.   }
  79.    
  80.   /* Send STOP Condition */
  81.   I2C_GenerateSTOP(IOE_I2C, ENABLE);
  82.    
  83.   /* Read the first byte from the IO Expander */
  84.   buf[0] = I2C_ReceiveData(IOE_I2C);
  85.    
  86.   /* Read the second byte from the IO Expander */
  87.   buf[1] = I2C_ReceiveData(IOE_I2C);
  88.                                          
  89.   /* Enable Acknowledgement and reset POS bit to be ready for another reception */
  90.   I2C_AcknowledgeConfig(IOE_I2C, ENABLE);
  91.   I2C_NACKPositionConfig(IOE_I2C, I2C_NACKPosition_Current);
  92.    
  93.   /* All read done */
  94.   return IOE_OK;  
  95. }
复制代码
  1. ……
  2.     ioe_err = IOE_Chuck_Seek(0);
  3.     delay(50000);
  4.     ioe_err = IOE_Chuck_Read(chuck_buf);
  5.     delay(1000);
  6.     ioe_err = IOE_Chuck_Read(&chuck_buf[2]);
  7.     delay(1000);
  8.     ioe_err = IOE_Chuck_Read(&chuck_buf[4]);
  9.     delay(1000);
复制代码

有童鞋可能会问,不是说从0读取6个字节么……是啊,可是,实践是检验真理的唯一标准。我发现结合STM32的I2C,还有我的破鸡腿,直接连续读6个字节真的是噩梦。恰示例读取触摸屏是每次2字节,于是我也就模仿着,然后自己每次去读两个字节,定好地址,连续读3次,反正都差不多嘛,最后我的代码也奏效了,哦呵呵

补充一点,如果你读到鸡腿的返回值都是0xFF,说明鸡腿初始化不正确哦,就需要在初始化部分做文章了,切记。

以上代码,在触摸屏的工程上,测试通过了,然后移到NES工程里。
为了能“慢”一点访问鸡腿,我不得不在每一帧的4条不同扫描线里去做读鸡腿的动作,先定好地址,之后3次每次都两个字节……搞出这样的处理,郁闷啊。

所幸的是,InfoNES已经预留了这样的位置,InfoNES_Wait函数,就是为了在高速环境里拖慢模拟器运行速度到真实速度,在每个扫描线都会调用一次,如果你想降低模拟器运行速度,就再这里写一些耽误时间的操作,例如Sleep。我们在这里,判断当前扫描线,在适当的时机进行动作。
  1. /* Wait */
  2. void InfoNES_Wait()
  3. {
  4.   switch(PPU_Scanline)
  5.   {
  6. //    case 9:
  7. //      ioe_err = IOE_Chuck_Seek(0);
  8. //      delay(50000);
  9. //      ioe_err = IOE_Chuck_Read(chuck_buf);
  10. //      delay(1000);
  11. //      ioe_err = IOE_Chuck_Read(&chuck_buf[2]);
  12. //      delay(1000);
  13. //      ioe_err = IOE_Chuck_Read(&chuck_buf[4]);
  14. //      delay(1000);
  15. //      break;
  16.     case 10:
  17.       ioe_err = IOE_Chuck_Seek(0);
  18.       break;
  19.     case 240:
  20.       ioe_err = IOE_Chuck_Read(chuck_buf);
  21.       break;
  22.     case 245:
  23.       ioe_err = IOE_Chuck_Read(&chuck_buf[2]);
  24.       break;
  25.     case 250:
  26.       ioe_err = IOE_Chuck_Read(&chuck_buf[4]);
  27.       break;
  28.     default:
  29.       break;
  30.   }
  31. }
复制代码

最后,在定期调用的InfoNES_PadState函数中,分析chuck_buf的内容,并填充pad键状态。
  1. /* Get a joypad state */
  2. void InfoNES_PadState( DWORD *pdwPad1, DWORD *pdwPad2, DWORD *pdwSystem )
  3. {
  4.   {
  5.     *pdwPad1 = 0;
  6.     if(chuck_buf[0]<0x60)
  7.     {
  8.       *pdwPad1 |= PAD_JOY_LEFT;
  9.     }
  10.     else if(chuck_buf[0]>0x90)
  11.     {
  12.       *pdwPad1 |= PAD_JOY_RIGHT;
  13.     }
  14.     else
  15.     {
  16.     }

  17.     if(chuck_buf[1]<0x60)
  18.     {
  19.       *pdwPad1 |= PAD_JOY_DOWN;
  20.     }
  21.     else if(chuck_buf[1]>0x90)
  22.     {
  23.       *pdwPad1 |= PAD_JOY_UP;
  24.     }
  25.     else
  26.     {
  27.     }
  28.    
  29.     if(chuck_buf[5]&0x01)
  30.     {
  31.     }
  32.     else
  33.     {
  34.       *pdwPad1 |= PAD_JOY_A;
  35.     }
  36.     if(chuck_buf[5]&0x02)
  37.     {
  38.     }
  39.     else
  40.     {
  41.       *pdwPad1 |= PAD_JOY_B;
  42.     }
  43.     if(STM_EVAL_PBGetState(BUTTON_USER) == Bit_RESET)
  44.     {
  45.     }
  46.     else
  47.     {
  48.       *pdwPad1 |= PAD_JOY_START;
  49.     }
  50.   }
  51. }
复制代码

需要注意到一点,鸡腿上只有两个按键,要想玩游戏,除了方向需要3个按键,所以用板子上的User按键顶个包。

当然,我把I2C和鸡腿的初始化放在读取ROM的后面了。

download!运行!

翻滚吧,大叔。


其实,稍有风吹草动,I2C就挂了,然后大叔就不受控制了。I2C的稳定性,暂时不在讨论范围内,追求稳定的童鞋,去用软件模拟吧。

仍然,最后把可以运行的工程,分享给大家。不过也隐隐的觉得,有可能直接烧写我的原版,也不能100%驱动你的鸡腿……桑心!
Nes.zip (2.36 MB, 下载次数: 34) (7z压缩,体积更小)

本辑完。

此帖出自stm32/stm8论坛

最新回复

带着回忆就把板子给用上了,赞 。 话说马里奥是从某一集爱情公寓里看到的,嘿嘿  看来我太缺少童年了  详情 回复 发表于 2014-2-21 17:33
点赞 关注(1)
 

回复
举报

1453

帖子

18

TA的资源

纯净的硅(高级)

沙发
 
翻滚吧,鸡腿~~
此帖出自stm32/stm8论坛
 
个人签名http://weibo.com/u/1391449055
 

回复

115

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
没赶上活动,昨天淘了个429discovery,,这会儿可以用上啦
此帖出自stm32/stm8论坛
 
 

回复

20

帖子

3

TA的资源

一粒金砂(初级)

4
 
不错的东西
此帖出自stm32/stm8论坛
 
 
 

回复

2万

帖子

71

TA的资源

管理员

5
 
带着回忆就把板子给用上了,赞 。

话说马里奥是从某一集爱情公寓里看到的,嘿嘿  看来我太缺少童年了
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表