Python,让嵌入式应用开发更便捷、更高效、更专注
本帖最后由 xld0932 于 2022-7-4 13:22 编辑#申请原创# @21小跑堂
前言前面分享了基于PikaScript如何在MM32平台上部署Python开发环境的帖子,实现了Python基础开发环境的部署,可以通过串口终端软件在线编写Python,然后直接运行得到结果。
通过Python编程我们是想用来实现应用程序控制的,而非底层驱动程序的实现;基于开发板的底层驱动、以及对板载资源的外设驱动,是在开发板上部署Python开发环境的时候就需要实现的,相当于Python的基建;只有基建实现了,才能够搭建出上层建筑。
那如何实现基于开发板Python的应用编程呢?或者说,怎样才能做到通过Python编程实现对开发板、或者板载外设的控制?如何灵活的下载程序呢?
本文将一步步带你来完成Python在开发板上基建的部署,以及实现上层Python应用程序的开发例程。
串口下载Python脚本、运行程序对于Python开发环境的部署本质还是在KEIL MDK或者IAR EWARM集成开发环境下通过C语言来开发的,编译后生成HEX执行文件,通过J-LINK或者CMSIS-DAP工具将执行文件下载到芯片当中去运行;那我们Python应用程序,是Python脚本源码文件,如何下载到芯片中去运行呢?
PikaScript提供了Python脚本源码解析的功能,它通过将Python脚本源码解析成Pika字节码,然后运行应用功能,所以我们只需要Python脚本源码传送给PikaScript解析运行即可;这样对于Python的存储方式就灵活很多了,你可以选择存放在MCU内部的FLASH空间、可以存放在外部SPI FLASH空间、也可以存放在TF/SD卡中,通过FatFs文件系统读取Python脚本源码给PikaScript解析运行。
我们这边的实现方式是通过串口的方式,将Python脚本源码存放在MCU内部的FLASH中,将串口接收到的Python脚本字符串完整写入到MCU内容FLASH即可。在启动时不再是直接使用pikaScriptInit()函数,而是通过定义的pikaMain根对象,使用obj_run(pikaMain, code)运行脚本功能。
要实现串口下载Python脚本,需要依据你使用的不同芯片进行相应的移植,参考如下几个步骤:
[*]配置串口参数、开启串口接收中断功能,将串口接收到的Python脚本字符存放在缓存中:
void MCU_InitUART(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
UART_InitTypeDef UART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
UART_StructInit(&UART_InitStructure);
UART_InitStructure.UART_BaudRate = 115200;
UART_InitStructure.UART_WordLength = UART_WordLength_8b;
UART_InitStructure.UART_StopBits = UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART1, &UART_InitStructure);
UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);
UART_Cmd(UART1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,GPIO_AF_7);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void UART1_IRQHandler(void)
{
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
{
pikaScript_RxData = UART_ReceiveData(UART1);
pikaScript_RxFlag = 1;
UART_ClearITPendingBit(UART1,UART_IT_RXIEN);
if(UART1_RxLength > sizeof(UART1_RxBuffer))
{
UART1_RxLength = 0;
memset(UART1_RxBuffer, 0, sizeof(UART1_RxBuffer));
}
UART1_RxBuffer = pikaScript_RxData;
if(pikaScript_RxData == '\n')
{
if(pikaScript_ASM_ReceiveHandler(UART1_RxBuffer, UART1_RxLength))
{
goto line_exit;
}
memcpy(pikaScript_ShellBuffer, UART1_RxBuffer, sizeof(pikaScript_ShellBuffer));
pikaScript_ShellReady = 1;
line_exit:
UART1_RxLength = 0;
memset(UART1_RxBuffer, 0, sizeof(UART1_RxBuffer));
}
}
}uint8_t pikaScript_ASM_ReceiveHandler(char *RxData, uint32_t RxSize)
{
char buff = {0};
if(0 == codeHeap.ena)
{
uint8_t is_launch_code_recive = 0;
char *strLine = strGetLastLine(buff, RxData);
/* auto erase mode, send python file directly */
if(strIsStartWith(strLine, "import "))
{
is_launch_code_recive = 1;
codeHeap.auto_erase = 1;
codeHeap.content = pikaMalloc(codeHeap.size + 1);
}
/* manual erase mode, send "import" to erase first,
then send the python file.
*/
if(strEqu(strLine, "import\r\n"))
{
is_launch_code_recive = 1;
codeHeap.auto_erase = 0;
codeHeap.wait = 2;
__disable_irq();
obj_deinit(__pikaMain);
codeHeap.content = malloc(pikaScript_ASM_FINIS_ADDRESS - pikaScript_ASM_START_ADDRESS);
if(codeHeap.content == NULL)
{
printf(" no enough code buff, please reset the device.\r\n");
while(1);
}
__enable_irq();
printf(" in download mode, send python please.\r\n");
}
/* launch the code reciver */
if(is_launch_code_recive)
{
codeHeap.reciveTime = SysTick_Tick;
codeHeap.ena = 1;
RxData = strLine;
}
}
if(1 == codeHeap.ena)
{
if(!pikaScript_MomoryLocked)
{
pikaScript_MomoryLocked = 1;
}
if(codeHeap.wait > 0)
{
codeHeap.wait--;
}
codeHeap.reciveTime = SysTick_Tick;
codeHeap.oldSize = codeHeap.size;
codeHeap.size += RxSize;
/* write to heap buff */
if(codeHeap.auto_erase)
{
codeHeap.content = realloc(codeHeap.content, codeHeap.size + 1);
}
memcpy(codeHeap.content + codeHeap.oldSize, RxData, RxSize);
codeHeap.content = 0;
return 1;
}
/* not work */
return 0;
}
[*]实现对MCU内部FLASH的读写操作,定义存储空间区域,用于存放Python脚本源码:
uint32_t pikaScript_FLASH_GetPageNumber(uint32_t Address)
{
return (Address - MM32F327xG_FLASH_START_ADDRESS) / MM32F327xG_FLASH_PAGE_SIZE;
}
void pikaScript_FLASH_EraseUserZone(void)
{
uint32_t StartPage = 0, NumberOfPages = 0;
printf(": erising flash...\r\n");
pikaScript_WriteAddress = 0;
pikaScript_FlashOffset= 0;
memset(pikaScript_FlashBuffer, 0, sizeof(pikaScript_FlashBuffer));
StartPage = pikaScript_FLASH_GetPageNumber(pikaScript_ASM_START_ADDRESS);
NumberOfPages = pikaScript_FLASH_GetPageNumber(pikaScript_ASM_FINIS_ADDRESS) - StartPage + 1;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
while(NumberOfPages--)
{
FLASH_ErasePage(MM32F327xG_FLASH_PAGE_SIZE * StartPage);
FLASH_ClearFlag(FLASH_FLAG_EOP); StartPage++;
}
FLASH_Lock();
printf("[ OK ]: erising flash ok!\r\n");
}
uint32_t pikaScript_FLASH_ProgramWord(uint32_t BaseAddress, uint32_t FlashAddress, char ch)
{
uint32_t pikaScript_FlashData = 0;
if(pikaScript_FlashOffset > 3)
{
pikaScript_FlashOffset = 0;
for(int i = 3; i >= 0; i--)
{
pikaScript_FlashData= pikaScript_FlashData << 8;
pikaScript_FlashData += pikaScript_FlashBuffer;
}
__platform_disable_irq_handle();
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
FLASH_ProgramWord(BaseAddress + FlashAddress, pikaScript_FlashData);
FLASH_ClearFlag(FLASH_FLAG_EOP);
FLASH_Lock();
__platform_enable_irq_handle();
FlashAddress = FlashAddress + 4;
}
pikaScript_FlashBuffer = ch;
return FlashAddress;
}
void pikaScript_FLASH_WriteASM(char *ptr, uint32_t StartAddress, uint32_t FinisAddress, uint32_t Size)
{
for(uint32_t i = 0; i < Size; i++)
{
pikaScript_WriteAddress = pikaScript_FLASH_ProgramWord(StartAddress, pikaScript_WriteAddress, ptr);
}
}
void pikaScript_FLASH_WriteEOF(void)
{
uint32_t StartAddress = pikaScript_ASM_START_ADDRESS;
for(uint8_t i = 0; i < 16; i++)
{
pikaScript_WriteAddress = pikaScript_FLASH_ProgramWord(StartAddress, pikaScript_WriteAddress, '\0');
}
}
void pikaScript_FLASH_SaveString(char *str, uint32_t StartAddress, uint32_t FinisAddress)
{
pikaScript_FLASH_WriteASM(str, StartAddress, FinisAddress, strGetSize(str));
}
[*]在定时器中对Python下载进行处理,用于判断是否完成接收,接收完成后存储Python脚本源码:
void pikaScript_ASM_ProgramHandler(void)
{
if(!codeHeap.ena)
{
/* recive not activate */
return;
}
if(codeHeap.wait)
{
/* still waiting */
return;
}
if(SysTick_Tick - codeHeap.reciveTime < 200)
{
/* still reciving */
return;
}
printf("============================\r\n");
/* transmite is finished */
printf(": recieved size: %d\r\n", codeHeap.size);
/* write to flash from buff (heap.content) */
pikaScript_FLASH_EraseUserZone();
printf(": Writing flash... \r\n");
FLASH_Unlock();
/* write to flash */
pikaScript_FLASH_WriteASM(codeHeap.content,
pikaScript_ASM_START_ADDRESS,
pikaScript_ASM_FINIS_ADDRESS,
codeHeap.size);
/* write EOF */
pikaScript_FLASH_WriteASM("\n\n",
pikaScript_ASM_START_ADDRESS,
pikaScript_ASM_FINIS_ADDRESS,
0x02);
pikaScript_FLASH_WriteEOF();
FLASH_Lock();
printf("[ OK ]: Write flash ok! \r\n");
char *codeInFlash = (char *)pikaScript_ASM_START_ADDRESS;
printf("\r\n");
printf("---------\r\n");
for(int i = 0; i < strGetSize(codeInFlash); i++)
{
if('\n' == codeInFlash)
{
if(codeInFlash != '\r')
{
fputc('\r', (FILE*)!NULL);
}
}
fputc(codeInFlash, (FILE*)!NULL);
}
printf("---------\r\n");
printf("\r\n");
printf("[ OK ]: Programing ok!\r\n");
printf(": Restarting... \r\n");
printf("============================\r\n");
printf("\r\n");
NVIC_SystemReset();
}
[*]系统上电启动后对Python脚本源码进行解析、然后跳转运行:
int main(void)
{
SysTick_Init();
MCU_InitUART();
printf("\r\nPikaScript SZ-III(MM32F3277G9P) %s %s", __DATE__, __TIME__);
pikaScriptInfo();
pikaScript_ASM_Init();
/* boot pikaScript */
char *code = (char *)pikaScript_ASM_START_ADDRESS;
if(((uint8_t)(code) != 0xFF) && ((uint8_t)(code) != 0xFF))
{
/* boot from flash */
pikaMain = newRootObj("pikaMain", New_PikaMain);
__pikaMain = pikaMain;
if(code == 'i')
{
printf(": compiling the python script...\r\n");
main_codeBuff = arg_setStr(NULL, "", code);
pikaCompile("", arg_getStr(main_codeBuff));
NVIC_SystemReset();
}
else
{
pikaVM_runByteCode(pikaMain, (uint8_t *)code);
goto main_loop;
}
}
else
{
/* boot from firmware */
printf(": boot from firmware.\r\n");
pikaMain = pikaScriptInit();
goto main_loop;
}
main_loop:
pikaScriptShell(pikaMain);
/* after exit() from pika shell */
NVIC_SystemReset();
}
接下来,我们来演示一下通过串口方式来下载Python应用程序,以及查看运行结果:https://www.bilibili.com/video/BV1s94y1R74x/
PikaScript标准库PikaScript提供了5个标准库,分别是PikaStdLib标准库、PikaStdDevice标准设备、PikaStdData数据结构、PikaStdTask多任务、PikaDebug调试器;基于除了PikaStdDevice标准设备是由我们用户定义和实现的外,其它的都是PikaScript已经完成的功能代码,我们不需要再做修改,直接使用就可以了。
为了使基于PikaScript的Python应用程序对不同平台设备的操作具有统一性,官方提出了PikaStdDevice标准设备库,其中定义了固定的对GPIO、TIM、PWM、ADC、I2C、SPI、UART等基本MCU外设的操作函数,我们只需要根据使用的MCU来进行适配就可以了。
PikaScript C模块开发Python的上层应用程序开发是基于PikaScript这个基建的,而PikaScript C模块开发可以给上层的Python应用程序提供丰富的功能,你可以理解为应用程序调用底层驱动的API接口函数或者库。接下来,我们使用神舟 III号开发板(板载MCU为MM32F3270)在完成串口下载功能的工程模板上,可以根据下面的描述和步骤来操作……
在附件工程中Source\PikaScript文件夹下需要有4个基础文件文件:requestment.txt、pikaPackage.exe、pikaBeforBuild-keil.bat、rust-msc-latest-win10.exe,根据requestment.txt的版本要求,双击pikaPackage.exe获取PikaScript源码到当前目录;pikaBeforBuild-keil.bat文件是用来在KEIL MDK中编译前预处理的指令集合,通过调用rust-msc-latest-win10.exe来自动运行PikaScript预编译器,将Python文件在PC开发时就转换成字节码并打包成一个库,就像C一样,可以通过KEIL进行编译下载到MCU运行,给Python上层应用程序提供基建模块。所以KEIL中需要有如下配置:
在获取/更新完PikaScript源码后,接着在Source\PikaScript文件夹下添加main.py、PikaStdDevice.pyi、MM32F3270.pyi、SHENZHOU3.pyi这4个文件:标准的设备驱动由PikaStdDevice结合MM32F3270来实现的,PikaStdDevice是一个抽象的设备驱动模块,定义了所有的用户API,各个平台的驱动模块只要从PikaStdDevice继承,就能名获得一模一样的用户API,而PikaStdDevice内部会间接调用平台驱动,通过多态特性重写底层的平台驱动,就可以在不同的平台工作了。如果是特定的开发板,你也可以选择不去实现PikaStdDevice也是可行的,通过MM32F3270和SHENZHOU3这两个文件来实现就可以了……
当前没有去实现标准设备库,只是通过MM32F3270.pyi基于SysTick实现了Task功能,这样在Python应用程序的时候,就可以使用Task来创建任务,进行任务调试;如下所示:#include <stdint.h>
#include "BaseObj.h"
#include "config.h"
#include "dataStrs.h"
extern uint32_t SysTick_Tick;
void MM32F3270_Task_platformGetTick(PikaObj* self)
{
obj_setInt(self, "tick", SysTick_Tick);
}
如果对Task任务调试的时序要求比较高的话,可以将PikaScript的优化配置将原来的PIKA_OPTIMIZE_SIZE切换到PIKA_OPTIMIZE_SPEED,优化后的结果就是在运行速度和代码占用空间这两者之间做了个平衡和取舍。
每一个PikaScript C模块都是由模块接口和模块实现两部分组成,其中SHENZHOU3.pyi实现了一个模块接口,在模块接口中我们可以根据板载的资源去定义不同的类(比如LED、KEY、LCD等等),每个类中再根据功能需求定义不同的调用/实现方法;将SHENZHOU3.pyi添加到KEIL工程进行编译(会报错缺少文件哦),此时会由PikaScript预编译会生成对应的API文件,即相应的模块实现,这是根据模块接口中的类定义来自动生成的,分别对应了多个模块实现(SHENZHOU3_LED-api.c、SHENZHOU3_KEY-api.c、SHENZHOU3_LCD-api.c等等):
将这些模块实现添加到KEIL工程中来,再编译一下,这个时候会报警,有很多函数没有定义:
这时我们就需要新建文件(SHENZHOU3_LED.c、SHENZHOU3_KEY.c、SHENZHOU3_LCD.c),来实现这些调用接口函数,在实现了这些功能之后,我们的Python基建就完成了。
下面看一下SHENZHOU3_LED的具体实现,是不是很简单,其它就是实现PikaScript自动生成的SHENZHOU3_LED-api的接口函数,完成对底层板载LED灯的操作:#include "SHENZHOU3_LED.h"
#include <stdint.h>
#include "BaseObj.h"
#include "dataStrs.h"
#include "config.h"
void SHENZHOU3_LED_Init(PikaObj* self)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOF, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_WriteBit(GPIOF, GPIO_Pin_6, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_7, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_8, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_9, Bit_SET);
}
void SHENZHOU3_LED_Toggle(PikaObj* self)
{
if(!obj_isArgExist(self, "ledState"))
{
obj_setInt(self, "ledState", 0);
}
int ledState = obj_getInt(self, "ledState");
if(0 == ledState)
{
GPIO_WriteBit(GPIOF, GPIO_Pin_6, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_7, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_8, Bit_SET);
GPIO_WriteBit(GPIOF, GPIO_Pin_9, Bit_SET);
}
else
{
GPIO_WriteBit(GPIOF, GPIO_Pin_6, Bit_RESET);
GPIO_WriteBit(GPIOF, GPIO_Pin_7, Bit_RESET);
GPIO_WriteBit(GPIOF, GPIO_Pin_8, Bit_RESET);
GPIO_WriteBit(GPIOF, GPIO_Pin_9, Bit_RESET);
}
obj_setInt(self, "ledState", (ledState + 1) % 2);
}
参考SHENZHOU3_LED的实现方法,结合SHENZHOU3_KEY-api和SHENZHOU3_LCD-api的所需要实现的接口函数,就可以轻松完善整个工程代码啦……
Python应用示例 通过编写Python应用程序,在神舟III号开发板上实现一个贪吃蛇的小程序功能示例:import PikaStdLib
import MM32F3270
import SHENZHOU3
snake_sx = 200
snake_sy = 50
snake_ex = 210
snake_ey = 60
snake_size = 10
snake_dir = 1
food_idx = 0
food_sx =
food_sy =
food_ex =
food_ey =
lcd = SHENZHOU3.LCD()
led = SHENZHOU3.LED()
key = SHENZHOU3.KEY()
lcd.Init()
led.Init()
key.Init()
mem = PikaStdLib.MemChecker()
print('Hello PikaScript ^_^')
print('memory used max:')
mem.max()
lcd.Clear(0)
lcd.ShowText(0,0, 'SZ-III(MM32):Hello PikaScript!')
lcd.FillArea(8,18, 232,19, 0x07E0)
lcd.FillArea(8,18, 9, 312, 0x07E0)
lcd.FillArea(231,18, 232, 312, 0x07E0)
lcd.FillArea(8, 311, 232, 312, 0x07E0)
lcd.FillArea(100, 100, 110, 110, 0x07E0)
def led_Task():
led.Toggle()
return
def key_Task():
global snake_dir
keyState = key.Read()
if keyState == 0:
snake_dir = 1
print('KEY WAKEUP : UP')
elif keyState == 1:
snake_dir = 2
print('KEY TAMPER : DOWN')
elif keyState == 2:
snake_dir = 3
print('KEY USER1: LEFT')
elif keyState == 3:
snake_dir = 4
print('KEY USER2: RIGHT')
return
def food_Run():
global snake_sx
global snake_sy
global snake_ex
global snake_ey
global snake_dir
global food_sx
global food_sy
global food_ex
global food_ey
global food_idx
sx = food_sx
sy = food_sy
ex = food_ex
ey = food_ey
food_eat = 0
if snake_dir == 1:
if snake_sx == sx:
if snake_sy == ey:
lcd.FillArea(sx, sy, ex, ey, 0xF800)
food_eat = 1
food_idx = food_idx + 1
elif snake_dir == 2:
if snake_sx == sx:
if snake_ey == sy:
lcd.FillArea(sx, sy, ex, ey, 0xF800)
food_eat = 1
food_idx = food_idx + 1
elif snake_dir == 3:
if snake_sy == sy:
if snake_sx == ex:
lcd.FillArea(sx, sy, ex, ey, 0xF800)
food_eat = 1
food_idx = food_idx + 1
elif snake_dir == 4:
if snake_sy == sy:
if snake_ex == sx:
lcd.FillArea(sx, sy, ex, ey, 0xF800)
food_eat = 1
food_idx = food_idx + 1
if food_idx == 10:
food_idx = 0
if food_eat == 1:
sx = food_sx
sy = food_sy
ex = food_ex
ey = food_ey
lcd.FillArea(sx, sy, ex, ey, 0x07E0)
return
def snake_Run():
global snake_sx
global snake_sy
global snake_ex
global snake_ey
global snake_size
global snake_dir
sx = snake_sx
sy = snake_sy
ex = snake_ex
ey = snake_ey
lcd.FillArea(sx, sy, ex, ey, 0x0000)
if snake_dir == 1:
if snake_sy > 20:
snake_sy = snake_sy - snake_size
else:
snake_dir = 4
snake_sx = snake_sx + snake_size
elif snake_dir == 2:
if snake_sy < 310 - snake_size:
snake_sy = snake_sy + snake_size
else:
snake_dir = 3
snake_sx = snake_sx - snake_size
elif snake_dir == 3:
if snake_sx > 10:
snake_sx = snake_sx - snake_size
else:
snake_dir = 1
snake_sy = snake_sy - snake_size
elif snake_dir == 4:
if snake_sx < 230 - snake_size:
snake_sx = snake_sx + snake_size
else:
snake_dir = 2
snake_sy = snake_sy + snake_size
else:
snake_dir = 0
if snake_sx< 10:
snake_sx = 10
if snake_sy< 20:
snake_sy = 20
if snake_sx> 230 - snake_size:
snake_sx = 230 - snake_size
if snake_sy> 310 - snake_size:
snake_sy = 310 - snake_size
snake_ex = snake_sx + snake_size
snake_ey = snake_sy + snake_size
sx = snake_sx
sy = snake_sy
ex = snake_ex
ey = snake_ey
food_Run()
lcd.FillArea(sx, sy, ex, ey, 0xF800)
return
task = MM32F3270.Task()
task.call_period_ms(led_Task,250)
task.call_period_ms(key_Task,50 )
task.call_period_ms(snake_Run, 500)
print('Task Running...')
task.run_forever()
下载运行、演示如下https://www.bilibili.com/video/BV1yt4y1t7ep/
附件Bootloader模版工程:贪吃蛇基建工程源码:贪吃蛇Python脚本程序: Python确实方便 这个确实不错,挺方便 python是不是一种上位机语言啊 python入门简单精通难啊 这种算法复杂吗 这个语言就是为嵌入式而生的吧 这种界面友好吗 这个语言和哪种比较类似啊 Python一直以为是PC端的 还能这样玩,学习了。 这种算法容易上手吗 这也能用在嵌入式?
页:
[1]