Hi3861 OpenHarmony 机械臂 (三)

这部分主要实现的是网络控制舵机。

前面写了一个网络应用,在网络应用的代码上加以修改增加舵机控制功能。

在写之前先添加一个模块,AT24C256存储模块。

因为无法获取舵机的实时角度,每次启动都要重新找位置,加一个存储模块将舵机的角度保存,需要时调出。

AT24C256分512页,每页64个字节,能存储32,768个字节。用I2C作为通信接口,1个I2C端口可以串联多个设备,用设备地址区分它们,对于端口少的小设备真的是非常友好。

AT24C256的读写非常简单,输入写指令 + 要写入的地址 +  写入数据,就可以写入,读出同理。

AT24C存储容量有很多型号,我就选一个价格差不多,容量最大的型号,AT24C256。

应为容量大一点,地址1个字节长度不够,这个模块的物理地址用2个字节表示。

 15bit的地址数据对其寻址,低6bit(D5-D0)为页内字节单元地址,高9bit(D14-D6)为页地址。

  | * 0 0 0 0 0 0 0 | 0 0 . 0 0 0 0 0 0 |


hi_void At24c256_I2C_Write_Byte(hi_u16 y, hi_u8 x, hi_u8 b)
{
    hi_u32 stat = 0;

    hi_u8 addr1 = 0xff;
    hi_u8 addr2 = 0xff;

    addr1 = addr1 & (y >> 2);
    addr2 = addr2 & (y << 6);
    addr2 = addr2 + x;

    hi_u8 send_buff[3] = {addr1, addr2, b};
    hi_i2c_data at24c256_i2c_data = {0};
    at24c256_i2c_data.send_buf = send_buff;
    at24c256_i2c_data.send_len = 3;

    stat = hi_i2c_write(HI_I2C_IDX_0, AT24C256_WRIT, &at24c256_i2c_data);

    hi_udelay(10 * 1000);

    if(stat != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Write_Byte] hi_i2c_write = Failed \n");
        return;
    }
}


hi_void At24c256_I2C_Read_Byte(hi_u16 y, hi_u8 x, hi_u8 *b)
{
    hi_u32 stat = 0;

    hi_u8 addr1 = 0xff;
    hi_u8 addr2 = 0xff;

    addr1 = addr1 & (y >> 2);
    addr2 = addr2 & (y << 6);
    addr2 = addr2 + x;

    hi_i2c_data at24c256_i2c_data = {0};

    hi_u8 send_buff[2] = {addr1, addr2};
    
    at24c256_i2c_data.send_buf = send_buff;
    at24c256_i2c_data.send_len = 2;

    stat = hi_i2c_write(HI_I2C_IDX_0, AT24C256_WRIT, &at24c256_i2c_data);

    hi_udelay(10 * 1000);

    if(stat != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Read_Byte] hi_i2c_write = Failed \n");
        return;
    }

    hi_u8 rece_buff[] = {0};

    at24c256_i2c_data.receive_buf = rece_buff;
    at24c256_i2c_data.receive_len = 1;

    stat = hi_i2c_read(HI_I2C_IDX_0, AT24C256_READ, &at24c256_i2c_data);
    if(stat != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Read_Byte] hi_i2c_read = Failed \n");
        return;
    }

    *b = rece_buff[0];
}

hi_void At24c256_I2C_Write_Data(hi_u16 y, hi_u8 x, hi_u8 *d, hi_u8 l)
{
    hi_u8 addr1 = 0xff;
    hi_u8 addr2 = 0xff;

    addr1 = addr1 & (y >> 2);
    addr2 = addr2 & (y << 6);
    addr2 = addr2 + x;

    hi_u8 send_data[l + 2];

    send_data[0] = addr1;
    send_data[1] = addr2;

    for(hi_u8 i=0; i<l; i++)
    {
        send_data[i+2] = d[i];
    }

    hi_i2c_data at24c256_i2c_data = {0};

    at24c256_i2c_data.send_buf = send_data;
    at24c256_i2c_data.send_len = l + 2;

    hi_u32 ret = hi_i2c_write(AT24C256_I2C_IDX, AT24C256_WRIT, &at24c256_i2c_data);

    hi_udelay(10 * 1000);

    if(ret != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Write_Data] hi_i2c_write = Failed \n");
        return;
    }
}

hi_void At24c256_I2C_Read_Data(hi_u16 y, hi_u8 x, hi_u8 *d, hi_u8 l)
{
    hi_u8 addr1 = 0xff;
    hi_u8 addr2 = 0xff;

    addr1 = addr1 & (y >> 2);
    addr2 = addr2 & (y << 6);
    addr2 = addr2 + x;

    hi_u8 send_data[2] = {0};

    send_data[0] = addr1;
    send_data[1] = addr2;

    hi_i2c_data at24c256_i2c_data = {0};

    at24c256_i2c_data.send_buf = send_data;
    at24c256_i2c_data.send_len = 2;

    hi_u32 ret = hi_i2c_write(AT24C256_I2C_IDX, AT24C256_WRIT, &at24c256_i2c_data);

    if(ret != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Read_Data] hi_i2c_write = Failed \n");
        return;
    }

    hi_u8 rece_data[l];

    at24c256_i2c_data.receive_buf = rece_data;
    at24c256_i2c_data.receive_len = l;

    ret = hi_i2c_read(AT24C256_I2C_IDX, AT24C256_READ, &at24c256_i2c_data);

    if(ret != HI_ERR_SUCCESS)
    {
        printf(" [At24c256_I2C_Read_Data] hi_i2c_read = Failed \n");
        return;
    }

    memcpy(d, rece_data, l);

}

