SDL2库使用及QEMU添加虚拟开发板--Apple的学习笔记

一,前言

上次blogQemu虚化HW相关—Apple的学习笔记已经分析了要修改qemu源码自定义开发板其实就是要看懂graphic.c,而里面用到的就是SDL2和其扩展库SDL2_Image扩展库,而我之前已经搭建了SLD2的mingw开发环境,并且在官网了解了SDL2的API看懂了graphic.c那么就开始修改为自定义开发板。

二,在SDL2中实现开发板LED闪烁界面

效果为按下鼠标后,右下角的LED灯颜色会变。


image.png

codeblocks源码如下:

#include 
#include 
#include 
//Screen dimension constants
const int SCREEN_WIDTH = 346;
const int SCREEN_HEIGHT = 452;

typedef struct {
    SDL_Renderer *renderer;
    SDL_Window *window;
} App;
App app;

typedef struct {
    int x;
    int y;
    SDL_Texture *texture;
} Entity;
Entity player;

unsigned int click=0;

void prepareScene(void)
{
    #if 0
    if(click == 1)
    {
        SDL_SetRenderDrawColor(app.renderer, 255, 128, 0, 255);
    }
    else
    {
        SDL_SetRenderDrawColor(app.renderer, 96, 128, 255, 255);
    }
    SDL_RenderClear(app.renderer);
    #endif // 0
    if(click == 1)
    {
        SDL_SetRenderDrawColor(app.renderer, 255, 128, 0, 255);
    }
    else
    {
        SDL_SetRenderDrawColor(app.renderer, 0, 255, 0, 255);
    }
    SDL_Rect rectangle;

    rectangle.x = 318;
    rectangle.y = 378;
    rectangle.w = 12;
    rectangle.h = 6;
    SDL_RenderFillRect(app.renderer, &rectangle);
}

void presentScene(void)
{
    SDL_RenderPresent(app.renderer);
}

void initSDL(void)
{
    int rendererFlags, windowFlags;

    rendererFlags = SDL_RENDERER_ACCELERATED;

    windowFlags = 0;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("Couldn't initialize SDL: %s\n", SDL_GetError());
        exit(1);
    }

    app.window = SDL_CreateWindow("Apple's STM32 EK-board", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, windowFlags);

    if (!app.window)
    {
        printf("Failed to open %d x %d window: %s\n", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_GetError());
        exit(1);
    }

    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");

    app.renderer = SDL_CreateRenderer(app.window, -1, rendererFlags);

    if (!app.renderer)
    {
        printf("Failed to create renderer: %s\n", SDL_GetError());
        exit(1);
    }

    IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG);
}

void cleanup(void)
{
    SDL_DestroyRenderer(app.renderer);
    SDL_DestroyWindow(app.window);
    SDL_Quit();
}

void doInput(void)
{
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                exit(0);
                break;
            case SDL_MOUSEBUTTONUP:
                click = !click;
                break;
            default:
                break;
        }
    }
}


int main(int argc, char *argv[])
{
    memset(&app, 0, sizeof(App));
    memset(&player, 0, sizeof(Entity));

    initSDL();

    SDL_Surface* board_bitmap = IMG_Load("pic/AppleSTM32.png");
    if (board_bitmap == NULL) {
        printf("Cannot load image file '%s' (%s).\n",
                "pic/player.png",
                IMG_GetError());
        exit(1);
    }
    SDL_Surface* board_bitmap_rgb = SDL_ConvertSurfaceFormat(board_bitmap,SDL_PIXELFORMAT_RGB888, 0);
    if (board_bitmap_rgb == 0) {
        printf("Could not create surface: %s\n", SDL_GetError());
        exit(1);
    }
    player.texture = SDL_CreateTextureFromSurface(app.renderer,board_bitmap_rgb);
    if (player.texture == NULL) {
        printf("Could not create texture: %s\n", SDL_GetError());
        exit(1);
    }
    SDL_RenderClear(app.renderer);
    SDL_RenderCopy(app.renderer, player.texture, NULL, NULL);
    SDL_RenderPresent(app.renderer);
    SDL_FreeSurface(board_bitmap);

    atexit(cleanup);

    while (1)
    {
        prepareScene();
        doInput();
        presentScene();
        SDL_Delay(16);
    }
    return 0;
}

三,修改qemu中的开发板图片

boards-discovery.c中修改LED的rect,修改要打开的图片

