2024-07-10 09:48 AM - edited 2024-07-10 01:56 PM
Hello, I'm practicing with dynamic indication on multidigit multisegment indicators, and run into the issue when on the screen appear some symbols that are not supposed to be printed. I'm using 3641AS, each segment is connected to PC0...7 via 2K2 resistor, and each digit's pin is connected to PC8...PC11.
The code is:
/* USER CODE BEGIN PV */
// binary presentation of numbers from 0 till 9
int numbers[10] = { 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110,
0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111 };
// the variable to be shown and changed during run of program
int value = 0;
// the decimal place to be depictured at the momen
int current_digit = 0;
// bitmasks of decimals to be depictured (including decimal point)
int digits[4] = { };
#define display_interval 5
#define count_interval 1000
uint32_t last_display_tick;
uint32_t last_count_tick;
uint32_t current_tick;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
//turn off all the LEDs
GPIOC->ODR = 0b000000000000;
refresh_digit_values();
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
current_tick = HAL_GetTick();
if (current_tick - last_count_tick > count_interval) {
value = (value == 9999) ? 0 : value + 1;
refresh_digit_values();
last_count_tick = current_tick;
};
if (current_tick - last_display_tick > display_interval) {
GPIOC->ODR = ((0b1111 ^ (1 << current_digit)) << 8)
+ digits[current_digit];
current_digit = current_digit > 3 ? 0 : current_digit + 1;
last_display_tick = current_tick;
};
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void refresh_digit_values(void) {
digits[3] = numbers[value / 1000]; // by default - integer division
digits[2] = numbers[value % 1000 / 100]; //
digits[1] = numbers[value % 100 / 10]; //
digits[0] = numbers[value % 10]; //
for (int i = 3; i > 0; i--) {
if (digits[i] == numbers[0]) {
digits[i] = 0b00000000;
} else {
break;
};
}
}
;
/* USER CODE END 4 */
THe expected activity was:
Actual output is:
The counting itself works. But the printing is a problem. Please, advise on the solution.
Solved! Go to Solution.
2024-07-11 04:12 AM - edited 2024-07-11 05:57 AM
Display interval is wrong, it's a digit interval, entire display interval is 4 times larger.
You also have an off-by-one error, you divide by 6, not 5.
Assuming hal tick is 1 millisecond, then display is only updated at only 1ms*6*4=24ms=41.7Hz.
This will lead to visible strobing when moving your head and can also lead to interference with camera exposure time.
Better increase it. I would simply leave a timer out entirely. LEDs can switch fast enough (datasheet mentions 10KHz with 10% duty cycle at 200mA, so I assume it can switch at 250Hz 25% duty cycle).
I actually recommend initially increasing the display interval and the counting interval so you can actually see things happening in case you have a bug somewhere that's setting the wrong segments.
Edit: another off-by-one bug:
current_digit = current_digit > 3 ? 0 : current_digit + 1;
current digit can become 4.
Better rewrite as:
current_digit = (current_digit + 1) % 4;
2024-07-11 04:12 AM - edited 2024-07-11 05:57 AM
Display interval is wrong, it's a digit interval, entire display interval is 4 times larger.
You also have an off-by-one error, you divide by 6, not 5.
Assuming hal tick is 1 millisecond, then display is only updated at only 1ms*6*4=24ms=41.7Hz.
This will lead to visible strobing when moving your head and can also lead to interference with camera exposure time.
Better increase it. I would simply leave a timer out entirely. LEDs can switch fast enough (datasheet mentions 10KHz with 10% duty cycle at 200mA, so I assume it can switch at 250Hz 25% duty cycle).
I actually recommend initially increasing the display interval and the counting interval so you can actually see things happening in case you have a bug somewhere that's setting the wrong segments.
Edit: another off-by-one bug:
current_digit = current_digit > 3 ? 0 : current_digit + 1;
current digit can become 4.
Better rewrite as:
current_digit = (current_digit + 1) % 4;
2024-07-11 10:42 AM - edited 2024-07-11 10:43 AM
The code was functional because the same code, but placed into interrupts on timers was working like a charm:
/* USER CODE BEGIN 4 */
void refresh_digit_values(void) {
digits[3] = numbers[value / 1000]; // by default - integer division
digits[2] = numbers[value % 1000 / 100]; //
digits[1] = numbers[value % 100 / 10]; //
digits[0] = numbers[value % 10]; //
for (int i = 3; i > 0; i--) {
if (digits[i] == numbers[0]) {
digits[i] = 0b00000000;
} else {
break;
};
}
}
;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
GPIOC->ODR = ((0b1111 ^ (1 << current_digit)) << 8)
+ digits[current_digit];
current_digit = current_digit > 3 ? 0 : current_digit + 1;
};
if (htim->Instance == TIM3) {
refresh_digit_values();
value = (value == 9999) ? 0 : value + 1;
};
}
;
/* USER CODE END 4 */
And yes, I tried different time intervals (the visual strobing started when `display_interval` is about 9). However, the use of:
current_digit = (current_digit + 1) % 4;
indeed resolved the problem when used HAL_GetTick. Thank you!
P.S. Also updated the limiter for changed value:
value = (value+1) % 10000;
2024-07-12 05:03 AM
Great to hear you got it to work. If my post helped you please mark it as the answer so the topic can be marked as solved.