diff --git a/Firmware/carteHRI.uvoptx b/Firmware/carteHRI.uvoptx index 06161cb..fc80484 100644 --- a/Firmware/carteHRI.uvoptx +++ b/Firmware/carteHRI.uvoptx @@ -1,749 +1,762 @@ 1.0
### uVision Project, (C) Keil Software
*.c *.s*; *.src; *.a* *.obj *.lib *.txt; *.h; *.inc *.plm *.cpp 0 0 0 Target 1 0x4 ARM-ADS 168000000 1 1 0 1 0 1 65535 0 0 0 79 66 8 .\lst\ 1 1 1 0 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 0 0 1 0 1 18 0 User Manual (MCBSTM32F400) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\Keil\MCBSTM32F400\Documentation\mcbstm32f200.chm 1 Schematics (MCBSTM32F400) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\Keil\MCBSTM32F400\Documentation\mcbstm32f400-schematics.pdf 2 Getting Started (STM32F4-Discovery) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\ST\STM32F4-Discovery\Documentation\DM00037368.pdf 3 User Manual (STM32F4-Discovery) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\ST\STM32F4-Discovery\Documentation\DM00039084.pdf 4 Bill of Materials (STM32F4-Discovery) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\ST\STM32F4-Discovery\Documentation\stm32f4discovery_bom.zip 5 Gerber Files (STM32F4-Discovery) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\ST\STM32F4-Discovery\Documentation\stm32f4discovery_gerber.zip 6 Schematics (STM32F4-Discovery) C:\Keil_v5\ARM\PACK\Keil\STM32F4xx_DFP\2.5.0\MDK\Boards\ST\STM32F4-Discovery\Documentation\stm32f4discovery_sch.zip 7 MCBSTM32F400 Evaluation Board Web Page (MCBSTM32F400) http://www.keil.com/mcbstm32f400/ 8 STM32F4-Discovery Web Page (STM32F4-Discovery) http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1199/PF252419 0 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 0 0 11 STLink\ST-LINKIII-KEIL_SWO.dll 0 ARMRTXEVENTFLAGS -L50 -Z18 -C0 -M0 -T0 0 DLGDARM (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0) 0 DLGTARM (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0) 0 ARMDBGFLAGS -T0 0 DLGUARM (105=-1,-1,-1,-1,0) 0 ST-LINKIII-KEIL_SWO - -U303030303030303030303031 -I0 -O8398 -S1 -C0 -A0 -N00("ARM CoreSight SW-DP") -D00(2BA01477) -L00(0) -TO19 -TC168000000 -TP21 -TDS8053 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC800 -FN1 -FF0STM32F4xx_1024.FLM -FS08000000 -FL0100000 -FP0($$Device:STM32F407VG$CMSIS\Flash\STM32F4xx_1024.FLM) + -U303030303030303030303031 -O8398 -S1 -C0 -A0 -N00("ARM CoreSight SW-DP") -D00(2BA01477) -L00(0) -TO19 -TC168000000 -TP21 -TDS8053 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC800 -FN1 -FF0STM32F4xx_1024.FLM -FS08000000 -FL0100000 -FP0($$Device:STM32F407VG$CMSIS\Flash\STM32F4xx_1024.FLM) 0 UL2CM3 UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F4xx_1024 -FS08000000 -FL0100000 -FP0($$Device:STM32F407VG$Flash\STM32F4xx_1024.FLM)) 0 1 - statusReg,0x0A + uart_txBuffer[0] 1 1 - ctrl_currentPid,0x0A + uart_txBuffer[1] 2 1 - adc_currentSensOffset,0x0A + uart_currentTxBufferToWriteTo 3 1 - ctrl_motorTorque_Nm_c + testVar 4 1 - ctrl_positionPid + uart_txBufferIndex 0 2 motorCurrentDebug 1 2 motorTargetCurrentDebug 2 2 ctrl_motorTorque_mNm_c 3 2 motorTorque_mNm_dbg 4 2 motorTorque_mNm 5 2 nCycles 6 2 motorPosCod,0x0A 7 2 CurrentSensOffset,0x0A 8 2 motorCurrent_mA_mean 9 2 motorCurrent_mA 10 2 ADCValuesBuffer,0x0A 11 2 motorCurrent_mA 12 2 motorCurrentSum 13 2 debugVar01,0x0A 14 2 adcValues1,0x0A 15 2 motorCurrent_mA 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 STM32_files 1 0 0 0 1 1 1 0 0 0 0 .\src\system_stm32f4xx.c system_stm32f4xx.c 0 0 1 2 2 0 0 0 0 .\startup_stm32f4xx.s startup_stm32f4xx.s 0 0 SRC 1 0 0 0 2 3 1 0 0 0 0 .\src\main.c main.c 0 0 2 4 1 0 0 0 0 .\src\communication.c communication.c 0 0 2 5 1 0 0 0 0 .\src\controller.c controller.c 0 0 STM32_libs 1 0 0 0 3 6 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_gpio.c stm32f4xx_gpio.c 0 0 3 7 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_rcc.c stm32f4xx_rcc.c 0 0 3 8 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_tim.c stm32f4xx_tim.c 0 0 3 9 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\misc.c misc.c 0 0 3 10 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_exti.c stm32f4xx_exti.c 0 0 3 11 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_syscfg.c stm32f4xx_syscfg.c 0 0 3 12 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_adc.c stm32f4xx_adc.c 0 0 3 13 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_dac.c stm32f4xx_dac.c 0 0 3 14 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_dma.c stm32f4xx_dma.c 0 0 3 15 1 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_usart.c stm32f4xx_usart.c 0 0 3 16 4 0 0 0 0 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\Lib\ARM\arm_cortexM4lf_math.lib arm_cortexM4lf_math.lib 0 0 SRC_drivers 1 0 0 0 4 17 1 0 0 0 0 .\src\drivers\adc.c adc.c 0 0 4 18 1 0 0 0 0 .\src\drivers\callback_timers.c callback_timers.c 0 0 4 19 1 0 0 0 0 .\src\drivers\dac.c dac.c 0 0 4 20 1 0 0 0 0 .\src\drivers\h_bridge.c h_bridge.c 0 0 4 21 1 0 0 0 0 .\src\drivers\incr_encoder.c incr_encoder.c 0 0 4 22 1 0 0 0 0 .\src\drivers\uart.c uart.c 0 0 4 23 1 0 0 0 0 .\src\drivers\hall.c hall.c 0 0 4 24 1 0 0 0 0 .\src\drivers\strain_gauge.c strain_gauge.c 0 0 4 25 1 0 0 0 0 .\src\drivers\tachometer.c tachometer.c 0 0 SRC_lib 1 0 0 0 5 26 1 0 0 0 0 .\src\lib\basic_filter.c basic_filter.c 0 0 5 27 1 0 0 0 0 .\src\lib\pid.c pid.c 0 0 5 28 1 0 0 0 0 .\src\lib\utils.c utils.c 0 0 + + 5 + 29 + 1 + 0 + 0 + 0 + 0 + .\src\lib\circular_buffer.c + circular_buffer.c + 0 + 0 +
diff --git a/Firmware/carteHRI.uvprojx b/Firmware/carteHRI.uvprojx index 3b37fa6..a59f0aa 100644 --- a/Firmware/carteHRI.uvprojx +++ b/Firmware/carteHRI.uvprojx @@ -1,581 +1,586 @@ 2.1
### uVision Project, (C) Keil Software
Target 1 0x4 ARM-ADS STM32F407VG STMicroelectronics Keil.STM32F4xx_DFP.2.5.0 http://www.keil.com/pack IROM(0x08000000,0x100000) IRAM(0x20000000,0x20000) IRAM2(0x10000000,0x10000) CPUTYPE("Cortex-M4") FPU2 CLOCK(168000000) ELITTLE UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F4xx_1024 -FS08000000 -FL0100000 -FP0($$Device:STM32F407VG$Flash\STM32F4xx_1024.FLM)) 6103 $$Device:STM32F407VG$Device\Include\stm32f4xx.h $$Device:STM32F407VG$SVD\STM32F40x.svd 0 0 0 0 0 0 1 .\output\ motorControl 1 0 1 1 1 .\lst\ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 3 1 SARMCM3.DLL -REMAP -MPU DCM.DLL -pCM4 SARMCM3.DLL -REMAP -MPU TCM.DLL -pCM4 1 0 0 0 16 0 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 11 STLink\ST-LINKIII-KEIL_SWO.dll 1 0 0 1 1 4096 1 BIN\UL2CM3.DLL "" () 0 0 1 1 1 1 1 1 1 0 1 1 0 1 1 0 0 1 1 1 1 1 1 1 1 1 0 0 "Cortex-M4" 0 0 0 1 1 0 0 2 1 0 8 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x20000000 0x20000 1 0x8000000 0x100000 0 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x8000000 0x100000 1 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x20000000 0x20000 0 0x10000000 0x10000 1 4 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 USE_STDPERIPH_DRIVER STM32F4XX ARM_MATH_CM4 ..\CodeUC;.\inc;.\src;C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\ST\STM32F4xx\Include;C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\inc;C:\STM32F4-Discovery_FW_V1.1.0\Utilities\STM32F4-Discovery;C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\Include 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0x08000000 0x20000000 STM32_files system_stm32f4xx.c 1 .\src\system_stm32f4xx.c startup_stm32f4xx.s 2 .\startup_stm32f4xx.s SRC main.c 1 .\src\main.c communication.c 1 .\src\communication.c controller.c 1 .\src\controller.c STM32_libs stm32f4xx_gpio.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_gpio.c stm32f4xx_rcc.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_rcc.c stm32f4xx_tim.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_tim.c misc.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\misc.c stm32f4xx_exti.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_exti.c stm32f4xx_syscfg.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_syscfg.c stm32f4xx_adc.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_adc.c stm32f4xx_dac.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_dac.c stm32f4xx_dma.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_dma.c stm32f4xx_usart.c 1 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\STM32F4xx_StdPeriph_Driver\src\stm32f4xx_usart.c arm_cortexM4lf_math.lib 4 C:\STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\Lib\ARM\arm_cortexM4lf_math.lib SRC_drivers adc.c 1 .\src\drivers\adc.c callback_timers.c 1 .\src\drivers\callback_timers.c dac.c 1 .\src\drivers\dac.c h_bridge.c 1 .\src\drivers\h_bridge.c incr_encoder.c 1 .\src\drivers\incr_encoder.c uart.c 1 .\src\drivers\uart.c hall.c 1 .\src\drivers\hall.c strain_gauge.c 1 .\src\drivers\strain_gauge.c tachometer.c 1 .\src\drivers\tachometer.c SRC_lib basic_filter.c 1 .\src\lib\basic_filter.c pid.c 1 .\src\lib\pid.c utils.c 1 .\src\lib\utils.c + + circular_buffer.c + 1 + .\src\lib\circular_buffer.c +
diff --git a/Firmware/src/communication.c b/Firmware/src/communication.c index 4ad4031..a3d2b8a 100644 --- a/Firmware/src/communication.c +++ b/Firmware/src/communication.c @@ -1,361 +1,378 @@ #include "communication.h" #include "controller.h" #include "drivers/callback_timers.h" #include "drivers/adc.h" #include "drivers/dac.h" #include "drivers/incr_encoder.h" #include "drivers/hall.h" #include "drivers/strain_gauge.h" #include "drivers/tachometer.h" #include "drivers/uart.h" #include "lib/basic_filter.h" #include "lib/pid.h" #include "lib/utils.h" -#include #include extern uint32_t statusReg, ctrl_timestamp, ctrl_currentFilterType; extern float32_t ctrl_motorPosCod, ctrl_motorPosCod_c; extern float32_t ctrl_motorTorque_Nm_c, ctrl_motorPosCodFilt, ctrl_firstOrderFilterTau; extern float32_t hall_angle, tac_speed, sg_force; extern pid_Pid ctrl_positionPid; extern bfilt_BasicFilter ctrl_encoderFilter; extern float32_t ctrl_sineAmplitude, ctrl_sinePeriod; uint32_t selectedVariablesToStream; // Bitfield that indicates for each variable if it should be streamed or not. void* variablesArray[N_VARIABLES]; // Pointers to variables to stream. uint8_t txBuffer[1024]; +#define COMM_BUFFER_SIZE 1024 #define DEBUG_MESSAGE_BUFFER_SIZE 1024 + +uint8_t comm_packetTxBuffer[COMM_BUFFER_SIZE]; char comm_debugMessageBuffer[DEBUG_MESSAGE_BUFFER_SIZE]; +cb_CircularBuffer *comm_rxQueue; uint8_t rxCurrentMessageType = PC_MESSAGE_DO_NOTHING; // Current message type for RX bytes. uint32_t rxBytesCount; // Number of received bytes for the current message. uint8_t firstHalfByte; // First half of the data byte to receive. uint8_t rxDataBytesBuffer[32]; // Data bytes received (ready to use, bytes already merged). void comm_SendPacket(uint8_t messageType, uint8_t *data, uint16_t dataLength); void comm_HandleByte(uint8_t rxData); void comm_Stream(void); uint32_t comm_GetVar(uint8_t variableIndex); /** * @brief Init the communication manager. */ void comm_Init(void) { // Initialize the array with all the pointers on variables. // Note that this array contains pointers only to uint32_t, even if the // variables are actually float32_t. This does not matter, since we only use // these pointers to perform byte-to-byte copies. // Do not modify this by hand, copy-paste code generated by the excel sheet. variablesArray[VAR_TIMESTAMP] = (uint32_t*) &ctrl_timestamp; variablesArray[VAR_POS_REG_PID_P] = (uint32_t*) &ctrl_positionPid.kp; variablesArray[VAR_POS_REG_PID_I] = (uint32_t*) &ctrl_positionPid.ki; variablesArray[VAR_POS_REG_PID_D] = (uint32_t*) &ctrl_positionPid.kd; variablesArray[VAR_POS_REG_PID_ARW] = (uint32_t*) &ctrl_positionPid.arw; variablesArray[VAR_POS_REG_PID_INTEGRATOR] = (uint32_t*) &ctrl_positionPid.integrator; variablesArray[VAR_POS_REG_PID_OUTPUT] = (uint32_t*) &ctrl_motorTorque_Nm_c; variablesArray[VAR_POS_REG_SAMPLING_PERIOD] = NULL; variablesArray[VAR_WALL_POSITION] = (uint32_t*) &ctrl_motorPosCod_c; variablesArray[VAR_MEASURED_ANGLE_RAW] = NULL; variablesArray[VAR_MEASURED_ANGLE_FILTERED] = (uint32_t*) &ctrl_motorPosCodFilt; variablesArray[VAR_COMM_TX_PERIOD] = NULL; variablesArray[VAR_ACTIVE_FILTER] = (uint32_t*) &ctrl_currentFilterType; variablesArray[VAR_1ST_FILT_TAU] = (uint32_t*) &ctrl_encoderFilter.tau; variablesArray[VAR_DAC1_VOLTAGE] = NULL; variablesArray[VAR_DAC2_VOLTAGE] = NULL; variablesArray[VAR_ADC1_VOLTAGE] = NULL; variablesArray[VAR_ADC2_VOLTAGE] = NULL; variablesArray[VAR_HALL_ANGLE] = NULL; variablesArray[VAR_TACHO_SPEED] = NULL; variablesArray[VAR_STRAIN_GAUGE_FORCE] = NULL; variablesArray[VAR_USER_VAR_1] = NULL; variablesArray[VAR_USER_VAR_2] = NULL; variablesArray[VAR_USER_VAR_3] = NULL; variablesArray[VAR_USER_VAR_4] = NULL; variablesArray[VAR_USER_VAR_5] = NULL; // Setup the UART peripheral, and specify the function that will be called // each time a byte is received. - uart_Init(comm_HandleByte); + uart_Init(); + comm_rxQueue = uart_GetRxQueue(); + rxCurrentMessageType = PC_MESSAGE_DO_NOTHING; // Make the streaming function periodically called by the timer 7. cbt_SetCommLoopTimer(comm_Stream, TE_DATA_LOOP_DEFAULT_VAL); } +void comm_Step(void) +{ + // Send the bytes in the TX queue, even if it is not full, to avoid latency. + uart_FlushTx(); + + // Interpret the bytes in the RX queue. + uart_Step(); + + while(!cb_IsEmpty(comm_rxQueue)) + comm_HandleByte(cb_Pull(comm_rxQueue)); +} + /** * @brief UART data streaming "loop" function. */ void comm_Stream() { // If the data streaming is enabled, send a stream packet to the PC. if(statusReg & DATA_UPSTREAM_ENB) { uint32_t i; int nDataBytesToSend = 0; for(i=0; i> 4); // Most significant bits. - uart_SendByte((data[i]&0x0f)); // Least significant bits. + *p = ((data[i]&0xf0) >> 4); // Most significant bits. + p++; + *p = ((data[i]&0x0f)); // Least significant bits. + p++; } + + uart_SendBytesAsync(comm_packetTxBuffer, dataLength*2+1); } /** * @brief Process the received byte to interpret the messages. * @param rxData: the byte to be processed and interpreted. */ void comm_HandleByte(uint8_t rxData) { if(rxData & (1<<7)) // The start byte has the most significant bit high. { rxCurrentMessageType = (rxData & ~(1<<7)); // Remove the start bit. rxBytesCount = 0; } else rxBytesCount++; if(rxBytesCount % 2 == 1) // First half of the data byte has been received. firstHalfByte = rxData; // Store it until the second half arrives. else // Second half of the data byte has been received (or no data bytes yet). { int dataBytesReady = rxBytesCount/2; rxDataBytesBuffer[dataBytesReady-1] = (firstHalfByte<<4) + (rxData & 0xf); switch(rxCurrentMessageType) { case PC_MESSAGE_DO_NOTHING: if(dataBytesReady == 0) ; // Do nothing. break; case PC_MESSAGE_SEND_STATUS: if(dataBytesReady == 0) comm_SendPacket(STM_MESSAGE_STATUS, (uint8_t*)(&statusReg), 4); break; case PC_MESSAGE_START_STREAMING: if(dataBytesReady == 0) { statusReg |= DATA_UPSTREAM_ENB; TIM_SetCounter(TIM7, 0); TIM_Cmd(TIM7, ENABLE); } break; case PC_MESSAGE_STOP_STREAMING: if(dataBytesReady == 0) { TIM_Cmd(TIM7, DISABLE); statusReg &= ~DATA_UPSTREAM_ENB; } break; case PC_MESSAGE_GET_VAR: if(dataBytesReady == 1) { uint32_t variableValue; // Extract the index, that indicates which variable will be sent. uint8_t variableIndex = rxDataBytesBuffer[0]; // If the variable index is out of range, ignore the request. if(variableIndex >= N_VARIABLES) return; // Prepare the message to be sent to the PC. // First byte: variable index. txBuffer[0] = variableIndex; // Four following bytes: actual value of the variable. variableValue = comm_GetVar(variableIndex); memcpy(&txBuffer[1], &variableValue, 4); // Send the message to the PC. comm_SendPacket(STM_MESSAGE_VAR, txBuffer, 5); } break; case PC_MESSAGE_SET_VAR: if(dataBytesReady == 5) { // Extract the index (that indicates which variable will change), // and the new value of the variable. uint32_t newValue; uint8_t variableIndex = rxDataBytesBuffer[0]; memcpy(&newValue, &rxDataBytesBuffer[1], 4); // Set the selected variable with the new value. comm_SetVar(variableIndex, newValue); } break; case PC_MESSAGE_SET_STREAMED_VAR: if(dataBytesReady == 4) { uint8_t *newSelectedVars = &rxDataBytesBuffer[0]; memcpy(&selectedVariablesToStream, newSelectedVars, 4); } break; default: // No data bytes for the other message types. break; } } } -void comm_SendDebugMessage(char format[], ...) +/** + * @brief Sends a debug message to the computer. + * @param format format string. + * @param ... variables to be printed in the format string. + */ +void comm_SendDebugMessage(const char* format, ...) { + uint16_t length; va_list args; - va_start(args, format); - snprintf(comm_debugMessageBuffer, DEBUG_MESSAGE_BUFFER_SIZE, - format, args); - va_end(args); + + va_start(args, format); + + length = vsnprintf(comm_debugMessageBuffer, DEBUG_MESSAGE_BUFFER_SIZE, + format, args); comm_SendPacket(STM_MESSAGE_DEBUG_TEXT, (uint8_t*)comm_debugMessageBuffer, - strlen(comm_debugMessageBuffer)); -} + length+1); -#define comm_SendDebugMessageDecimatedII(decimation, COUNTER, message, ...) \ - static int comm_decim##COUNTER = 0; \ - if(comm_decim##COUNTER++ % decimation == 0) \ - comm_SendDebugMessage(message, __VA_ARGS__); - -#define comm_SendDebugMessageDecimatedI(decimation, format, ...) \ - do { \ - comm_SendDebugMessageDecimatedII(decimation, __COUNTER__, format, __VA_ARGS__) \ - } while(0) - -void comm_SendDebugMessageDecimated(int decimation, char format[], ...) -{ - va_list args; - va_start(args, format); - comm_SendDebugMessageDecimatedI(decimation, format, args); - va_end(args); + va_end(args); } + diff --git a/Firmware/src/communication.h b/Firmware/src/communication.h index fa8228e..bf1c710 100644 --- a/Firmware/src/communication.h +++ b/Firmware/src/communication.h @@ -1,79 +1,57 @@ #ifndef __COMMUNICATION_H #define __COMMUNICATION_H #include "main.h" +#include "definitions.h" -// Message IDs sent by host (PC) to the device (STM). -#define PC_MESSAGE_DO_NOTHING 0 // Do nothing. -#define PC_MESSAGE_TOGGLE_LED 1 // Toggle the LED (debug). -#define PC_MESSAGE_SEND_STATUS 2 // Request the device to send the main status register. -#define PC_MESSAGE_START_STREAMING 3 // Request the device to start streaming continuously. -#define PC_MESSAGE_STOP_STREAMING 4 // Request the device to stop streaming continuously. -#define PC_MESSAGE_GET_VAR 5 // Request the device to send the selected value. -#define PC_MESSAGE_SET_VAR 6 // Set the selected variable. -#define PC_MESSAGE_SET_STREAMED_VAR 7 // Set the variables to be streamed. - -// Message IDs sent by device (STM) to the device (PC). -#define STM_MESSAGE_STATUS 0 // Main status register. -#define STM_MESSAGE_VAR 1 // Variable state. -#define STM_MESSAGE_STREAMING_PACKET 2 // Streaming packet. -#define STM_MESSAGE_DEBUG_TEXT 3 // Debug text message. - -// Variables IDs. -#define VAR_TIMESTAMP 0 // Timestamp. [us] -#define VAR_POS_REG_PID_P 1 // Position regulator - proportionnal coefficient. [1] -#define VAR_POS_REG_PID_I 2 // Position regulator - integral coefficient. [1] -#define VAR_POS_REG_PID_D 3 // Position regulator - derivative coefficient. [1] -#define VAR_POS_REG_PID_ARW 4 // Position regulator - ARW (integrator max value). [1] -#define VAR_POS_REG_PID_INTEGRATOR 5 // Position regulator - integrator state. [1] -#define VAR_POS_REG_PID_OUTPUT 6 // Position regulator - output. [mN.m] -#define VAR_POS_REG_SAMPLING_PERIOD 7 // Position regulator - sampling period [us] -#define VAR_WALL_POSITION 8 // Position regulator - target (wall position). [pulses] -#define VAR_MEASURED_ANGLE_RAW 9 // Measured output angle, unfiltered. [deg] -#define VAR_MEASURED_ANGLE_FILTERED 10 // Measured output angle, filtered. [deg] -#define VAR_COMM_TX_PERIOD 11 // Communication loop - sending period. [us] -#define VAR_ACTIVE_FILTER 12 // Active filter type (0: none, 1: first-order, 2: running mean). [None] -#define VAR_1ST_FILT_TAU 13 // First order filter - time constant. [1] -#define VAR_DAC1_VOLTAGE 14 // DAC 1 channel voltage. [V] -#define VAR_DAC2_VOLTAGE 15 // DAC 2 channel voltage. [V] -#define VAR_ADC1_VOLTAGE 16 // ACDC 1 channel voltage. [V] -#define VAR_ADC2_VOLTAGE 17 // ACDC 2 channel voltage. [V] -#define VAR_HALL_ANGLE 18 // Angle measured by the Hall sensor. [deg] -#define VAR_TACHO_SPEED 19 // Angular speed measured by the tachometer. [deg/s] -#define VAR_STRAIN_GAUGE_FORCE 20 // Force measured by the strain gauge. [?] -#define VAR_USER_VAR_1 21 // User Variable 1 [] -#define VAR_USER_VAR_2 22 // User Variable 2 [] -#define VAR_USER_VAR_3 23 // User Variable 3 [] -#define VAR_USER_VAR_4 24 // User Variable 4 [] -#define VAR_USER_VAR_5 25 // User Variable 5 [] - -#define N_VARIABLES 26 // Number of variables, used for enumeration of all variables. +#include /** @defgroup Communication Main / Communication * @brief Control the communication with the computer. * * This module controls all the communication logic between the board and the * computer. It uses a specific communication protocol between the * computer and the board, with a system of messages. Thanks to this, the * the MATLAB application is able to get the value of selected variables on the * STM, and is even capable of modifiying them remotely. * * Make sure that the files communication.h/.c are up-to-date with the Excel * spreadsheet "Protocol description.xlsx". * * Call comm_Init() to setup this module. Its interrupt function will be called * automatically when a message arrives, or periodically when the data * streaming is enabled. * * @addtogroup Communication * @{ */ void comm_Init(void); -void comm_SendDebugMessage(char format[], ...); -void comm_SendDebugMessageDecimated(int decimation, char format[], ...); +void comm_Step(void); +void comm_SendDebugMessage(const char *format, ...); + +/** + * @brief Sends a debug message to the computer, with decimation. + * This macro is useful to print human-readable text, in a fast loop, to avoid + * overloading the communication bus, or the computer. + * @param decimation this macro will actually print once out of decimation, and + * do nothing otherwise. + * @param format format string. See the printf() documentation for format + * specification. + * @param ... variables to be printed in the format string. + */ +#define comm_SendDebugMessageDecimated(decimation, format, ...) \ +do \ +{ \ + static int comm_decim##__COUNTER__ = 0; \ + if(comm_decim##__COUNTER__++ % decimation == 0) \ + { \ + comm_SendDebugMessage(format, ##__VA_ARGS__); \ + } \ +} while(0) + /** * @} */ #endif diff --git a/Firmware/src/communication.h b/Firmware/src/definitions.h similarity index 72% copy from Firmware/src/communication.h copy to Firmware/src/definitions.h index fa8228e..9f263b2 100644 --- a/Firmware/src/communication.h +++ b/Firmware/src/definitions.h @@ -1,79 +1,50 @@ -#ifndef __COMMUNICATION_H -#define __COMMUNICATION_H - -#include "main.h" +#ifndef DEF_DEFINITIONS_H +#define DEF_DEFINITIONS_H // Message IDs sent by host (PC) to the device (STM). #define PC_MESSAGE_DO_NOTHING 0 // Do nothing. #define PC_MESSAGE_TOGGLE_LED 1 // Toggle the LED (debug). #define PC_MESSAGE_SEND_STATUS 2 // Request the device to send the main status register. #define PC_MESSAGE_START_STREAMING 3 // Request the device to start streaming continuously. #define PC_MESSAGE_STOP_STREAMING 4 // Request the device to stop streaming continuously. #define PC_MESSAGE_GET_VAR 5 // Request the device to send the selected value. #define PC_MESSAGE_SET_VAR 6 // Set the selected variable. #define PC_MESSAGE_SET_STREAMED_VAR 7 // Set the variables to be streamed. // Message IDs sent by device (STM) to the device (PC). #define STM_MESSAGE_STATUS 0 // Main status register. #define STM_MESSAGE_VAR 1 // Variable state. #define STM_MESSAGE_STREAMING_PACKET 2 // Streaming packet. #define STM_MESSAGE_DEBUG_TEXT 3 // Debug text message. // Variables IDs. #define VAR_TIMESTAMP 0 // Timestamp. [us] #define VAR_POS_REG_PID_P 1 // Position regulator - proportionnal coefficient. [1] #define VAR_POS_REG_PID_I 2 // Position regulator - integral coefficient. [1] #define VAR_POS_REG_PID_D 3 // Position regulator - derivative coefficient. [1] #define VAR_POS_REG_PID_ARW 4 // Position regulator - ARW (integrator max value). [1] #define VAR_POS_REG_PID_INTEGRATOR 5 // Position regulator - integrator state. [1] #define VAR_POS_REG_PID_OUTPUT 6 // Position regulator - output. [mN.m] #define VAR_POS_REG_SAMPLING_PERIOD 7 // Position regulator - sampling period [us] #define VAR_WALL_POSITION 8 // Position regulator - target (wall position). [pulses] #define VAR_MEASURED_ANGLE_RAW 9 // Measured output angle, unfiltered. [deg] #define VAR_MEASURED_ANGLE_FILTERED 10 // Measured output angle, filtered. [deg] #define VAR_COMM_TX_PERIOD 11 // Communication loop - sending period. [us] #define VAR_ACTIVE_FILTER 12 // Active filter type (0: none, 1: first-order, 2: running mean). [None] #define VAR_1ST_FILT_TAU 13 // First order filter - time constant. [1] #define VAR_DAC1_VOLTAGE 14 // DAC 1 channel voltage. [V] #define VAR_DAC2_VOLTAGE 15 // DAC 2 channel voltage. [V] #define VAR_ADC1_VOLTAGE 16 // ACDC 1 channel voltage. [V] #define VAR_ADC2_VOLTAGE 17 // ACDC 2 channel voltage. [V] #define VAR_HALL_ANGLE 18 // Angle measured by the Hall sensor. [deg] #define VAR_TACHO_SPEED 19 // Angular speed measured by the tachometer. [deg/s] #define VAR_STRAIN_GAUGE_FORCE 20 // Force measured by the strain gauge. [?] #define VAR_USER_VAR_1 21 // User Variable 1 [] #define VAR_USER_VAR_2 22 // User Variable 2 [] #define VAR_USER_VAR_3 23 // User Variable 3 [] #define VAR_USER_VAR_4 24 // User Variable 4 [] #define VAR_USER_VAR_5 25 // User Variable 5 [] #define N_VARIABLES 26 // Number of variables, used for enumeration of all variables. -/** @defgroup Communication Main / Communication - * @brief Control the communication with the computer. - * - * This module controls all the communication logic between the board and the - * computer. It uses a specific communication protocol between the - * computer and the board, with a system of messages. Thanks to this, the - * the MATLAB application is able to get the value of selected variables on the - * STM, and is even capable of modifiying them remotely. - * - * Make sure that the files communication.h/.c are up-to-date with the Excel - * spreadsheet "Protocol description.xlsx". - * - * Call comm_Init() to setup this module. Its interrupt function will be called - * automatically when a message arrives, or periodically when the data - * streaming is enabled. - * - * @addtogroup Communication - * @{ - */ - -void comm_Init(void); -void comm_SendDebugMessage(char format[], ...); -void comm_SendDebugMessageDecimated(int decimation, char format[], ...); -/** - * @} - */ - #endif diff --git a/Firmware/src/drivers/callback_timers.c b/Firmware/src/drivers/callback_timers.c index d1e98ea..1e4d267 100644 --- a/Firmware/src/drivers/callback_timers.c +++ b/Firmware/src/drivers/callback_timers.c @@ -1,230 +1,227 @@ #include "callback_timers.h" #include "../controller.h" #include "../lib/utils.h" cbt_PeriodicTaskFunc cbt_tim1Task, cbt_tim6Task, cbt_tim7Task; volatile float32_t cbt_ucLoad; // Processor load (%). void tim1InitFunc(void); void tim67InitFunc(void); /** * @brief Initialize the timers to call an interrupt routine periodically. */ void cbt_Init(void) -{ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE, ENABLE); - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // ??? - +{ // Initialize the timers. tim1InitFunc(); tim67InitFunc(); // Initialize the function pointers to NULL, in order to be able to test // if they are already affected or not. cbt_tim1Task = NULL; cbt_tim6Task = NULL; cbt_tim7Task = NULL; } /** * @brief Set the function to call periodically by the timer 1. * @param f: the function to call periodically. * @param period: the period between each call of f [us]. */ void cbt_SetCurrentLoopTimer(cbt_PeriodicTaskFunc f, uint32_t period) { cbt_tim1Task = f; utils_SaturateU(&period, TE_LOOP_MIN_VALUE, TE_LOOP_MAX_VALUE); TIM1->ARR = (uint16_t) period; } /** * @brief Set the function to call periodically by the timer 6. * @param f: the function to call periodically. * @param period: the period between each call of f [us]. */ void cbt_SetPositionLoopTimer(cbt_PeriodicTaskFunc f, uint32_t period) { cbt_tim6Task = f; utils_SaturateU(&period, TE_LOOP_MIN_VALUE, TE_LOOP_MAX_VALUE); TIM6->ARR = (uint16_t) period; } /** * @brief Set the function to call periodically by the timer 7. * @param f: the function to call periodically. * @param period: the period between each call of f [us]. */ void cbt_SetCommLoopTimer(cbt_PeriodicTaskFunc f, uint32_t period) { cbt_tim7Task = f; utils_SaturateU(&period, TE_LOOP_MIN_VALUE, TE_LOOP_MAX_VALUE); TIM7->ARR = (uint16_t) period; } /** * @brief Initialize TIM1 used for timing the current loop (resolution 1us). */ void tim1InitFunc(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; //TIM_OCInitTypeDef TIM_OCInitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); NVIC_InitStruct.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = CURRENT_LOOP_IRQ_PRIORITY; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); TIM_TimeBaseStruct.TIM_Period = (uint16_t)(TE_CURRENT_LOOP_DEFAULT_VAL-1); TIM_TimeBaseStruct.TIM_Prescaler = TIM1_PRESCALER; TIM_TimeBaseStruct.TIM_ClockDivision = 0; // TIM_CKD_DIV2; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct); TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); TIM_Cmd(TIM1, ENABLE); } /** * @brief Initialize TIM6, for timing the main control loop (resolution 1us) * Initialize TIM7, for timing the data transmission loop (resolution 1us) */ void tim67InitFunc(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6|RCC_APB1Periph_TIM7, ENABLE); NVIC_InitStruct.NVIC_IRQChannel = TIM6_DAC_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = CONTROL_LOOP_IRQ_PRIORITY; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = TIM7_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = DATA_LOOP_IRQ_PRIORITY; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStruct); TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Prescaler = TIM6_PRESCALER; TIM_TimeBaseStruct.TIM_Period = (uint16_t)(TE_CONTROL_LOOP_DEFAULT_VAL-1); TIM_TimeBaseStruct.TIM_ClockDivision = 0; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStruct); TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Prescaler = TIM7_PRESCALER; TIM_TimeBaseStruct.TIM_Period = (uint16_t)(TE_DATA_LOOP_DEFAULT_VAL-1); TIM_TimeBaseStruct.TIM_ClockDivision = 0; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStruct); TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); TIM_Cmd(TIM6, ENABLE); TIM_Cmd(TIM7, DISABLE); } /** * @brief Interrupt from current control loop timer (TIM1) */ void TIM1_UP_TIM10_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { if(cbt_tim1Task != NULL) cbt_tim1Task(); TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } /** * @brief Interrupt from main control loop timer (TIM6) */ void TIM6_DAC_IRQHandler(void) { if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { if(cbt_tim6Task != NULL) cbt_tim6Task(); // Percentage of time consumed by the control task 0..100%. cbt_ucLoad = (((float32_t)(TIM6->CNT))*100)/((float32_t)(TIM6->ARR)); TIM_ClearITPendingBit(TIM6, TIM_IT_Update); } } /** * @brief Interrupt from data transmission loop timer (TIM7) (data loop) */ void TIM7_IRQHandler(void) { if(TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) { if(cbt_tim7Task != NULL) cbt_tim7Task(); TIM_ClearITPendingBit(TIM7, TIM_IT_Update); } } /** * @brief Set the period of the position loop. * @param period: the new period of the position loop [us]. */ void cbt_SetPositionLoopPeriod(uint32_t period) { utils_SaturateU(&period, TE_LOOP_MIN_VALUE, TE_LOOP_MAX_VALUE); TIM6->ARR = period; } /** * @brief Set the period of the communication loop. * @param period: the new period of the communication loop [us]. */ void cbt_SetCommLoopPeriod(uint32_t period) { utils_SaturateU(&period, TE_LOOP_MIN_VALUE, TE_LOOP_MAX_VALUE); TIM7->ARR = period; } /** * @brief Get the period of the current loop. * @return the period of the current loop [us]. */ uint32_t cbt_GetCurrentLoopPeriod(void) { return TIM1->ARR; } /** * @brief Get the period of the position loop. * @return the period of the position loop [us]. */ uint32_t cbt_GetPositionLoopPeriod(void) { return TIM6->ARR; } /** * @brief Get the period of the communication loop. * @return the period of the communication loop [us]. */ uint32_t cbt_GetCommLoopPeriod(void) { return TIM7->ARR; } diff --git a/Firmware/src/drivers/uart.c b/Firmware/src/drivers/uart.c index 1460998..8030485 100644 --- a/Firmware/src/drivers/uart.c +++ b/Firmware/src/drivers/uart.c @@ -1,95 +1,281 @@ #include "uart.h" +#include "../lib/utils.h" +#include "../communication.h" // TODO: REMOVE. DEBUG ONLY. -uart_rxByteHandlerFunc uart_rxHandler; +#define UART_RX_DMA DMA1_Stream5 +#define UART_RX_DMA_CHANNEL DMA_Channel_4 +#define UART_TX_DMA DMA1_Stream6 +#define UART_TX_DMA_CHANNEL DMA_Channel_4 + +#define UART_TX_BUFFER_SIZE 512 +#define UART_RX_BUFFER_SIZE 512 +#define UART_USER_RX_QUEUE_SIZE 512 + +uint8_t uart_txBuffer[2][UART_TX_BUFFER_SIZE]; +uint8_t uart_currentTxBufferToWriteTo; +uint16_t uart_txBufferIndex; +uint16_t uart_nBytesToTransferDma; + +uint8_t uart_rxBuffer[UART_RX_BUFFER_SIZE]; +uint8_t const * uart_rxBuffTail; + +uint8_t uart_userRxQueue[UART_USER_RX_QUEUE_SIZE]; +cb_CircularBuffer uart_rxQueue; + +#define UART_DMA_TX_IS_BUSY (DMA_GetCmdStatus(UART_TX_DMA) == ENABLE && DMA_GetCurrDataCounter(UART_TX_DMA) > 0) /** - * @brief Initialize the UART module. - * @param f: a pointer to the function to be called each time a byte arrives. + * @brief Initializes the UART module. */ -void uart_Init(uart_rxByteHandlerFunc f) +void uart_Init(void) { - GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitTypeDef GPIO_InitStruct; USART_InitTypeDef USART_InitStruct; - NVIC_InitTypeDef NVIC_InitStruct; - - // Periph clock enable + DMA_InitTypeDef DMA_InitStruct; + + // Enable UART and DMA peripherals clocks. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); - // GPIO (PC10 & PC11) + // Setup GPIOs as UART pins. GPIO_InitStruct.GPIO_Pin = USART_TX_Pin; 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(USART_TX_Port, &GPIO_InitStruct); GPIO_PinAFConfig(USART_TX_Port, USART_TX_PinSource, GPIO_AF_USART2); GPIO_InitStruct.GPIO_Pin = USART_RX_Pin; GPIO_Init(USART_RX_Port, &GPIO_InitStruct); GPIO_PinAFConfig(USART_RX_Port, USART_RX_PinSource, GPIO_AF_USART2); - // UART config + // Setup the UART peripheral. USART_InitStruct.USART_BaudRate = USART_BAUDRATE; USART_InitStruct.USART_WordLength = USART_WordLength_8b; - USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1 stop bit (standard) + 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_Tx|USART_Mode_Rx; //Two way datas - USART_Init(USART2, &USART_InitStruct); + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART_PC_COMM, &USART_InitStruct); + + USART_Cmd(USART_PC_COMM, ENABLE); + + // Setup the DMA for RX. + DMA_DeInit(UART_RX_DMA); - // USART Rx interrupt - USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); - NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = UART_RX_IRQ_PRIORIY; - NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStruct); + DMA_InitStruct.DMA_Channel = UART_RX_DMA_CHANNEL; + DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; + DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&uart_rxBuffer[0]; + DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; + DMA_InitStruct.DMA_BufferSize = UART_RX_BUFFER_SIZE; + DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; + DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; + DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + DMA_Init(UART_RX_DMA, &DMA_InitStruct); - USART_Cmd(USART2, ENABLE); + USART_DMACmd(USART_PC_COMM, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(UART_RX_DMA, DMA_IT_TC, ENABLE); + DMA_Cmd(UART_RX_DMA, ENABLE); + + uart_rxBuffTail = &uart_rxBuffer[0]; + + // Setup the DMA for TX. + DMA_DeInit(UART_TX_DMA); + + DMA_InitStruct.DMA_Channel = UART_TX_DMA_CHANNEL; + DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; + DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&uart_txBuffer[0][0]; + DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStruct.DMA_BufferSize = UART_TX_BUFFER_SIZE; + DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; + DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; + DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + DMA_Init(UART_TX_DMA, &DMA_InitStruct); - // Function called when a byte arrives, to process it. - uart_rxHandler = f; + USART_DMACmd(USART_PC_COMM, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(UART_TX_DMA, DMA_IT_TC, ENABLE); + //DMA_Cmd(UART_TX_DMA, ENABLE); + + // Initialize the RX circular buffer. + cb_Init(&uart_rxQueue, uart_userRxQueue, UART_USER_RX_QUEUE_SIZE); + + // Initialize the variables for the UART TX. + uart_currentTxBufferToWriteTo = 0; + uart_txBufferIndex = 0; + uart_nBytesToTransferDma = 0; } /** - * @brief Get the received byte, and send it to the specified RX handler. - * @note Called by the received byte interrupt. - */ -void USART2_IRQHandler(void) + * @brief Copies the received bytes into the user-accessible queue. + * Reads all the available bytes in the DMA RX buffer, and copies them to the + * user-accessible queue. + * @remark This function must called often, otherwise the DMA RX buffer may be + * full. + */ +void uart_Step(void) { - if(USART_GetITStatus(USART2, USART_IT_RXNE)) + // Get the location of the location currently pointed by the DMA. + uint8_t const * head = uart_rxBuffer + UART_RX_BUFFER_SIZE + - DMA_GetCurrDataCounter(UART_RX_DMA); + + // Even if the STM32F4 reference manual (RM0090) states that the NDTR + // register is decremented after the transfer, this is not the case. + // So we wait a few cycles to be sure that the DMA actually performed the + // transfer. + utils_DelayUs(1); + + // RX: add the received bytes into the user queue. + while(uart_rxBuffTail != head) { - uint8_t rxData = USART2->DR; // Read the DataRegister (also clears the RX interrupt pending bit). - - // Call the external RX byte handler. - uart_rxHandler(rxData); + uint8_t b = *uart_rxBuffTail; + cb_Push(&uart_rxQueue, b); + + uart_rxBuffTail++; + + if(uart_rxBuffTail >= uart_rxBuffer + UART_RX_BUFFER_SIZE) + uart_rxBuffTail -= UART_RX_BUFFER_SIZE; } } +/** + * @brief Gets the user-accessible queue of the received bytes. + * @return the address of the queue. + */ +cb_CircularBuffer* uart_GetRxQueue(void) +{ + return &uart_rxQueue; +} /** - * @brief Sent a byte through UART. - * @param data: byte to send through UART. - * @note This function blocks until the byte is sent. - */ -void uart_SendByte(uint8_t data) + * @brief Starts the DMA transfer to send bytes to UART peripheral. + */ +void uart_StartDma(void) { - while( !(USART_PC_COMM->SR & 0x00000040) ) // Wait until data register is empty. - ; - USART_SendData(USART_PC_COMM, data); + DMA_InitTypeDef DMA_InitStruct; + + // + uart_nBytesToTransferDma = uart_txBufferIndex; + uart_currentTxBufferToWriteTo = !uart_currentTxBufferToWriteTo; + uart_txBufferIndex = 0; + + // Start the DMA transfer. + DMA_DeInit(UART_TX_DMA); + + DMA_InitStruct.DMA_Channel = UART_TX_DMA_CHANNEL; + DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; + DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&uart_txBuffer[!uart_currentTxBufferToWriteTo][0]; + DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStruct.DMA_BufferSize = uart_nBytesToTransferDma; + DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; + DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; + DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + DMA_Init(UART_TX_DMA, &DMA_InitStruct); + + DMA_Cmd(UART_TX_DMA, ENABLE); } /** - * @brief Sent an array of bytes through UART. - * @note This function blocks until all the bytes are sent. - * @param data: bytes array to send through UART. - * @param length: number of bytes in the array. - */ -void uart_SendBytes(uint8_t *data, int length) + * @brief Asynchronously sends the given byte through the UART bus. + * @param data the data byte to send. + */ +void uart_SendByteAsync(uint8_t data) +{ + // Check that it is possible to write in the current buffer. + if(uart_txBufferIndex >= UART_TX_BUFFER_SIZE) + { + // If the DMA is idle, switch to the other buffer. + if(UART_DMA_TX_IS_BUSY) + { + utils_TrapCpu(); // Error, can't write anywhere! + return; + } + else + uart_StartDma(); + } + + // Write the byte in the buffer. + uart_txBuffer[uart_currentTxBufferToWriteTo][uart_txBufferIndex] = data; + uart_txBufferIndex++; + + // If the buffer is full, start the DMA transfer. + if(uart_txBufferIndex == UART_TX_BUFFER_SIZE) + uart_StartDma(); +} + +/** + * @brief Asynchronously sends the given bytes through the UART bus. + * @param data pointer to the data bytes array to send. + * @param length number of bytes to send (array size). + * @remark The bytes may not be send immediately, they are stored temporarily in + * an intermediate buffer that the DMA will copy to UART peripheral, when it is + * full. Call uart_FlushTx() to start the DMA transfer immediately. + */ +void uart_SendBytesAsync(uint8_t *data, int length) { - int i; + while(length > 0) + { + uint16_t nBytesToWriteInBuf; - for(i=0; i= UART_TX_BUFFER_SIZE) + { + // If the DMA is idle, switch to the other buffer. + if(UART_DMA_TX_IS_BUSY) + { + utils_TrapCpu(); // Error, can't write anywhere! + return; + } + else + uart_StartDma(); + } + + // Write as many bytes as possible in the current buffer. + nBytesToWriteInBuf = UART_TX_BUFFER_SIZE - uart_txBufferIndex; + + if(nBytesToWriteInBuf > length) + nBytesToWriteInBuf = length; + + memcpy(&uart_txBuffer[uart_currentTxBufferToWriteTo][uart_txBufferIndex], + data, nBytesToWriteInBuf); + uart_txBufferIndex += nBytesToWriteInBuf; + + data += nBytesToWriteInBuf; + length -= nBytesToWriteInBuf; + + // If the buffer is full, start the DMA transfer. + if(uart_txBufferIndex == UART_TX_BUFFER_SIZE) + uart_StartDma(); + } +} + +/** + * @brief Start the DMA to send the bytes waiting in the intermediate buffer. + */ +void uart_FlushTx(void) +{ + if(uart_txBufferIndex > 0 && !UART_DMA_TX_IS_BUSY) + uart_StartDma(); } diff --git a/Firmware/src/drivers/uart.h b/Firmware/src/drivers/uart.h index 9999532..8a83d89 100644 --- a/Firmware/src/drivers/uart.h +++ b/Firmware/src/drivers/uart.h @@ -1,48 +1,52 @@ #ifndef __UART_H #define __UART_H #include "../main.h" +#include "../lib/circular_buffer.h" #define USART_RX_Pin GPIO_Pin_5 #define USART_RX_PinSource GPIO_PinSource5 #define USART_RX_Port GPIOD #define USART_TX_Pin GPIO_Pin_6 #define USART_TX_PinSource GPIO_PinSource6 #define USART_TX_Port GPIOD #define USART_PC_COMM USART2 // UART peripheral used for the comm with the PC. #define USART_BAUDRATE 1843200 //921600 // USART baudrate [b/s]. /** @defgroup UART Driver / UART * @brief Driver for the UART serial communication peripheral. * * This driver controls the UART peripheral of the STM32, connected to the * USB-to-serial chip. * * Call uart_Init() first in the initialization code, specifiying the function * to call when a byte arrives (sent from the computer). To send data, call * uart_SendByte() or uart_SendBytes(). * * Note that this module should not be used directly. It is a better option to * use it through the Communication module, to benefit from the already * implemented communication protocol between the board and MATLAB. * * @addtogroup UART * @{ */ /** * Typedef for a pointer to a function to call automatically when a byte * arrives from the computer. */ typedef void (*uart_rxByteHandlerFunc)(uint8_t rxByte); -void uart_Init(uart_rxByteHandlerFunc f); -void uart_SendByte(uint8_t data); -void uart_SendBytes(uint8_t *data, int length); +void uart_Init(void); +void uart_Step(void); +cb_CircularBuffer* uart_GetRxQueue(void); +void uart_SendByteAsync(uint8_t data); +void uart_SendBytesAsync(uint8_t *data, int length); +void uart_FlushTx(void); /** * @} */ #endif diff --git a/Firmware/src/lib/circular_buffer.c b/Firmware/src/lib/circular_buffer.c new file mode 100644 index 0000000..3f847b6 --- /dev/null +++ b/Firmware/src/lib/circular_buffer.c @@ -0,0 +1,109 @@ +#include "circular_buffer.h" +#include "utils.h" + +/** + * @brief Initializes a cb_CircularBuffer structure. + * Initializes a cb_CircularBuffer structure with the given buffer. The buffer + * has to be provided by the user, to avoid dynamic memory allocation. + * @param cb the cb_CircularBuffer structure to initialize. + * @param buffer pointer to an existing array. + * @param bufferSize length of the circular buffer. The given buffer should have + * a size greater or equal to this value. + */ +void cb_Init(cb_CircularBuffer* cb, uint8_t *buffer, uint16_t bufferSize) +{ + cb->buffer = buffer; + cb->bufferSize = bufferSize; + cb->readIndex = 0; + cb->writeIndex = 0; +} + +/** + * @brief Gets the number of bytes stored in the queue. + * @param cb the cb_CircularBuffer to check. + * @return the number of bytes stored in the queue. + */ +uint16_t cb_ItemsCount(cb_CircularBuffer* cb) +{ + if (cb->writeIndex >= cb->readIndex) + return cb->writeIndex - cb->readIndex; + else + return cb->bufferSize - cb->readIndex + cb->writeIndex; +} + +/** + * @brief Check if the queue is empty. + * @param cb the cb_CircularBuffer to check. + * @return 1 if there are no bytes stored in the queue, 0 otherwise. + */ +bool cb_IsEmpty(cb_CircularBuffer* cb) +{ + return cb->writeIndex == cb->readIndex; +} + +/** + * @brief Check if the queue is full. + * @param cb the cb_CircularBuffer to check. + * @return 1 if the queue is full, 0 otherwise. + */ +bool cb_IsFull(cb_CircularBuffer* cb) +{ + if (cb->writeIndex == cb->readIndex) + return false; + else if (cb->readIndex < cb->writeIndex) + return (cb->readIndex == 0 && cb->writeIndex == cb->bufferSize - 1); + else + return (cb->writeIndex == cb->readIndex-1); +} + +/** + * @brief Add a item at the back of the queue. + * @param cb the cb_CircularBuffer to affect. + * @param newElem the byte to add to the back of the queue. + * @warning If the queue is already full, this function does nothing if + * CPU_TRAPS_ENABLED is 0. If CPU_TRAPS_ENABLED is 1, this function will block + * forever the program execution, so the problem can be found with the + * debugger. + */ +void cb_Push(cb_CircularBuffer* cb, uint8_t newElem) +{ + if (!cb_IsFull(cb)) // Not full. + { + cb->buffer[cb->writeIndex] = newElem; + cb->writeIndex++; + + if (cb->writeIndex >= cb->bufferSize) + cb->writeIndex = 0; + } + else // Error, can't push an item, because the queue is full. + utils_TrapCpu(); +} + +/** + * @brief Extract the item at the front of the queue. + * Returns the value of the item at the front of the queue, and remove this item + * from the queue. + * @param cb the cb_CircularBuffer to affect. + * @return the value of the byte that has been extracted from the queue. + * @warning If the queue is empty and CPU_TRAPS_ENABLED is 0, this function + * returns 0. If CPU_TRAPS_ENABLED is 1, this function will block forever the + * program execution, so the problem can be found with the debugger. + */ +uint8_t cb_Pull(cb_CircularBuffer* cb) +{ + if (cb->writeIndex != cb->readIndex) // Not empty. + { + uint8_t pulled = cb->buffer[cb->readIndex]; + cb->readIndex++; + + if (cb->readIndex >= cb->bufferSize) + cb->readIndex = 0; + + return pulled; + } + else // Error, can't pull an item, because the queue is empty. + { + utils_TrapCpu(); + return 0; + } +} diff --git a/Firmware/src/lib/circular_buffer.h b/Firmware/src/lib/circular_buffer.h new file mode 100644 index 0000000..5d628ba --- /dev/null +++ b/Firmware/src/lib/circular_buffer.h @@ -0,0 +1,40 @@ +#ifndef __CIRCULAR_BUFFER_H +#define __CIRCULAR_BUFFER_H + +#include "../main.h" + +/** @defgroup CircularBuffer Circular buffer + * @brief Bytes queue (FIFO) implemented with a circular buffer. + * + * This bytes container works as a queue, which means "first in, first out". + * Create a cb_CircularBuffer structure, and initialize it with cb_Init(). + * Then, add bytes using cb_Push(), and extract them with cb_Pull(). + * + * @ingroup Lib + * @addtogroup CircularBuffer + * @{ + */ + +/** + * @brief Circular buffer structure. + */ +typedef struct +{ + uint8_t *buffer; ///< Pointer to the byte buffer. + uint16_t bufferSize; ///< Size of buffer. + volatile uint16_t readIndex, ///< Index of the element at the front of the queue. + writeIndex; ///< Index of the next free location at the end of the queue. +} cb_CircularBuffer; + +void cb_Init(cb_CircularBuffer* cb, uint8_t *buffer, uint16_t bufferSize); +uint16_t cb_ItemsCount(cb_CircularBuffer* cb); +bool cb_IsEmpty(cb_CircularBuffer* cb); +bool cb_IsFull(cb_CircularBuffer* cb); +void cb_Push(cb_CircularBuffer* cb, uint8_t newElem); +uint8_t cb_Pull(cb_CircularBuffer* cb); + +/** + * @} + */ + +#endif diff --git a/Firmware/src/lib/utils.c b/Firmware/src/lib/utils.c index 3a1a5f5..2b8ac8b 100644 --- a/Firmware/src/lib/utils.c +++ b/Firmware/src/lib/utils.c @@ -1,87 +1,99 @@ #include "utils.h" +/** + * @brief Endless loop function to stop the execution of the program here. + * @note This function does nothing if CPU_TRAPS_ENABLED is set to zero. + */ +void utils_TrapCpu(void) +{ +#if CPU_TRAPS_ENABLED + while(1) + ; +#endif +} + /** * @brief "Busy wait" delay function * @param duration: Delay time in [us] (approximative value based on a 168MHz core clock) */ void utils_DelayUs(uint32_t duration) { uint32_t i = 0; uint32_t j = 0; for(i=0; i<=duration; i++) { for(j=0; j<=26; j++) { __asm{NOP}; __asm{NOP}; } } } /** * @brief "Busy wait" delay function * @param duration: Delay time in [ms] (approximative value based on a 168MHz core clock). * @note This delay is approximative, and may last longer if there are many interrupts. */ void utils_DelayMs(uint32_t duration) { uint32_t i = 0; uint32_t j = 0; for(i=0; i<=duration; i++) { for(j=0; j<=33600; j++) { __asm{NOP}; } } } /** * @brief Saturate a float number between two bounds. * @param val: value to constrain between two limits. * @param min: minimum * @param max: maximum * @retval None. */ void utils_SaturateF(float32_t *val, float32_t min, float32_t max) { if(*val < min) *val = min; else if(*val > max) *val = max; } /** * @brief Saturate an integer number between two bounds. * @param val: value to constrain between the two limits. * @param min: lower limit. * @param max: upper limit. * @retval None. */ void utils_SaturateU(uint32_t *val, uint32_t min, uint32_t max) { if(*val < min) *val = min; else if(*val > max) *val = max; } /** * @brief Compute the mean of the array values. * @param array: array of float number to get the mean from. * @param size: size of the array. * @retval the mean of the array values. */ float32_t utils_Mean(float32_t *array, int size) { int i; float32_t sum = 0.0f; for(i=0; i +#include #include "arm_math.h" // Clocks configuration. #define APB1_PRESCALER 4 #define APB2_PRESCALER 2 #define TIM_MULTIPLIER 2 // Interupt priority. #define CURRENT_LOOP_IRQ_PRIORITY 1 // High freq loop, should not be interrupted. #define CONTROL_LOOP_IRQ_PRIORITY 2 #define CODER_INDEX_IRQ_PRIORITY 2 // Useless, remove? #define UART_RX_IRQ_PRIORIY 3 #define DATA_LOOP_IRQ_PRIORITY 4 // Streaming packets, lowest priority. // Electrical parameters. #define STM_SUPPLY_VOLTAGE 3.3f // [V]. #define H_BRIDGE_SUPPLY_VOLTAGE 24.0f // [V]. #define CURRENT_SHUNT_RESISTANCE 0.01f // [ohm]. #define CURRENT_SHUNT_AMPLIFIER_GAIN 50 // []. #define MOTOR_RESISTANCE 10.6f + 5.0f // 10.6 ohm according to the datasheet, actually more, depends on the motor [ohm]. // Mechanical parameters. #define REDUCTION_RATIO 16.5f #define MOTOR_TORQUE_CONST 0.0538f // [N.m/A]. // Status register bit list. #define STREG_0 ((uint32_t)0x00000001) #define STREG_1 ((uint32_t)0x00000002) #define REF_POS_INIT ((uint32_t)0x00000004) // 1 -> Ref position succesfully found #define CALIBRE ((uint32_t)0x00000008) // 1 -> Calibration done #define RUN_CURRENT_LOOP ((uint32_t)0x00000010) // 1 -> Current loop active (if motorDrv eneable but RUN_CURRENT_LOOP=false -> fixed 50% PWM on the motor) #define UN_CONTROL_LOOP ((uint32_t)0x00000020) // 1 -> Control loop active #define STREG_6 ((uint32_t)0x00000040) #define STREG_7 ((uint32_t)0x00000080) #define STREG_8 ((uint32_t)0x00000100) #define DATA_UPSTREAM_ENB ((uint32_t)0x00000200) // 1 -> data upstream active #define STREG_10 ((uint32_t)0x00000400) #define RX_COMPLETED ((uint32_t)0x00000800) // 1 -> packet recieved #define RX_ERROR ((uint32_t)0x00001000) // 1 -> last received packet incorrect #define PROCESSING_REQUEST ((uint32_t)0x00002000) // 1 -> processing last received packet in progress (can not receive new data) #define LAST_REQUEST_IGNORED ((uint32_t)0x00004000) #define STREG_15 ((uint32_t)0x00008000) #define STREG_16 ((uint32_t)0x00010000) #define STREG_17 ((uint32_t)0x00020000) #define STREG_18 ((uint32_t)0x00040000) #define STREG_19 ((uint32_t)0x00080000) #define STREG_20 ((uint32_t)0x00100000) #define STREG_21 ((uint32_t)0x00200000) #define STREG_22 ((uint32_t)0x00400000) #define STREG_23 ((uint32_t)0x00800000) #define INDEX_ERROR ((uint32_t)0x01000000) // 1 -> wrong step count over 1 rotation #define STREG_25 ((uint32_t)0x02000000) #define STREG_26 ((uint32_t)0x04000000) #define STREG_27 ((uint32_t)0x08000000) #define STREG_28 ((uint32_t)0x10000000) #define STREG_29 ((uint32_t)0x20000000) #define STREG_30 ((uint32_t)0x40000000) #define STREG_31 ((uint32_t)0x80000000) #endif