//修改1,LED0位置
static GPIOLEDInfo stm32f429i_discovery_leds_info[] = {
    {
        .name = "led:green",
        .active_low = false,
        .colour_name = "green",
        .x = 486,//519,
        .y = 574,//109,
        .w = 12,//10,
        .h = 8,//8,
        .gpio_path = DEVICE_PATH_STM32_GPIO_G,
        .irq_name = STM32_IRQ_GPIO_ODR_OUT,
        .gpio_bit = 13,
//修改2,背景开发板图片
    BoardGraphicContext *board_graphic_context =cortexm_board_init_graphic_image(board, "AppleSTM32.png");
            //cortexm_board_init_graphic_image(board, "STM32F429I-Discovery.jpg");
//修改3,窗口台头名称
mc->desc = "Apple Cai's STM32F429ZG board";//"ST Discovery kit for STM32F429/439 lines";

qemu运行我的stm32开发板效果,右下角D1处为LED灯。


image.png

四,qemu添加自定义虚拟开发板

刚刚在第三个步骤中,只是在原来的开发板基础上修改了图片。至于开发板的IO口等还是原来的。所以第四步是要在qemu中添加自定义开发板的仿真代码,而不破坏原来的开发板仿真代码。
关于如何添加开发板,要看我最早的一篇qemu源码分析,主要是调试了vl.c了解其框架及传参的作用,大家可以参考Qemu2.8虚拟机源码分析—Apple的学习笔记和qemu虚化原理入门--Apple的学习笔记,在qemu中添加一个开发板还是比较简单的。参考其它hw中的board.c文件依样画葫芦即可。我在stm32_machines_init中注册了type_register_static(&appleSTM32F429ZG_machine)设置了board名字为AppleSTM32。添加代码如下:

/* ----- AppleCai's STM32F429ZG board ----- */
static GPIOLEDInfo appleSTM32_leds_info[] = {
    {
        .name = "led:green",
        .active_low = false,
        .colour_name = "green",
        .x = 486,
        .y = 574,
        .w = 12,
        .h = 8,
        .gpio_path = DEVICE_PATH_STM32_GPIO_F,
        .irq_name = STM32_IRQ_GPIO_ODR_OUT,
        .gpio_bit = 9,
    /**/
    },
    {
        .name = "led:red",
        .active_low = false,
        .colour_name = "red",
        .x = 486,
        .y = 584,
        .w = 10,
        .h = 8,
        .gpio_path = DEVICE_PATH_STM32_GPIO_F,
        .irq_name = STM32_IRQ_GPIO_ODR_OUT,
        .gpio_bit = 10,
    /**/
    },
    { },
/**/
};
static void appleSTM32F4_board_init_callback(MachineState *machine)
{
    CortexMBoardState *board = CORTEXM_BOARD_STATE(machine);

    cortexm_board_greeting(board);
    BoardGraphicContext *board_graphic_context =cortexm_board_init_graphic_image(board, "AppleSTM32.png");
    {
        // Create the MCU
        Object *mcu = cm_object_new_mcu(machine, TYPE_STM32F429ZI);

        // Set the board specific oscillator frequencies.
        cm_object_property_set_int(mcu, 8000000, "hse-freq-hz"); // 8.0 MHz
        cm_object_property_set_int(mcu, 32768, "lse-freq-hz"); // 32 kHz

        cm_object_realize(mcu);
    }

    Object *peripheral = cm_container_get_peripheral();
    // Create board LEDs.
    gpio_led_create_from_info(peripheral, appleSTM32_leds_info,
            board_graphic_context);
}

static void appleSTM32_discovery_init_callback(ObjectClass *oc,void *data);

static const TypeInfo appleSTM32F429ZG_machine = {
    .name = BOARD_TYPE_NAME("AppleSTM32"),
    .parent = TYPE_CORTEXM_BOARD,
    .class_init = appleSTM32_discovery_init_callback
};

static void appleSTM32_discovery_init_callback(ObjectClass *oc,void *data)
{
    MachineClass *mc = MACHINE_CLASS(oc);
    mc->desc = "Apple Cai's STM32F429ZG board";
    mc->init = appleSTM32F4_board_init_callback;
}

编译通过后,查看Apple cai'sboard已经添加成功

image.png

接着修改了LED工程的引脚为PF9和PF10。原来的是PG14和PG14,直接运行LED灯是不会亮的。LED工程make通过后,结合qemu进行仿真。记得传入参数--board AppleSTM32这样才可以调用我添加的函数。输入仿真命令
qemu-system-gnuarmeclipse --verbose --verbose --board AppleSTM32 --mcu STM32F429ZI -d unimp,guest_errors --image led.elf
led工程中我设置了D1和D2两个LED灯轮流闪烁。效果如下,自定义最小开发板完工。
image.png

五,总结

通过化繁为简的方法,进行分步学习,逐渐熟悉qemu源码框架及其子系统,并且掌握qemu自定义开发板和外设的方法。真的好好玩啊~

你可能感兴趣的:(SDL2库使用及QEMU添加虚拟开发板--Apple的学习笔记)