Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
// 11/2015 Rene Hopf<renehopf@mac.com> and Ian McMahon<facetious.ian@gmail.com>
//using CMD_TX
//using CMD_RX
HAL_COMP(sserial);
//dump:
//sigrok-cli --driver fx2lafw --config samplerate=24m --time 1s --channels 3=rx,2=tx -t rx=f -o sserial.sr
//decode:
//sigrok-cli -i sserial.sr -P uart:baudrate=2500000:tx=tx:rx=rx:format=hex -A uart=tx-data:rx-data
//decode more:
//sigrok-cli -i sserial.sr -P uart:baudrate=2500000:tx=tx:rx=rx,sserial
#define UnitNumberRPC 0xbc
#define DiscoveryRPC 0xbb
#define ProcessDataRPC 0xbd
//local commands
#define LBPCookieCMD 0xdf
#define LBPCookie 0x5a
#define LBPStatusCMD 0xc1
#define LBPCardName "stbl"
#define LBPCardName0Cmd 0xd0
#define LBPCardName1Cmd 0xd1
#define LBPCardName2Cmd 0xd2
#define LBPCardName3Cmd 0xd3
#define CT_RW 1 // 01b
#define CT_RPC 2 // 10b
#define CT_LOCAL 3 // 11b
typedef union{
struct {
uint8_t ds:2;//DataSize: Specifies data size, 00b = 1 bytes, 01b = 2 bytes, 10 b= 4 bytes, 11b = 8 bytes.
uint8_t as:1;//AddressSize: 0 to specify current address, 1 to specify 2 byte address.
uint8_t ai:1;//AutoInc: 0 leaves address unchanged, 1 specifies that address is post incremented by data size in bytes.
uint8_t rid:1;//RPCIncludesData: 0 specifies that data is from stream, 1, that data is from RPC (RPC only, ignored for non RPC commands)
uint8_t wr:1;//Write: 1 to specify write, 0 to specify read
uint8_t ct:2;//CommandType: Must be 01b to specify data read/write command
// 01:rw 11:local command 10:rpc
};
struct {
uint8_t rpc:6;
uint8_t dummy:2;
//01:rw 11: local command 10:rpc
};
uint8_t byte;
} lbp_t;
#define MEM_SIZE 2048
#define RECORD_TYPE_PROCESS_DATA_RECORD 0xA0
#define RECORD_TYPE_MODE_DATA_RECORD 0xB0
//process data
#define DATA_TYPE_PAD 0x00
#define DATA_TYPE_BITS 0x01
#define DATA_TYPE_UNSIGNED 0x02
#define DATA_TYPE_SIGNED 0x03
#define DATA_TYPE_NONVOL_UNSIGNED 0x04
#define DATA_TYPE_NONVOL_SIGNED 0x05
#define DATA_TYPE_NONVOL_STREAM 0x06
#define DATA_TYPE_NONVOL_BOOLEAN 0x07
#define DATA_DIRECTION_INPUT 0x00
#define DATA_DIRECTION_BI_DIRECTIONAL 0x40
#define DATA_DIRECTION_OUTPUT 0x80
#define MEMPTR(p) ((uint32_t)&p-(uint32_t)&memory)
#define MEMU8(ptr) (memory.bytes[ptr])
#define MEMU16(ptr) (memory.bytes[ptr] | memory.bytes[ptr+1]<<8)
#define MEMU32(ptr) (memory.bytes[ptr] | memory.bytes[ptr+1]<<8 | memory.bytes[ptr+2]<<16 | memory.bytes[ptr+3]<<24)
#define MEMFLOAT(ptr) ((float)({uint32_t tmp = MEMU32(ptr);*((float*)&tmp);}))
#define NUM_BYTES(bits) (bits / 8 + (bits % 8 > 0 ? 1 : 0))
#define IS_INPUT(pdr) (pdr->data_direction != 0x80)
#define IS_OUTPUT(pdr) (pdr->data_direction != 0x00)
#define SIGNED(pdr) (pdr->data_type == DATA_TYPE_SIGNED || pdr->data_type == DATA_TYPE_NONVOL_SIGNED)
#define UNSIGNED(pdr) (pdr->data_type == DATA_TYPE_UNSIGNED || pdr->data_type == DATA_TYPE_NONVOL_UNSIGNED)
// pins
HAL_PIN(dump_pd_vals) = 0.0;
HAL_PIN(error) = 0.0;//counts unknown commands
HAL_PIN(crc_error) = 0.0;//counts crc errors
HAL_PIN(connected) = 0.0;//connection status TODO: not stable during startup, needs link to pd
HAL_PIN(timeout) = 100.0;// 20khz / 1khz * 2 reads = 40
HAL_PIN(pos_cmd) = 0.0;
HAL_PIN(pos_fb) = 0.0;
HAL_PIN(vel_fb) = 0.0;
HAL_PIN(in0) = 0.0;
HAL_PIN(in1) = 0.0;
HAL_PIN(in2) = 0.0;
HAL_PIN(in3) = 0.0;
HAL_PIN(out0) = 0.0;
HAL_PIN(out1) = 0.0;
HAL_PIN(out2) = 0.0;
HAL_PIN(out3) = 0.0;
typedef struct{
uint8_t record_type;//0xa0
uint8_t data_size;
uint8_t data_type;
uint8_t data_direction;
float param_min;
float param_max;
uint16_t data_addr;
char names;
} process_data_descriptor_t;
typedef struct{
uint8_t record_type;//0xb0
uint8_t index;
uint8_t type;
uint8_t unused;
char names;
} mode_descriptor_t;
typedef struct{
uint8_t input; //process data input bytes
uint8_t output; //process data output bytes
uint16_t ptocp; //pointer to process data table start
uint16_t gtocp; //pointer to mode data table start
} discovery_rpc_t;
typedef union {
struct {
discovery_rpc_t discovery;
uint16_t foo;
uint8_t heap[MEM_SIZE - sizeof(discovery_rpc_t)];
};
uint8_t bytes[MEM_SIZE];
} memory_t;
typedef struct {
process_data_descriptor_t *ptr;
float range;
uint32_t bitmax;
} pd_metadata_t;
typedef struct {
pd_metadata_t pos_cmd;
pd_metadata_t scale;
pd_metadata_t pos_fb;
pd_metadata_t vel_fb;
pd_metadata_t input_pins;
pd_metadata_t output_pins;
} pd_table_t;
MEM(volatile uint8_t rxbuf[128]);
MEM(volatile uint8_t txbuf[20]);
MEM(uint16_t address);//current address pointer
MEM(int rxpos);
MEM(memory_t memory);
MEM(uint8_t *heap_ptr);
MEM(uint32_t timeout);
MEM(pd_table_t pd_table);
//pb13 txen
//pc12 usart5 tx
//pa9 usart1 tx as rx
void init_hardware() {
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
//USART TX
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//USART RX
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitStruct.USART_BaudRate = 2500000;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx;
USART_Init(USART1, &USART_InitStruct);
USART_HalfDuplexCmd(USART1,ENABLE);
USART_InitStruct.USART_Mode = USART_Mode_Tx;
USART_Init(UART5, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
USART_Cmd(UART5, ENABLE);
//RX DMA
DMA_Cmd(DMA2_Stream5, DISABLE);
DMA_DeInit(DMA2_Stream5);
// DMA2-Config
DMA_InitStructure.DMA_Channel = DMA_Channel_4;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&rxbuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = sizeof(rxbuf);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream5, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
//TX DMA
DMA_Cmd(DMA1_Stream7, DISABLE);
DMA_DeInit(DMA1_Stream7);
// DMA2-Config
DMA_InitStructure.DMA_Channel = DMA_Channel_4;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(UART5->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&txbuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = sizeof(txbuf);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Priority_Low;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream7, &DMA_InitStructure);
//DMA_Cmd(DMA1_Stream7, ENABLE);
USART_DMACmd(UART5, USART_DMAReq_Tx, ENABLE);
GPIO_SetBits(GPIOB, GPIO_Pin_13);//tx enable
}
process_data_descriptor_t create_pdr(uint8_t data_size_in_bits, uint8_t data_type, uint8_t data_dir, float param_min, float param_max) {
process_data_descriptor_t pd;
pd.record_type = RECORD_TYPE_PROCESS_DATA_RECORD;
pd.data_size = data_size_in_bits;
pd.data_type = data_type;
pd.data_direction = data_dir;
pd.param_min = param_min;
pd.param_max = param_max;
return pd;
}
mode_descriptor_t create_mdr(uint8_t index, uint8_t type) {
mode_descriptor_t md;
md.record_type = RECORD_TYPE_MODE_DATA_RECORD;
md.index = index;
md.type = type;
md.unused = 0x00;
return md;
}
uint16_t add_pd(char *name_string, char *unit_string, uint8_t data_size_in_bits, uint8_t data_type, uint8_t data_dir, float param_min, float param_max) {
process_data_descriptor_t pdr = create_pdr(data_size_in_bits, data_type, data_dir, param_min, param_max);
pdr.data_addr = MEMPTR(*heap_ptr);
heap_ptr += NUM_BYTES(data_size_in_bits);
// this aligns the heap pointer to 32bit. Not doing this causes the floats in the pd to be misaligned, which crashes the arm.
if((uint32_t)heap_ptr % 4){
heap_ptr += 4 - (uint32_t)heap_ptr % 4;
}
memcpy(heap_ptr, &pdr, sizeof(process_data_descriptor_t));
// note that we don't store the names in the struct anymore. The fixed-length struct is copied into memory, and then the nmaes go in directly behind it, so they'll read out properly
uint16_t pd_ptr = MEMPTR(*heap_ptr); // save off the ptr to return, before we modify the heap ptr
heap_ptr = (uint8_t *)&(((process_data_descriptor_t *)heap_ptr)->names);
// copy the strings in after the pd
strcpy((char *)heap_ptr, unit_string);
heap_ptr += strlen(unit_string)+1;
strcpy((char *)heap_ptr, name_string);
heap_ptr += strlen(name_string)+1;
// moved this up to before the pd record
/*
// this aligns the heap pointer to 32bit. Not doing this causes the floats in the pd to be misaligned, which crashes the arm.
if((uint32_t)heap_ptr % 4){
heap_ptr += 4 - (uint32_t)heap_ptr % 4;
}
*/
return pd_ptr;
}
uint16_t add_mode(char *name_string, uint8_t index, uint8_t type) {
mode_descriptor_t mdr = create_mdr(index, type);
memcpy(heap_ptr, &mdr, sizeof(mode_descriptor_t));
uint16_t md_ptr = MEMPTR(*heap_ptr);
heap_ptr = (uint8_t *)&(((mode_descriptor_t *)heap_ptr)->names);
strcpy((char *)heap_ptr, name_string);
heap_ptr += strlen(name_string)+1;
return md_ptr;
}
#define INDIRECT_PD(pd_ptr) ((process_data_descriptor_t *)(memory.bytes + *pd_ptr))
#define DATA_DIR(pd_ptr) INDIRECT_PD(pd_ptr)->data_direction
#define DATA_SIZE(pd_ptr) INDIRECT_PD(pd_ptr)->data_size
#define ADD_PROCESS_VAR(args) *ptocp = add_pd args; input_bits += IS_INPUT(INDIRECT_PD(ptocp)) ? DATA_SIZE(ptocp) : 0; output_bits += IS_OUTPUT(INDIRECT_PD(ptocp)) ? DATA_SIZE(ptocp) : 0; last_pd = INDIRECT_PD(ptocp++)
#define ADD_GLOBAL_VAR(args) *gtocp++ = add_pd args
#define ADD_MODE(args) *gtocp++ = add_mode args
void metadata(pd_metadata_t *pdm, process_data_descriptor_t *ptr) {
pdm->ptr = ptr;
pdm->range = ptr->data_type == DATA_TYPE_SIGNED ? MAX(ptr->param_min, ptr->param_max)*2 : ptr->param_max;
pdm->bitmax = (1<<ptr->data_size)-1;
}
RT_INIT(
init_hardware();
rxpos = 0;
heap_ptr = memory.heap;
uint16_t input_bits = 8; // this starts at 8 bits = 1 byte for the fault byte
uint16_t output_bits = 0;
// these are temp toc arrays that the macros will write pointers into. the tocs get copied to main memory after everything else is written in
uint16_t ptoc[32];
uint16_t gtoc[32];
uint16_t *ptocp = ptoc; uint16_t *gtocp = gtoc;
process_data_descriptor_t *last_pd;
ADD_PROCESS_VAR(("output_pins", "none", 2, DATA_TYPE_BITS, DATA_DIRECTION_OUTPUT, 0, 1)); metadata(&(pd_table.output_pins), last_pd);
ADD_PROCESS_VAR(("pos_cmd", "rad", 16, DATA_TYPE_SIGNED, DATA_DIRECTION_OUTPUT, -3.2, 3.2)); metadata(&(pd_table.pos_cmd), last_pd);
ADD_PROCESS_VAR(("input_pins", "none", 2, DATA_TYPE_BITS, DATA_DIRECTION_INPUT, -100, 100)); metadata(&(pd_table.input_pins), last_pd);
ADD_PROCESS_VAR(("pos_fb", "rad", 16, DATA_TYPE_SIGNED, DATA_DIRECTION_INPUT, -3.2, 3.2)); metadata(&(pd_table.pos_fb), last_pd);
//ADD_PROCESS_VAR(("input_pins", "none", 1, DATA_TYPE_BITS, DATA_DIRECTION_INPUT, 0, 1)); metadata(&(pd_table.input_pins), last_pd);
ADD_GLOBAL_VAR(("swr", "non", 8, DATA_TYPE_UNSIGNED, DATA_DIRECTION_OUTPUT, 0, 0));
ADD_MODE(("foo", 0, 0));
ADD_MODE(("io_", 1, 1));
// automatically create padding pds based on the mod remainder of input/output bits
if (input_bits % 8) ADD_PROCESS_VAR(("", "", 8 - (input_bits%8), DATA_TYPE_PAD, DATA_DIRECTION_INPUT, 0, 0));
if (output_bits % 8) ADD_PROCESS_VAR(("", "", 8 - (output_bits%8), DATA_TYPE_PAD, DATA_DIRECTION_OUTPUT, 0, 0));
// now that all the toc entries have been added, write out the tocs to memory and set up the toc pointers
memory.discovery.input = input_bits >> 3;
memory.discovery.output = output_bits >> 3;
memory.discovery.ptocp = MEMPTR(*heap_ptr);
for(uint8_t i = 0; i < ptocp - ptoc; i++) {
*heap_ptr++ = ptoc[i] & 0x00FF;
*heap_ptr++ = (ptoc[i] & 0xFF00) >> 8;
}
*heap_ptr++ = 0x00; *heap_ptr++ = 0x00; // this is the ptoc end marker
memory.discovery.gtocp = MEMPTR(*heap_ptr);
for(uint8_t i = 0; i < gtocp - gtoc; i++) {
*heap_ptr++ = gtoc[i] & 0x00FF;
*heap_ptr++ = (gtoc[i] & 0xFF00) >> 8;
}
*heap_ptr++ = 0x00; *heap_ptr++ = 0x00; // this is the gtoc end marker
timeout = 1000;
);
uint8_t crc_reuest(uint8_t len) {
uint8_t crc = crc8_init();
for(int i = rxpos; i < rxpos+len; i++){
crc = crc8_update(crc, (void*)&(rxbuf[i%sizeof(rxbuf)]), 1);
}
crc8_finalize(crc);
return crc == rxbuf[(rxpos+len)%sizeof(rxbuf)];
}
uint8_t crc8( uint8_t *addr, uint8_t len) {
uint8_t crc = crc8_init();
crc = crc8_update(crc, addr, len);
return crc8_finalize(crc);
}
void send(uint8_t len, uint8_t docrc){
timeout = 0;
if(docrc){
txbuf[len] = crc8((uint8_t *)txbuf,len);
DMA_SetCurrDataCounter(DMA1_Stream7,len+1);
}else{
DMA_SetCurrDataCounter(DMA1_Stream7,len);
}
DMA_Cmd(DMA1_Stream7, DISABLE);
DMA_ClearFlag(DMA1_Stream7, DMA_FLAG_TCIF7);
DMA_Cmd(DMA1_Stream7, ENABLE);
}
#define BITSLEFT(ptr) (8-ptr)
void process_data_rpc(uint8_t fault, volatile uint8_t *input, volatile uint8_t *output) {
uint16_t *ptocp = (uint16_t *)(memory.bytes + memory.discovery.ptocp);
uint32_t local_rxpos = rxpos;
*(input++) = fault;
*input = 0x00;
// data needs to be packed and unpacked based on its type and size
// input is a pointer to the data that gets sent back to the host
// need a bit pointer to keep track of partials
uint8_t output_bit_ptr = 0;
uint8_t input_bit_ptr = 0;
while(*ptocp != 0x0000) {
process_data_descriptor_t *pd = (process_data_descriptor_t *)(memory.bytes + *ptocp++);
if (IS_INPUT(pd)) {
uint16_t data_addr = pd->data_addr;
uint8_t data_size = pd->data_size;
uint8_t data_bit_ptr = 0;
while(data_size > 0) {
uint8_t bits_to_pack = data_size < BITSLEFT(input_bit_ptr) ? data_size : BITSLEFT(input_bit_ptr);
if (BITSLEFT(data_bit_ptr) < bits_to_pack) { bits_to_pack = BITSLEFT(data_bit_ptr); }
uint8_t mask = ((1<<bits_to_pack) - 1) << (data_bit_ptr);
*input |= ((MEMU8(data_addr) & mask) >> data_bit_ptr) << input_bit_ptr;
input_bit_ptr += bits_to_pack;
data_bit_ptr += bits_to_pack;
data_size -= bits_to_pack;
if((input_bit_ptr %= 8) == 0) *(++input) = 0x00; // make sure we clear the input buffer whenever we increment bytes
if((data_bit_ptr %= 8) == 0) data_addr++;
}
}
if (IS_OUTPUT(pd)) {
uint16_t data_addr = pd->data_addr;
uint8_t data_size = pd->data_size;
uint8_t val_bits_remaining = 8;
uint8_t val = 0x00;
while(data_size > 0) {
// the number of bits to unpack this iteration is the number of bits remaining in the pd, or the number of bits remaining in the output byte,
// whichever is smaller. Then, it can be even smaller if we have less room in the current val.
uint8_t bits_to_unpack = data_size < BITSLEFT(output_bit_ptr) ? data_size : BITSLEFT(output_bit_ptr);
if (val_bits_remaining < bits_to_unpack) { bits_to_unpack = val_bits_remaining; }
// create a bitmask the width of the bits to read, shifted to the position in the output byte that we're pointing to
uint8_t mask = ((1<<bits_to_unpack) - 1) << (output_bit_ptr);
// val is what we get when we mask off output and then shift it to the proper place.
val = val | ((rxbuf[(local_rxpos+1)%sizeof(rxbuf)] & mask) >> (output_bit_ptr)) << (8-val_bits_remaining);
val_bits_remaining -= bits_to_unpack;
data_size -= bits_to_unpack;
output_bit_ptr += bits_to_unpack;
// rxpos is a ringbuf and wraps around
// note: this replaces the output argument
if((output_bit_ptr %= 8) == 0){
local_rxpos++;
local_rxpos = local_rxpos % sizeof(rxbuf);
}
if(val_bits_remaining == 0 || data_size == 0) {
MEMU8(data_addr++) = val;
val_bits_remaining = 8;
val = 0x00;
}
}
// now we've finished unpacking it and storing it in memory, but we have to fix up the high bits if it wasn't a byte-aligned datasize.
// for instance, if we receive 0xFFF in a 12 bit field, that is a negative number, but we stored it as 0x0FFF in memory.
// strategy is to set the most significant n bits of the MSB to the most significant bit of the output value, iff the pd is defined as signed.
if (SIGNED(pd) && pd->data_size % 8 != 0) {
//printf("in output fixup. data_addr %h data_size %i num_bytes %i\n", pd->data_addr, pd->data_size, NUM_BYTES(pd->data_size));
uint8_t msb_addr = pd->data_addr + NUM_BYTES(pd->data_size) - 1;
//printf("in output fixup. MSB (at %h): %h\n", msb_addr, MEMU8(msb_addr));
// these two masks use data_size%8, this is the number of bits in the most significant byte, and since we tested for %8!=0 above, we know it's a partial byte
if(MEMU8(msb_addr) & 1<<(pd->data_size%8 - 1)) { // this test uses a mask that is 1 in the most significant bit position, we only need to fixup the val if it's 1 (ie negative)
// this mask is all the unused high bits set
uint8_t mask = 0xFF ^ ((1<<pd->data_size%8) - 1);
//printf("applying mask: %h\n", mask);
MEMU8(msb_addr) |= mask;
}
//printf("fixed up val: %h\n", MEMU8(msb_addr));
}
}
}
}
float scale_out(pd_metadata_t pd, int32_t val) {
return val * pd.range / (float)pd.bitmax;
}
int32_t scale_in(pd_metadata_t pd, float val) {
return CLAMP(val, pd.ptr->param_min, pd.ptr->param_max) * pd.bitmax / pd.range;
}
#define BOOLPIN(pin) (uint8_t)(PIN(pin) != 0.0)
RT (
// update all hal pins with values from their associated pds
PIN(pos_cmd) = scale_out(pd_table.pos_cmd, (int16_t)(MEMU16(pd_table.pos_cmd.ptr->data_addr)));
uint8_t outpins = MEMU8(pd_table.output_pins.ptr->data_addr);
PIN(out0) = outpins & 1 ? 1.0 : 0.0;
PIN(out1) = outpins & 1 ? 1.0 : 0.0;
PIN(out2) = outpins & 1 ? 1.0 : 0.0;
PIN(out3) = outpins & 1 ? 1.0 : 0.0;
// todo: how to handle bidirectional pins properly?
*((uint16_t *)&(memory.bytes[pd_table.pos_fb.ptr->data_addr])) = (uint16_t)scale_in(pd_table.pos_fb, PIN(pos_fb));
MEMU8(pd_table.input_pins.ptr->data_addr) = BOOLPIN(in0)<<0 | BOOLPIN(in1)<<1 | BOOLPIN(in2)<<2 | BOOLPIN(in3)<<3;
);
FRT(
lbp_t lbp;
char name[] = LBPCardName;
int bufferpos;
int available;
for(int j = 0;j<2;j++){
//next received packet will be written to bufferpos
bufferpos = sizeof(rxbuf) - DMA_GetCurrDataCounter(DMA2_Stream5);
//how many packets we have the the rx buffer for processing
available = (bufferpos - rxpos + sizeof(rxbuf)) % sizeof(rxbuf);
if (available >= 1) {
lbp.byte = rxbuf[rxpos];
if (lbp.ct == CT_LOCAL && lbp.wr == 0) { //local read, cmd+crc = 2b
if(available >= 2){
switch(lbp.byte) {
case LBPCookieCMD:
txbuf[0] = LBPCookie;
break;
case LBPStatusCMD: //TODO: return status
txbuf[0] = 0x00;
break;
case LBPCardName0Cmd ... LBPCardName3Cmd:
txbuf[0] = name[lbp.byte-LBPCardName0Cmd];
break;
default: //TODO: handle unknown command condition
txbuf[0] = 0x00;
}
send(1,1);
rxpos += 2;
}else{
continue;
}
}else if(lbp.ct == CT_LOCAL && lbp.wr == 1){//local write, cmd+data+crc = 3b
//0xFF and 0xFC are not followed by crc
if(rxbuf[rxpos] == 0xFF){
// reset parser
timeout = 0;
rxpos += 1;
}else if(rxbuf[rxpos] == 0xFC){
// todo
timeout = 0;
rxpos += 1;
}else if(available >= 3){//writes do not expect crc in reply
txbuf[0] = 0x00;
send(1,0);
rxpos += 3;
}else{
continue;
}
}else if(lbp.ct == CT_RPC){//RPC
if(lbp.byte == UnitNumberRPC && available >= 2){//unit number, cmd+crc = 2b
txbuf[0] = 0x76;
txbuf[1] = 0x01;
txbuf[2] = 0x00;
txbuf[3] = 0x10;
send(4,1);
rxpos += 2;
}else if(lbp.byte == DiscoveryRPC && available >= 2){//discovery, cmd+crc = 2b
memcpy((void*)txbuf,((uint8_t*)&memory.discovery),sizeof(memory.discovery));
send(sizeof(memory.discovery),1);
rxpos += 2;
}else if(lbp.byte == ProcessDataRPC && available >= memory.discovery.output + 2){//process data, requires cmd+output bytes+crc
//TODO: maybe packing and unpacking can be moved to RT
process_data_rpc(0x00, txbuf, &(rxbuf[rxpos+1])); // todo: send a proper fault byte?
send(memory.discovery.input,1);
//we cannot send the reply based on crc, as this causes timeouts
//instead we should check for errors in RT
if(!crc_reuest(memory.discovery.output + 1)){
PIN(crc_error)++;
}
rxpos += memory.discovery.output + 2;
}else{
continue;
}
}else if (lbp.ct == CT_RW && lbp.wr == 0){ //read
if(available >= 2){
if (lbp.as == 1){ //address included in command = cmd+addr+addr+crc
if (available >= 4) {
address = rxbuf[(rxpos+1)%sizeof(rxbuf)] + (rxbuf[(rxpos+2)%sizeof(rxbuf)]<<8);
rxpos += 4;
}else{
continue;
}
}else{ //address not included in command = cmd+crc
rxpos += 2;
}
memcpy((void*)txbuf,&memory.bytes[address],(1<<lbp.ds));
send((1<<lbp.ds),1);
if(lbp.ai == 1){//auto increment address by datasize
address += (1<<lbp.ds);
}
}else{
continue;
}
} else {
//TODO: handle unkown packet
PIN(error)++;
}
}
timeout++;
if(timeout > PIN(timeout)){//TODO: clamping
PIN(connected) = 0;
rxpos = bufferpos;
}else{
PIN(connected) = 1;
}
rxpos = rxpos % sizeof(rxbuf);
}
);
NRT(
if(PIN(dump_pd_vals) != 0.0) {
if(PIN(error) != 0) {
printf("Error count: %f\n", PIN(error));
}
PIN(dump_pd_vals) = 0.0;
printf("pos_cmd: %f\n", PIN(pos_cmd));
uint16_t *ptocp = (uint16_t *)(memory.bytes + memory.discovery.ptocp);
while(*ptocp != 0x0000) {
process_data_descriptor_t *pd = (process_data_descriptor_t *)(memory.bytes + *ptocp++);
printf("pd has data at %x with value %x\n", pd->data_addr, MEMU16(pd->data_addr));
}
}
);
ENDCOMP;
6e29299f3This file is pinned to the selected release. Download the original to open it in your design tool.