元驭机器人元驭机器人Hall 传感器教学下载原文件
1/*!2    \file    rs485_proto.c3    \brief   RS485 收发、参数保存和霍尔命令4*/56#include "rs485_proto.h"78#include "gd32e23x.h"9#include "hall_encoder.h"10#include "hall_line.h"11#include "systick.h"1213#include <stddef.h>14#include <string.h>1516#define RS485_PAYLOAD_MAX           32U17#define RS485_FRAME_MAX             (10U + RS485_PAYLOAD_MAX)18#define RS485_RX_SIZE               128U19#define RS485_RX_MASK               (RS485_RX_SIZE - 1U)20#define RS485_GAP_MS                20U21#define RS485_QUIET_MS              3U22#define RS485_REPORT_MIN_MS         5U23#define RS485_REPORT_MAX_MS         60000U24#define RS485_CFG_MAGIC             0x314C4C48UL25#define RS485_CFG_LAYOUT            1U26#define RS485_CFG_ADDR              0x08007C00UL2728/*!29    \brief      掉电保存的参数,放在最后一页 Flash30*/31typedef struct {32    uint32_t magic;33    uint16_t layout;34    uint16_t crc;35    uint8_t id;36    uint8_t mode;37    uint8_t baud_index;38    uint8_t cal_valid;39    uint16_t report_ms;40    uint16_t zero_cdeg;41    int16_t offset[3];42    uint16_t gain_q12[3];43    uint16_t profile;          /*!< 0 或 1 为角度,2 为直线。旧固件这里是 0 */44    int16_t line_zero_cmm;     /*!< 直线零点,0.01 mm。旧固件这里是 0 */45} rs485_cfg_t;4647_Static_assert(sizeof(rs485_cfg_t) == 32U, "rs485 config must be 32 bytes");48_Static_assert(offsetof(rs485_cfg_t, crc) == 6U, "crc follows magic and layout");49_Static_assert(offsetof(rs485_cfg_t, profile) == 28U, "profile stays in the old reserved slot");50_Static_assert((RS485_RX_SIZE & RS485_RX_MASK) == 0U, "rx ring is a power of two");5152static const uint32_t s_baud_table[RS485_BAUD_COUNT] = {53    9600UL, 19200UL, 38400UL, 57600UL, 115200UL, 230400UL, 460800UL, 921600UL54};5556static rs485_cfg_t s_cfg;57static volatile uint8_t s_rx[RS485_RX_SIZE];58static volatile uint8_t s_rx_w;59static volatile uint8_t s_rx_r;60static uint8_t s_asm[RS485_FRAME_MAX];61static uint8_t s_asm_len;62static uint32_t s_asm_ms;63static uint32_t s_last_rx_ms;64static uint32_t s_last_tx_ms;65static uint32_t s_report_ms;66static uint8_t s_report_seq;67static uint8_t s_reinit_uart;6869/*!70    \brief      CRC-16/MODBUS71    \param[in]  data: 数据72    \param[in]  len: 字节数73    \retval     校验值74*/75static uint16_t rs485_crc16(const uint8_t *data, uint16_t len)76{77    uint16_t crc = 0xFFFFU;78    uint16_t i;79    uint8_t bit;8081    for(i = 0U; i < len; i++) {82        crc ^= data[i];83        for(bit = 0U; bit < 8U; bit++) {84            if(0U != (crc & 0x0001U)) {85                crc = (uint16_t)((crc >> 1) ^ 0xA001U);86            } else {87                crc >>= 1;88            }89        }90    }91    return crc;92}9394/*!95    \brief      大约等待若干微秒,只用于方向脚翻转96    \param[in]  us: 微秒97    \retval     none98*/99static void rs485_delay_us(uint32_t us)100{101    uint32_t n = (SystemCoreClock / 4000000U) * us;102103    while(0U != n) {104        __NOP();105        n--;106    }107}108109static void rs485_put_u16(uint8_t *p, uint16_t value)110{111    p[0] = (uint8_t)value;112    p[1] = (uint8_t)(value >> 8);113}114115static uint16_t rs485_get_u16(const uint8_t *p)116{117    return (uint16_t)p[0] | ((uint16_t)p[1] << 8);118}119120static void rs485_put_i16(uint8_t *p, int32_t value)121{122    if(value > 32767) {123        value = 32767;124    }125    if(value < -32768) {126        value = -32768;127    }128    rs485_put_u16(p, (uint16_t)(int16_t)value);129}130131static void rs485_put_i32(uint8_t *p, int32_t value)132{133    p[0] = (uint8_t)value;134    p[1] = (uint8_t)((uint32_t)value >> 8);135    p[2] = (uint8_t)((uint32_t)value >> 16);136    p[3] = (uint8_t)((uint32_t)value >> 24);137}138139/*!140    \brief      恢复未写过 Flash 时的默认参数141    \retval     none142*/143static void rs485_cfg_defaults(void)144{145    memset(&s_cfg, 0, sizeof(s_cfg));146    s_cfg.magic = RS485_CFG_MAGIC;147    s_cfg.layout = RS485_CFG_LAYOUT;148    s_cfg.id = 1U;149    s_cfg.mode = RS485_MODE_POLL;150    s_cfg.baud_index = RS485_BAUD_115200;151    s_cfg.report_ms = 20U;152    s_cfg.gain_q12[0] = HALL_CAL_GAIN_ONE;153    s_cfg.gain_q12[1] = HALL_CAL_GAIN_ONE;154    s_cfg.gain_q12[2] = HALL_CAL_GAIN_ONE;155}156157/*!158    \brief      当前是不是直线位置模式159    \retval     1 直线,0 角度160*/161static uint8_t rs485_is_line(void)162{163    return (RS485_TYPE_HALL_POSITION == s_cfg.profile) ? 1U : 0U;164}165166/*!167    \brief      对外报告的设备类型。旧参数里的 0 仍是角度编码器168    \retval     RS485_TYPE_HALL_ENCODER 或 RS485_TYPE_HALL_POSITION169*/170static uint8_t rs485_device_type(void)171{172    if(0U != rs485_is_line()) {173        return RS485_TYPE_HALL_POSITION;174    }175    return RS485_TYPE_HALL_ENCODER;176}177178/*!179    \brief      把正在使用的零点和校准抄进参数,不动另一种模式的槽位180    \retval     none181*/182static void rs485_cfg_pull_hall(void)183{184    uint32_t i;185186    if(0U != rs485_is_line()) {187        int16_t baseline[3];188        uint8_t valid;189        uint16_t sigma;190        int16_t zero;191192        hall_line_get(baseline, &valid, &sigma, &zero);193        s_cfg.cal_valid = valid;194        s_cfg.line_zero_cmm = zero;195        s_cfg.gain_q12[0] = sigma;196        for(i = 0U; i < 3U; i++) {197            s_cfg.offset[i] = baseline[i];198        }199        return;200    }201202    {203        hall_cal_t cal;204205        hall_encoder_cal_get(&cal);206        s_cfg.cal_valid = cal.valid;207        s_cfg.zero_cdeg = hall_encoder_zero_get();208        for(i = 0U; i < 3U; i++) {209            s_cfg.offset[i] = cal.offset[i];210            s_cfg.gain_q12[i] = cal.gain_q12[i];211        }212    }213}214215/*!216    \brief      把参数装进当前模式。直线模式的宽度放在 gain_q12[0]217    \retval     none218*/219static void rs485_cfg_push_hall(void)220{221    uint32_t i;222223    if(0U != rs485_is_line()) {224        hall_line_apply(s_cfg.offset, s_cfg.cal_valid, s_cfg.gain_q12[0], s_cfg.line_zero_cmm);225        return;226    }227228    {229        hall_cal_t cal;230231        memset(&cal, 0, sizeof(cal));232        cal.valid = s_cfg.cal_valid;233        for(i = 0U; i < 3U; i++) {234            cal.offset[i] = s_cfg.offset[i];235            cal.gain_q12[i] = s_cfg.gain_q12[i];236            if(0U == cal.gain_q12[i]) {237                cal.gain_q12[i] = HALL_CAL_GAIN_ONE;238                cal.valid = 0U;239            }240        }241        hall_encoder_cal_apply(&cal);242        hall_encoder_zero_set(s_cfg.zero_cdeg);243    }244}245246/*!247    \brief      换成另一种测量,并清掉两边的零点和校准248    \param[in]  type: RS485_TYPE_HALL_ENCODER 或 RS485_TYPE_HALL_POSITION249    \retval     none250*/251static void rs485_enter_profile(uint8_t type)252{253    s_cfg.profile = (RS485_TYPE_HALL_POSITION == type) ? RS485_TYPE_HALL_POSITION : 0U;254    s_cfg.cal_valid = 0U;255    s_cfg.zero_cdeg = 0U;256    s_cfg.line_zero_cmm = 0;257    s_cfg.offset[0] = 0;258    s_cfg.offset[1] = 0;259    s_cfg.offset[2] = 0;260    hall_encoder_cal_clear();261    hall_encoder_zero_set(0U);262    hall_line_cal_clear();263    hall_line_zero_set(0);264    if(0U != rs485_is_line()) {265        s_cfg.gain_q12[0] = HALL_LINE_SIGMA_DEFAULT;266        s_cfg.gain_q12[1] = 0U;267        s_cfg.gain_q12[2] = 0U;268    } else {269        s_cfg.gain_q12[0] = HALL_CAL_GAIN_ONE;270        s_cfg.gain_q12[1] = HALL_CAL_GAIN_ONE;271        s_cfg.gain_q12[2] = HALL_CAL_GAIN_ONE;272    }273    rs485_cfg_push_hall();274}275276/*!277    \brief      参数是否能用278    \param[in]  cfg: Flash 里读出的副本279    \retval     1 可用,0 丢弃280*/281static uint8_t rs485_cfg_sane(const rs485_cfg_t *cfg)282{283    rs485_cfg_t img;284    uint16_t crc;285286    if((RS485_CFG_MAGIC != cfg->magic) || (RS485_CFG_LAYOUT != cfg->layout)) {287        return 0U;288    }289    if((0U == cfg->id) || (cfg->id >= RS485_ADDR_BCAST)) {290        return 0U;291    }292    if(cfg->mode > RS485_MODE_REPORT) {293        return 0U;294    }295    if(cfg->baud_index >= RS485_BAUD_COUNT) {296        return 0U;297    }298    if((cfg->report_ms < RS485_REPORT_MIN_MS) || (cfg->report_ms > RS485_REPORT_MAX_MS)) {299        return 0U;300    }301    if(cfg->zero_cdeg >= 36000U) {302        return 0U;303    }304    if((0U != cfg->profile) &&305            (RS485_TYPE_HALL_ENCODER != cfg->profile) &&306            (RS485_TYPE_HALL_POSITION != cfg->profile)) {307        return 0U;308    }309    img = *cfg;310    img.crc = 0U;311    crc = rs485_crc16((const uint8_t *)&img, (uint16_t)sizeof(img));312    return (crc == cfg->crc) ? 1U : 0U;313}314315/*!316    \brief      擦除并写入最后一页317    \retval     0 成功,-1 失败318*/319static int rs485_cfg_save(void)320{321    rs485_cfg_t img;322    const uint32_t *word;323    uint32_t primask;324    uint32_t i;325    int ok = 0;326327    rs485_cfg_pull_hall();328    img = s_cfg;329    img.magic = RS485_CFG_MAGIC;330    img.layout = RS485_CFG_LAYOUT;331    img.crc = 0U;332    img.crc = rs485_crc16((const uint8_t *)&img, (uint16_t)sizeof(img));333334    primask = __get_PRIMASK();335    __disable_irq();336    fmc_unlock();337    fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR | FMC_FLAG_PGAERR);338    if(FMC_READY == fmc_page_erase(RS485_CFG_ADDR)) {339        word = (const uint32_t *)&img;340        ok = 1;341        for(i = 0U; i < (sizeof(img) / 4U); i++) {342            if(FMC_READY != fmc_word_program(RS485_CFG_ADDR + (i * 4U), word[i])) {343                ok = 0;344                break;345            }346        }347    }348    fmc_lock();349    if(0U == primask) {350        __enable_irq();351    }352    if(0 != ok) {353        s_cfg.crc = img.crc;354        return 0;355    }356    return -1;357}358359/*!360    \brief      读 Flash,校验失败就用默认值,不立刻回写361    \retval     none362*/363static void rs485_cfg_load(void)364{365    rs485_cfg_t img;366367    memcpy(&img, (const void *)RS485_CFG_ADDR, sizeof(img));368    if(0U != rs485_cfg_sane(&img)) {369        s_cfg = img;370    } else {371        rs485_cfg_defaults();372    }373    rs485_cfg_push_hall();374}375376/*!377    \brief      切换波特率,引脚和中断保持不变378    \param[in]  baud: bit/s379    \retval     none380*/381static void rs485_uart_baud(uint32_t baud)382{383    usart_disable(USART0);384    usart_baudrate_set(USART0, baud);385    usart_enable(USART0);386    usart_interrupt_enable(USART0, USART_INT_RBNE);387}388389/*!390    \brief      配置 PA9/PA10 和 USART0391    \retval     none392*/393static void rs485_uart_init(void)394{395    rcu_periph_clock_enable(RCU_GPIOA);396    rcu_periph_clock_enable(RCU_GPIOB);397    rcu_periph_clock_enable(RCU_USART0);398    rcu_usart_clock_config(RCU_USART0SRC_CKAPB2);399400    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5);401    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_5);402    gpio_bit_reset(GPIOB, GPIO_PIN_5);403404    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9 | GPIO_PIN_10);405    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9 | GPIO_PIN_10);406    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9 | GPIO_PIN_10);407408    usart_deinit(USART0);409    usart_baudrate_set(USART0, s_baud_table[s_cfg.baud_index]);410    usart_word_length_set(USART0, USART_WL_8BIT);411    usart_stop_bit_set(USART0, USART_STB_1BIT);412    usart_parity_config(USART0, USART_PM_NONE);413    usart_receive_config(USART0, USART_RECEIVE_ENABLE);414    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);415    usart_enable(USART0);416    nvic_irq_enable(USART0_IRQn, 2U);417    usart_interrupt_enable(USART0, USART_INT_RBNE);418}419420/*!421    \brief      中断里写入接收环422    \param[in]  byte: 收到的字节423    \retval     none424*/425static void rs485_rx_push(uint8_t byte)426{427    uint8_t next = (uint8_t)((s_rx_w + 1U) & RS485_RX_MASK);428429    if(next != s_rx_r) {430        s_rx[s_rx_w] = byte;431        s_rx_w = next;432    }433}434435/*!436    \brief      等到标志置位,避免时钟异常时卡死437    \param[in]  flag: USART 标志438    \retval     0 等到了,-1 超时439*/440static int rs485_wait_flag(usart_flag_enum flag)441{442    uint32_t n = SystemCoreClock / 20U;443444    while(RESET == usart_flag_get(USART0, flag)) {445        if(0U == n) {446            return -1;447        }448        n--;449    }450    return 0;451}452453/*!454    \brief      拉高方向脚,发出一帧,再回到接收455    \param[in]  data: 整帧456    \param[in]  len: 字节数457    \retval     none458*/459static void rs485_send_raw(const uint8_t *data, uint8_t len)460{461    uint8_t i;462463    gpio_bit_set(GPIOB, GPIO_PIN_5);464    rs485_delay_us(50U);465    for(i = 0U; i < len; i++) {466        if(0 != rs485_wait_flag(USART_FLAG_TBE)) {467            break;468        }469        usart_data_transmit(USART0, data[i]);470    }471    if(0 == rs485_wait_flag(USART_FLAG_TBE)) {472        usart_flag_clear(USART0, USART_FLAG_TC);473        (void)rs485_wait_flag(USART_FLAG_TC);474    }475    rs485_delay_us(50U);476    gpio_bit_reset(GPIOB, GPIO_PIN_5);477    s_last_tx_ms = systick_get_ms();478}479480/*!481    \brief      组帧并发送。源地址固定为本机设备号482    \param[in]  dst: 目标483    \param[in]  cmd: 命令484    \param[in]  seq: 序号485    \param[in]  payload: 载荷,长度 0 时可以为空486    \param[in]  len: 载荷长度487    \retval     none488*/489static void rs485_send_frame(uint8_t dst, uint8_t cmd, uint8_t seq,490                             const uint8_t *payload, uint8_t len)491{492    uint8_t buf[RS485_FRAME_MAX];493    uint16_t crc;494    uint8_t i;495496    if(len > RS485_PAYLOAD_MAX) {497        return;498    }499    buf[0] = RS485_SOF0;500    buf[1] = RS485_SOF1;501    buf[2] = dst;502    buf[3] = s_cfg.id;503    buf[4] = rs485_device_type();504    buf[5] = cmd;505    buf[6] = seq;506    buf[7] = len;507    for(i = 0U; i < len; i++) {508        buf[8U + i] = payload[i];509    }510    crc = rs485_crc16(&buf[2], (uint16_t)(6U + len));511    buf[8U + len] = (uint8_t)crc;512    buf[9U + len] = (uint8_t)(crc >> 8);513    rs485_send_raw(buf, (uint8_t)(10U + len));514}515516/*!517    \brief      打包一帧角度,13 字节518    \param[out] p: 至少 13 字节519    \param[in]  sample: 解算结果520    \retval     none521*/522static void rs485_pack_sample(uint8_t *p, const hall_sample_t *sample)523{524    rs485_put_i32(&p[0], sample->angle_cdeg);525    rs485_put_i16(&p[4], sample->amplitude);526    rs485_put_u16(&p[6], sample->raw[0]);527    rs485_put_u16(&p[8], sample->raw[1]);528    rs485_put_u16(&p[10], sample->raw[2]);529    if(0U != rs485_is_line()) {530        p[12] = hall_line_flags(sample);531    } else {532        p[12] = hall_encoder_flags(sample);533    }534}535536/*!537    \brief      读当前模式的一帧采样538    \param[out] sample: 角度或直线位置539    \retval     none540*/541static void rs485_sense_read(hall_sample_t *sample)542{543    if(0U != rs485_is_line()) {544        hall_line_read(sample);545    } else {546        hall_encoder_read(sample);547    }548}549550/*!551    \brief      把当前模式的校准填进应答结构552    直线模式的 gain_q12[0] 是敏感宽度,单位 µm。553    \param[out] cal: 偏置或基线,以及增益或宽度554    \retval     none555*/556static void rs485_export_cal(hall_cal_t *cal)557{558    uint32_t i;559560    memset(cal, 0, sizeof(*cal));561    if(0U != rs485_is_line()) {562        int16_t baseline[3];563        uint8_t valid;564        uint16_t sigma;565        int16_t zero;566567        hall_line_get(baseline, &valid, &sigma, &zero);568        (void)zero;569        cal->valid = valid;570        cal->gain_q12[0] = sigma;571        for(i = 0U; i < 3U; i++) {572            cal->offset[i] = baseline[i];573        }574        return;575    }576    hall_encoder_cal_get(cal);577}578579/*!580    \brief      执行一条已经通过校验的命令581    \param[in]  frame: 从 SOF 开始的整帧582    \retval     1 已经回答,0 没有回答583*/584static uint8_t rs485_handle(const uint8_t *frame)585{586    uint8_t dst = frame[2];587    uint8_t src = frame[3];588    uint8_t type = frame[4];589    uint8_t cmd = frame[5];590    uint8_t seq = frame[6];591    uint8_t len = frame[7];592    const uint8_t *in = &frame[8];593    uint8_t pay[24];594    uint8_t plen = 1U;595    uint8_t status = RS485_OK;596    uint8_t reply;597    uint8_t old_baud;598    uint8_t old_id;599    uint16_t period;600    uint16_t zero;601    hall_sample_t sample;602    hall_cal_t cal;603    uint8_t cal_rc;604    uint32_t i;605606    if((RS485_TYPE_ANY != type) && (rs485_device_type() != type)) {607        return 0U;608    }609    if((dst != s_cfg.id) && (RS485_ADDR_BCAST != dst)) {610        return 0U;611    }612    if(0U != (cmd & RS485_CMD_RESPONSE)) {613        return 0U;614    }615616    reply = (RS485_ADDR_BCAST == dst) ? 0U : 1U;617    if((0U == reply) &&618            ((RS485_CMD_GET_INFO == cmd) || (RS485_CMD_GET_DATA == cmd) ||619             (RS485_CMD_GET_CAL == cmd) || (RS485_CMD_SET_ID == cmd) ||620             (RS485_CMD_SET_BAUD == cmd) || (RS485_CMD_FACTORY == cmd))) {621        return 0U;622    }623624    memset(pay, 0, sizeof(pay));625    switch(cmd) {626    case RS485_CMD_GET_INFO:627        if(0U != len) {628            status = RS485_ERR_LEN;629            break;630        }631        rs485_sense_read(&sample);632        rs485_export_cal(&cal);633        pay[1] = rs485_device_type();634        pay[2] = s_cfg.id;635        pay[3] = s_cfg.mode;636        pay[4] = s_cfg.baud_index;637        rs485_put_u16(&pay[5], s_cfg.report_ms);638        rs485_put_u16(&pay[7], RS485_FW_VERSION);639        pay[9] = cal.valid;640        if(0U != rs485_is_line()) {641            pay[10] = hall_line_flags(&sample);642            rs485_put_i16(&pay[11], hall_line_zero_get());643        } else {644            pay[10] = hall_encoder_flags(&sample);645            rs485_put_u16(&pay[11], hall_encoder_zero_get());646        }647        plen = 13U;648        break;649650    case RS485_CMD_SET_ID:651        if(1U != len) {652            status = RS485_ERR_LEN;653            break;654        }655        if((0U == in[0]) || (in[0] >= RS485_ADDR_BCAST)) {656            status = RS485_ERR_PARAM;657            break;658        }659        old_id = s_cfg.id;660        s_cfg.id = in[0];661        if(0 != rs485_cfg_save()) {662            s_cfg.id = old_id;663            status = RS485_ERR_FLASH;664            break;665        }666        pay[1] = s_cfg.id;667        plen = 2U;668        break;669670    case RS485_CMD_SET_BAUD:671        if(1U != len) {672            status = RS485_ERR_LEN;673            break;674        }675        if(in[0] >= RS485_BAUD_COUNT) {676            status = RS485_ERR_PARAM;677            break;678        }679        old_baud = s_cfg.baud_index;680        s_cfg.baud_index = in[0];681        if(0 != rs485_cfg_save()) {682            s_cfg.baud_index = old_baud;683            status = RS485_ERR_FLASH;684            break;685        }686        if(old_baud != s_cfg.baud_index) {687            s_reinit_uart = 1U;688        }689        pay[1] = s_cfg.baud_index;690        plen = 2U;691        break;692693    case RS485_CMD_SET_MODE: {694        uint8_t old_mode = s_cfg.mode;695        uint16_t old_period = s_cfg.report_ms;696697        if(3U != len) {698            status = RS485_ERR_LEN;699            break;700        }701        period = rs485_get_u16(&in[1]);702        if((in[0] > RS485_MODE_REPORT) ||703                (period < RS485_REPORT_MIN_MS) || (period > RS485_REPORT_MAX_MS)) {704            status = RS485_ERR_PARAM;705            break;706        }707        s_cfg.mode = in[0];708        s_cfg.report_ms = period;709        if(0 != rs485_cfg_save()) {710            s_cfg.mode = old_mode;711            s_cfg.report_ms = old_period;712            status = RS485_ERR_FLASH;713            break;714        }715        pay[1] = s_cfg.mode;716        rs485_put_u16(&pay[2], s_cfg.report_ms);717        plen = 4U;718        break;719    }720721    case RS485_CMD_SAVE:722        if(0U != len) {723            status = RS485_ERR_LEN;724            break;725        }726        if(0 != rs485_cfg_save()) {727            status = RS485_ERR_FLASH;728        }729        break;730731    case RS485_CMD_FACTORY: {732        rs485_cfg_t old = s_cfg;733734        if(0U != len) {735            status = RS485_ERR_LEN;736            break;737        }738        hall_encoder_cal_clear();739        hall_encoder_zero_set(0U);740        hall_line_cal_clear();741        hall_line_zero_set(0);742        rs485_cfg_defaults();743        if(0 != rs485_cfg_save()) {744            s_cfg = old;745            rs485_cfg_push_hall();746            status = RS485_ERR_FLASH;747            break;748        }749        if(old.baud_index != s_cfg.baud_index) {750            s_reinit_uart = 1U;751        }752        pay[1] = s_cfg.id;753        pay[2] = s_cfg.baud_index;754        plen = 3U;755        break;756    }757758    case RS485_CMD_GET_DATA:759        if(0U != len) {760            status = RS485_ERR_LEN;761            break;762        }763        rs485_sense_read(&sample);764        rs485_pack_sample(&pay[1], &sample);765        plen = 14U;766        break;767768    case RS485_CMD_SET_ZERO:769        if((0U != len) && (2U != len)) {770            status = RS485_ERR_LEN;771            break;772        }773        if(0U != rs485_is_line()) {774            int16_t old_zero = hall_line_zero_get();775776            if(2U == len) {777                hall_line_zero_set((int16_t)rs485_get_u16(in));778            } else {779                hall_line_zero_capture();780            }781            if(0 != rs485_cfg_save()) {782                hall_line_zero_set(old_zero);783                status = RS485_ERR_FLASH;784                break;785            }786            rs485_put_i16(&pay[1], hall_line_zero_get());787            plen = 3U;788            break;789        }790        zero = hall_encoder_zero_get();791        if(2U == len) {792            period = rs485_get_u16(in);793            if(period >= 36000U) {794                status = RS485_ERR_PARAM;795                break;796            }797            hall_encoder_zero_set(period);798        } else {799            hall_encoder_zero_capture();800        }801        if(0 != rs485_cfg_save()) {802            hall_encoder_zero_set(zero);803            status = RS485_ERR_FLASH;804            break;805        }806        rs485_put_u16(&pay[1], hall_encoder_zero_get());807        plen = 3U;808        break;809810    case RS485_CMD_CLEAR_ZERO:811        if(0U != len) {812            status = RS485_ERR_LEN;813            break;814        }815        if(0U != rs485_is_line()) {816            hall_line_zero_set(0);817        } else {818            hall_encoder_zero_set(0U);819        }820        if(0 != rs485_cfg_save()) {821            status = RS485_ERR_FLASH;822            break;823        }824        break;825826    case RS485_CMD_CAL_BEGIN:827        if(0U != len) {828            status = RS485_ERR_LEN;829            break;830        }831        if(0U != rs485_is_line()) {832            hall_line_cal_begin();833        } else {834            hall_encoder_cal_begin();835        }836        break;837838    case RS485_CMD_CAL_END:839        if(0U != len) {840            status = RS485_ERR_LEN;841            break;842        }843        if(0U != rs485_is_line()) {844            cal_rc = hall_line_cal_finish();845        } else {846            cal_rc = hall_encoder_cal_finish();847        }848        if(HALL_CAL_IDLE == cal_rc) {849            status = RS485_ERR_STATE;850            break;851        }852        if(HALL_CAL_WEAK == cal_rc) {853            status = RS485_ERR_CAL;854            break;855        }856        if(0 != rs485_cfg_save()) {857            status = RS485_ERR_FLASH;858            break;859        }860        rs485_export_cal(&cal);861        for(i = 0U; i < 3U; i++) {862            rs485_put_i16(&pay[1U + (i * 2U)], cal.offset[i]);863            rs485_put_u16(&pay[7U + (i * 2U)], cal.gain_q12[i]);864        }865        plen = 13U;866        break;867868    case RS485_CMD_CAL_CLEAR:869        if(0U != len) {870            status = RS485_ERR_LEN;871            break;872        }873        if(0U != rs485_is_line()) {874            hall_line_cal_clear();875        } else {876            hall_encoder_cal_clear();877        }878        if(0 != rs485_cfg_save()) {879            status = RS485_ERR_FLASH;880        }881        break;882883    case RS485_CMD_GET_CAL:884        if(0U != len) {885            status = RS485_ERR_LEN;886            break;887        }888        rs485_sense_read(&sample);889        rs485_export_cal(&cal);890        if(0U != rs485_is_line()) {891            pay[1] = hall_line_flags(&sample);892        } else {893            pay[1] = hall_encoder_flags(&sample);894        }895        for(i = 0U; i < 3U; i++) {896            rs485_put_i16(&pay[2U + (i * 2U)], cal.offset[i]);897            rs485_put_u16(&pay[8U + (i * 2U)], cal.gain_q12[i]);898        }899        plen = 14U;900        break;901902    case RS485_CMD_SET_PROFILE: {903        rs485_cfg_t old;904905        if(1U != len) {906            status = RS485_ERR_LEN;907            break;908        }909        if((RS485_TYPE_HALL_ENCODER != in[0]) && (RS485_TYPE_HALL_POSITION != in[0])) {910            status = RS485_ERR_PARAM;911            break;912        }913        if(rs485_device_type() == in[0]) {914            pay[1] = rs485_device_type();915            plen = 2U;916            break;917        }918        old = s_cfg;919        rs485_enter_profile(in[0]);920        if(0 != rs485_cfg_save()) {921            s_cfg = old;922            rs485_cfg_push_hall();923            status = RS485_ERR_FLASH;924            break;925        }926        pay[1] = rs485_device_type();927        plen = 2U;928        break;929    }930931    case RS485_CMD_SET_SIGMA: {932        uint16_t sigma;933        uint16_t old_sigma;934935        if(0U == rs485_is_line()) {936            status = RS485_ERR_STATE;937            break;938        }939        if(2U != len) {940            status = RS485_ERR_LEN;941            break;942        }943        sigma = rs485_get_u16(in);944        if((sigma < HALL_LINE_SIGMA_MIN) || (sigma > HALL_LINE_SIGMA_MAX)) {945            status = RS485_ERR_PARAM;946            break;947        }948        old_sigma = s_cfg.gain_q12[0];949        s_cfg.gain_q12[0] = sigma;950        hall_line_sigma_set(sigma);951        if(0 != rs485_cfg_save()) {952            s_cfg.gain_q12[0] = old_sigma;953            hall_line_sigma_set(old_sigma);954            status = RS485_ERR_FLASH;955            break;956        }957        rs485_put_u16(&pay[1], s_cfg.gain_q12[0]);958        plen = 3U;959        break;960    }961962    default:963        status = RS485_ERR_CMD;964        break;965    }966967    pay[0] = status;968    if(0U == reply) {969        s_reinit_uart = 0U;970        return 0U;971    }972    rs485_send_frame(src, (uint8_t)(cmd | RS485_CMD_RESPONSE), seq, pay, plen);973    if(0U != s_reinit_uart) {974        s_reinit_uart = 0U;975        rs485_uart_baud(s_baud_table[s_cfg.baud_index]);976    }977    return 1U;978}979980/*!981    \brief      收一个字节,凑齐一帧就执行982    \param[in]  byte: 新字节983    \retval     1 已回答984*/985static uint8_t rs485_asm_push(uint8_t byte)986{987    uint8_t plen;988    uint16_t crc;989    uint16_t got;990991    if(0U == s_asm_len) {992        if(RS485_SOF0 != byte) {993            return 0U;994        }995        s_asm[0] = byte;996        s_asm_len = 1U;997        return 0U;998    }999    if(1U == s_asm_len) {1000        if(RS485_SOF1 != byte) {1001            s_asm_len = 0U;1002            if(RS485_SOF0 == byte) {1003                s_asm[0] = byte;1004                s_asm_len = 1U;1005            }1006            return 0U;1007        }1008    }1009    if(s_asm_len >= RS485_FRAME_MAX) {1010        s_asm_len = 0U;1011        return 0U;1012    }1013    s_asm[s_asm_len] = byte;1014    s_asm_len++;1015    if(s_asm_len < 8U) {1016        return 0U;1017    }1018    plen = s_asm[7];1019    if(plen > RS485_PAYLOAD_MAX) {1020        s_asm_len = 0U;1021        return 0U;1022    }1023    if(s_asm_len < (uint8_t)(10U + plen)) {1024        return 0U;1025    }1026    crc = rs485_crc16(&s_asm[2], (uint16_t)(6U + plen));1027    got = (uint16_t)s_asm[8U + plen] | ((uint16_t)s_asm[9U + plen] << 8);1028    s_asm_len = 0U;1029    if(crc != got) {1030        return 0U;1031    }1032    return rs485_handle(s_asm);1033}10341035/*!1036    \brief      把接收环里的字节解成命令1037    \retval     1 本轮回答过1038*/1039static uint8_t rs485_service_rx(void)1040{1041    uint8_t replied = 0U;1042    uint32_t now = systick_get_ms();10431044    if((0U != s_asm_len) && ((now - s_asm_ms) > RS485_GAP_MS)) {1045        s_asm_len = 0U;1046    }1047    while(s_rx_r != s_rx_w) {1048        uint8_t byte = s_rx[s_rx_r];10491050        s_rx_r = (uint8_t)((s_rx_r + 1U) & RS485_RX_MASK);1051        s_asm_ms = now;1052        s_last_rx_ms = now;1053        if(0U != rs485_asm_push(byte)) {1054            replied = 1U;1055        }1056    }1057    return replied;1058}10591060/*!1061    \brief      主动上报模式到点后发一帧角度1062    \param[in]  replied: 本轮已经回答过查询1063    \retval     none1064*/1065static void rs485_try_report(uint8_t replied)1066{1067    hall_sample_t sample;1068    uint8_t payload[13];1069    uint32_t now;10701071    if(0U != replied) {1072        return;1073    }1074    if(RS485_MODE_REPORT != s_cfg.mode) {1075        return;1076    }1077    if(0U != s_asm_len) {1078        return;1079    }1080    now = systick_get_ms();1081    if((now - s_last_rx_ms) < RS485_QUIET_MS) {1082        return;1083    }1084    if((now - s_last_tx_ms) < RS485_QUIET_MS) {1085        return;1086    }1087    if((now - s_report_ms) < s_cfg.report_ms) {1088        return;1089    }1090    s_report_ms = now;1091    rs485_sense_read(&sample);1092    rs485_pack_sample(payload, &sample);1093    rs485_send_frame(RS485_ADDR_HOST, RS485_CMD_REPORT, s_report_seq, payload, 13U);1094    s_report_seq++;1095}10961097void rs485_usart_isr(void)1098{1099    if(SET == usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {1100        rs485_rx_push((uint8_t)usart_data_receive(USART0));1101    }1102    if(SET == usart_flag_get(USART0, USART_FLAG_ORERR)) {1103        usart_flag_clear(USART0, USART_FLAG_ORERR);1104    }1105    if(SET == usart_flag_get(USART0, USART_FLAG_FERR)) {1106        usart_flag_clear(USART0, USART_FLAG_FERR);1107    }1108    if(SET == usart_flag_get(USART0, USART_FLAG_NERR)) {1109        usart_flag_clear(USART0, USART_FLAG_NERR);1110    }1111}11121113void rs485_proto_init(void)1114{1115    uint32_t now;11161117    rs485_cfg_load();1118    rs485_uart_init();1119    now = systick_get_ms();1120    s_report_ms = now;1121    s_last_tx_ms = now;1122    s_last_rx_ms = now;1123}11241125void rs485_proto_poll(void)1126{1127    uint8_t replied = rs485_service_rx();11281129    rs485_try_report(replied);1130}11311132uint8_t rs485_proto_id(void)1133{1134    return s_cfg.id;1135}11361137uint32_t rs485_proto_baud(void)1138{1139    return s_baud_table[s_cfg.baud_index];1140}11411142uint8_t rs485_proto_mode(void)1143{1144    return s_cfg.mode;1145}11461147uint8_t rs485_proto_type(void)1148{1149    return rs485_device_type();1150}