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}