比如向第10页第5位写入10个字节的数据,就是At24c256_I2C_Write_Data(10, 5, datas, 10);

网络控制跟遥控器控制是差不多的,在主循环里不断接收网络信息,有运行状态就运行舵机。

        if(net_stat == 1)
        {
            // 接收数据
            SE_Recv_Data();
        }

        if(Get_SE_State() == 1)
        {
            // 舵机 运行
            Engine_Run(2);
            Run_Wait(500 * 100);
        }

在舵机运行里加个参数,一次跨1度慢,跨多少根据需要设,2就是一次跨2度。

网络控制这个部分根据需要灵活控制。


// 发送数据
hi_u8 SE_Send_Data(hi_u8 i)
{
    memset(send_buff, 0, 20);
    send_buff[19] = i;
    SE_To_Bytes(&send_buff, se_ps[se_pi]);

    int ret = TCP_Send(client_sock, send_buff, 20);
    if(ret > 0)
    {
        return (hi_u8)ret;
    }
    if(ret == -1)
    {
        printf(" Send ERROR \n");
        return 0;
    }
}


// 接收数据
hi_u8 SE_Recv_Data(hi_void)
{
    int ret = TCP_Recv_Nonblocking(client_sock, recv_buff, 20);
    if(ret > 0)
    {
        Net_Control(recv_buff[17], recv_buff[18]);
        return (hi_u8)ret;
    }

    return 0;
}


// 网络控制
hi_u8 Net_Control(hi_u8 n, hi_u8 c)
{
    // 舵机号
    se_pi = n;

    // 接收数据
    if(c == 1)
    {
        printf("\n [1] Receive Data : \n");

        // 接收数组 转 舵机数据
        Bytes_To_SE(recv_buff, &se_ps[se_pi]);

        // 回传
        SE_Send_Data(1);
    }

    // 发送数据
    if(c == 2)
    {
        printf("\n [2] Send Data : \n");

        // 回传
        SE_Send_Data(2);
    }

    // 
    if(c == 3)
    {
        printf("\n [3] Read Data : \n");

        // 读取存储卡数据 转到 舵机数据
        memset(se_buff, 0, 20);
        At24c256_I2C_Read_Data(se_pi, 0, se_buff, 20);

        Bytes_To_SE(se_buff, &se_ps[se_pi]);

        // 回传
        SE_Send_Data(3);
    }

    if(c == 4)
    {
        printf("\n [4] Save Data : \n");

        // 将舵机数据 储存到存储卡
        memset(se_buff, 0, 20);
        SE_To_Bytes(&se_buff, se_ps[se_pi]);

        At24c256_I2C_Write_Data(se_pi, 0, se_buff, 20);

        // 回传
        SE_Send_Data(4);
    }

    if(c == 5)
    {
        printf("\n [5] Stop : \n");

        se_ps[se_pi].run = 0;

        SE_Send_Data(5);
    }

    if(c == 6)
    {
        printf("\n [6] Angel Init : \n");

        // 接收数组 转 舵机数据
        Bytes_To_SE(recv_buff, &se_ps[se_pi]);

        // 旋转舵机
        PCA9685_Angle(se_ps[se_pi].pin, se_ps[se_pi].ang, se_ps[se_pi].ran);
        PCA9685_Set_PWM(se_ps[se_pi].pin, 0, 0);

        SE_Send_Data(6);
    }

    if(c == 7)
    {
        printf("\n [7] Turn : \n");

        // 将 舵机数据 回传
        SE_Send_Data(7);
    }

    Display_SE_Data();
}

现在的控制内容主要还是以测试为主,控制功能监测、试舵机连接运行等等。写到这里也发现个问题,前面写的时候考虑后面的修改,代码环节太多,代码冗余太高,堆到一定程度,能感觉运行变得不可靠,所以后面定型重新整理一下。

写到这里想说一个我写代码的原则,第一遍代码是给自己看的,用人的思维方式写,方便自己看,面向过程。第二遍是给别人看的,因为过一段时间自己再修改,已经看不懂自己写的繁琐过程了,还要重新理顺一遍,这个时候代码考虑结构性,方便可读,能重用,容易修改,面向结构,最后一遍是给机器运行用的,删除冗余代码过程化,提高运行效率,面向设备。

网络控制写一个Window下的上位机实现,上位机用C#写,C#我是一点都没学过,以前写过Java,用C#感觉非常容易,当然没系统学过就是看工具提示写,代码肯定有很多不太合理。出于方便阅读用中文命名函数名,这个真的很好,用起来舒服多了。

运行效果

具体功能看代码吧,看不懂的可以发信息。

有很多朋友没有积分,我用百度网盘发一份。

链接: https://pan.baidu.com/s/17ApzDqpXmx5dMnvi388dQQ?pwd=xxni 提取码: xxni 
 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值