Hi3861 : 使用ssd1306玩贪吃蛇

发布时间 2023-10-11 18:40:59作者: memo2586

练手写了个贪吃蛇玩玩(

屏幕驱动库:ssd1306 · 连志安/3861智能家居套件代码仓库 - 码云 - 开源中国 (gitee.com)

user键开始,S1向左,S2向右

#include <string.h>
#include <stdio.h>
#include <time.h>
#include "ssd1306.h"
#include "ssd1306_tests.h"
#include "link.h"

#include <unistd.h>
#include <hi_types_base.h>
#include <hi_io.h>
#include <hi_early_debug.h>
#include <hi_gpio.h>
#include <hi_task.h>
#include "ohos_init.h"
#include "cmsis_os2.h"

#include <hi_adc.h>
#include <hi_stdlib.h>
#include <hi_early_debug.h>

#define KEY_EVENT_NONE 0
#define KEY_EVENT_S1 1
#define KEY_EVENT_S2 2
#define KEY_EVENT_S3 3
#define KEY_EVENT_S4 4
#define ADC_TEST_LENGTH 64
#define VLT_MIN 100

//--------------------------------------------------------------------------------//

hi_u16 g_adc_buf[ADC_TEST_LENGTH] = {0};

int key_status = KEY_EVENT_NONE;
char key_flg = 0;

int get_key_event(void){
    int tmp = key_status;
    key_status = KEY_EVENT_NONE;
    return tmp;
}

hi_void convert_to_voltage(hi_u32 data_len){

    float vlt_max = 0;
    float vlt_min = VLT_MIN;
    float vlt_val = 0;          //均值
    hi_u16 vlt;

    for(hi_u32 i=0;i<data_len;i++){
        vlt = g_adc_buf[i];
        float voltage = (float)vlt * 1.8 * 4 /4096.0;       //码字转化电压值
        vlt_max = (voltage > vlt_max) ? voltage : vlt_max;
        vlt_min = (voltage < vlt_min) ? voltage : vlt_min;
    }

    //output
    // printf("vlt_min: %.3f , vlt_max: %.3f \n", vlt_min,vlt_max);

    vlt_val = (vlt_max + vlt_min) / 2.0;

    //根据电压识别按钮

    if(vlt_val > 0.4 && vlt_val < 0.6){     //S1
        if(key_flg == 0){
            key_flg = 1;
            key_status = KEY_EVENT_S1;
        }
    }
    if(vlt_val > 0.8 && vlt_val < 1.1){     //S2
        if(key_flg == 0){
            key_flg = 1;
            key_status = KEY_EVENT_S2;
        }
    }
    if(vlt_val>0.01 && vlt_val < 0.3){      //USER
        if(key_flg == 0){
            key_flg = 1;
            key_status = KEY_EVENT_S3;
        }
    }
    if(vlt_val > 3.0){                      //NONE
        key_flg = 0;
        key_status = KEY_EVENT_NONE;
    }
}

void adc_read(void){
    hi_u16 data;

    //清空数组
    memset_s(g_adc_buf, sizeof(g_adc_buf), 0x0, sizeof(g_adc_buf));

    //循环检查ADC数值
    for(hi_u32 i=0;i<ADC_TEST_LENGTH;i++){
        //读取adc数值,存在地址data
        hi_u32 ret = hi_adc_read(
            (hi_adc_channel_index)HI_ADC_CHANNEL_2,
            &data,
            HI_ADC_EQU_MODEL_1,
            HI_ADC_CUR_BAIS_DEFAULT,
            0
        );
        //失败检查
        if(ret != HI_ERR_SUCCESS){
            printf("ADC read fail !\n");
            return;
        }

        g_adc_buf[i] = data;
    }

    convert_to_voltage(ADC_TEST_LENGTH);
}

void key_Init(){
  hi_u32 ret;

  (hi_void)hi_gpio_init();

  //设置GPIO5复用功能
  hi_io_set_func(HI_IO_NAME_GPIO_5, HI_IO_FUNC_GPIO_5_GPIO);

  //设置GPIO5方向
  ret = hi_gpio_set_dir(HI_GPIO_IDX_5, HI_GPIO_DIR_IN);

  if(ret != HI_ERR_SUCCESS){
    printf("===== ERROR ====== gpio -> hi_gpio_set_dir1 ret:%d\r\n", ret);
    return;
  }
}

void gameover(){
  ssd1306_Fill(Black);
  ssd1306_SetCursor(38, 28);
  ssd1306_DrawString("Game Over!", Font_7x10, White);
  ssd1306_UpdateScreen();
}

void init_map(void){
  ssd1306_Fill(Black);
  //上下边界
  for(int i=0;i<=127;i++){
    ssd1306_DrawPixel(i,0,White);
    ssd1306_DrawPixel(i,63,White);
  }
  //左右边界
  for(int i=1;i<63;i++){
    ssd1306_DrawPixel(0,i,White);
    ssd1306_DrawPixel(127,i,White);
  }
  ssd1306_UpdateScreen();
}

//检查是否在链表中
bool check(PNODE head, int x, int y){
  PNODE p = head->pNext;
  while (p != NULL)
  {
    if(p->x==x && p->y==y) return 1;
    p = p->pNext;
  }
  return 0;
}

//随机数
void random(int* x,int* y)
{
  time_t ts = time(NULL);
  srand(ts);
  *x=rand()%127+3;
  srand(ts*ts);
  *y=rand()%63+3;
}

void snake(void){

  //蛇与地图初始化
  init_map();
  bool start = 0;
  int speed_key = 0;
  int item_x=20, item_y=10;   //初始化道具位置
  ssd1306_DrawPixel(item_x, item_y, White);
  int mo[][2] = {1,0,0,1,-1,0,0,-1};
  int sp[] = {5000,2500,1250,750,350};
  PNODE head = create_list();
  insert_list(head, 2, 9, 10);
  insert_list(head, 3, 8, 10);
  insert_list(head, 4, 7, 10);
  insert_list(head, 5, 6, 10);
  insert_list(head, 6, 5, 10);

  PNODE p = head->pNext;
  while (p != NULL)
  {
    ssd1306_DrawPixel(p->x, p->y, White);
    p = p->pNext;
  }
  ssd1306_UpdateScreen();

  int res=0;

  while(1){
    //按钮控制
    //读取ADC数值
    adc_read();

    //按键判断与输出
    switch(get_key_event()){
      case KEY_EVENT_NONE:
      {}
      break;

      case KEY_EVENT_S1:
      {
        res--;
        res=(res+4)%4;
      }
      break;

      case KEY_EVENT_S2:
      {
        res++;
        res%=4;
      }
      break;

      case KEY_EVENT_S3:
      {
        start=1;
        speed_key++;
        speed_key%=5;
      }
      break;
    }

    if(!start){
      usleep(1);
      continue;
    }

    int x = head->pNext->x, y = head->pNext->y;   //头部坐标
    int hx=x+mo[res][0], hy=y+mo[res][1];   //头部新坐标
    
    //检查头部是否越界或撞到自身
    if(hx<0 || hx>127 || hy<0 || hy>63 || check(head, hx, hy)){
      usleep(500000);
      return;
    }

    // 头部前进1
    insert_list(head, 1, hx, hy);
    ssd1306_DrawPixel(hx, hy, White);

    //是否吃到道具
    if(hx==item_x && hy==item_y){
      //更新道具位置
      while(check(head, item_x, item_y)){
        random(&item_x, &item_y);
      }
      ssd1306_DrawPixel(item_x, item_y, White);
    }
    else{
      //没吃到道具则删除尾部
      int px,py;    //尾部坐标
      delete_list(head, length_list(head), &px, &py);
      ssd1306_DrawPixel(px, py, Black);
    }
    
    // 刷新
    ssd1306_UpdateScreen();
    usleep(sp[speed_key]);

    // traverse_list(head);
  }
}


void ssd1306_sanke() {

  ssd1306_Init();
  key_Init();

  while(1){
    snake();
    gameover();
    HAL_Delay(2000);
  }
}