时间码发生器通过两条线与MCU相连,一条是地,一条是信号线用于传输数据。信号传输使用差分曼彻斯特编码。电平±1.5V, 信号传输的速率960Hz 到 2400Hz 变化,需要软件自动适配。 时间码固定为80bit数据(bit0~bit79), 其中bit64~bit79 为同步码,固定为0011 1111 1111 1101。 部分波形如下图,
实现原理,
1. EXTI Rising/Falling 中断+定时器方式测量100次波形脉冲宽度,通过排序算法找到合适的值确定为定时器周期值Period
2. 继续用EXTI Rising/Falling 中断+定时器方式接收时间码0101值, EXTI Rising/Falling 中断时,修正定时器中断周期为Period/2。
产生定时器中断之后,取Pin 值保存,修正定时器中断周期为Period
3. 解析差分曼切斯特编码,查找同步码
流程图,
关键过程代码,
1. EXIT 中断处理
struct LtcInput_s LtcInput = {
.tim = {0},
.tim_count = 0,
.period = 0xffff,
};
static uint16_t parse_tim_of_ltc_input_get_period(void)
{
for (int i = 0; i < LTC_INPUT_BUF_SIZE; ++i) {
for (int j = i; j < LTC_INPUT_BUF_SIZE; ++j) {
if(LtcInput.tim[i] > LtcInput.tim[j]) {
uint16_t tmp = LtcInput.tim[i];
LtcInput.tim[i] = LtcInput.tim[j];
LtcInput.tim[j] = tmp;
}
}
}
uint16_t period = LtcInput.tim[10];
return period;
}
void ltc_input_timer_restart(int period)
{
htim3.Instance->SR = 0;
htim3.Instance->ARR = period;
htim3.Instance->CNT = 0;
}
void ltc_input_pin_callback(void)
{
// HAL_TIM_Base_Stop_IT(&htim3);
if(LtcInput.tim_count >= LTC_INPUT_BUF_SIZE) {
ltc_input_timer_restart(LtcInput.period/2);
} else {
LtcInput.tim[LtcInput.tim_count] = htim3.Instance->CNT;
LtcInput.tim_count++;
if(LtcInput.tim_count >= LTC_INPUT_BUF_SIZE) {
LtcInput.period = parse_tim_of_ltc_input_get_period();
}
ltc_input_timer_restart(0xffff);
}
}
2. 定时器处理
/**
* @brief This function handles TIM3 global interrupt.
*/
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
/* USER CODE END TIM3_IRQn 0 */
HAL_GPIO_TogglePin(TEST1_GPIO_Port, TEST1_Pin);
htim3.Instance->SR = 0;
htim3.Instance->ARR = LtcInput.period;
htim3.Instance->CNT = 0;
ltc_input_timer_callback();
/* USER CODE END TIM3_IRQn 1 */
}
char ltc_input_timer_callback(void)
{
GPIO_PinState status;
unsigned char x, y;
status = HAL_GPIO_ReadPin(LTC_INPUT_GPIO_Port, LTC_INPUT_Pin);
//status = GPIO_ReadInputDataBit(LTC_PORT, LTC_PIN);
//HAL_GPIO_TogglePin(TEST1_GPIO_Port, TEST1_Pin);
//GPIO_WriteBit(TEST_PORT, TEST_PIN, status);
//GPIO_WriteBit(TEST_PORT, TEST_PIN, SET);
//move_right_linear_timecode_raw(linear_timecode_raw, sizeof(linear_timecode_raw));
x = linear_timecode_count /8;
y = linear_timecode_count %8;
//buf[linear_timecode_count] = status;
linear_timecode_raw[x] &= ~(1 << y);
if(status != GPIO_PIN_RESET) {
linear_timecode_raw[x] |= (1 << y);
}
linear_timecode_count ++;
if(linear_timecode_status == LTC_STATUS_NEED_FIND_SYNC) {
if(linear_timecode_count >= sizeof(linear_timecode_raw) * 8) {
linear_timecode_count =0;
memcpy(linear_timecode_raw_ready, linear_timecode_raw, sizeof(linear_timecode_raw));
linear_timecode_ready = 1;
}
} else {
if(offset_bits > 0) {
//skip offset_bits
//If offset < linear_timecode_count, adjust the offset to the next frame.
//Next time, when count = offset, the counter is cleared. From then on, each frame starts from scratch.
if(linear_timecode_count > offset_bits) {
offset_bits += 160;
} else if(linear_timecode_count == offset_bits) {
offset_bits = 0;
linear_timecode_count = 0;
}
} else {
if(linear_timecode_count >= sizeof(linear_timecode_raw) * 8 / 2) {
linear_timecode_count =0;
memcpy(linear_timecode_raw_ready, linear_timecode_raw, sizeof(linear_timecode_raw) / 2);
linear_timecode_ready = 1;
}
}
}
return 0;
}
3. 差分曼切斯特码解码
unsigned char r_mask[] = {0x0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
unsigned char l_mask[] = {0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
static void move_right_linear_timecode_raw_7bits(unsigned char *raw, unsigned char nbytes, unsigned char nbits)
{
unsigned char i;
unsigned char tmp;
if((nbits < 1) || (nbits > 7)) {
return;
}
for(i=0; i<nbytes - 1; i++) {
tmp = raw[i+1] & r_mask[nbits];
tmp <<= (8-nbits);
raw[i] >>= nbits;
raw[i] += tmp;
}
raw[i] >>= nbits;
}
static void move_left_linear_timecode_raw_7bits(unsigned char *raw, unsigned char nbytes, unsigned char nbits)
{
unsigned char i;
unsigned char tmp;
if((nbits < 1) || (nbits > 7)) {
return;
}
for(i=0; i<nbytes - 1; i++) {
//prev_bit = raw[i] & 0x80;
tmp = raw[nbytes - i - 2] & l_mask[nbits];
raw[nbytes - i -1] <<= nbits;
tmp >>= (8-nbits);
raw[nbytes - i -1] += tmp;
}
raw[0] <<= nbits;
}
static void move_right_linear_timecode_raw_nbits(unsigned char *raw, unsigned char nbytes, unsigned short nbits)
{
unsigned char x, y;
x = nbits / 8;
y = nbits % 8;
if(x > 0) {
for (int i = 0; i < nbytes ; i++) {
raw[i] = raw[x+i];
}
}
move_right_linear_timecode_raw_7bits(raw, nbytes, y);
}
static void move_left_linear_timecode_raw_nbits(unsigned char *raw, unsigned char nbytes, unsigned short nbits)
{
unsigned char x, y;
x = nbits / 8;
y = nbits % 8;
if(x > 0) {
for (int i = 0; i < nbytes ; i++) {
raw[nbytes-i-1] = raw[nbytes-i-1-x];
}
}
move_left_linear_timecode_raw_7bits(raw, nbytes, y);
}
unsigned char get_bit_linear_timecode_raw(unsigned char index)
{
unsigned char x = index/8;
unsigned char y = index%8;
if(linear_timecode_raw[x] & (1 <<y))
return 1;
else
return 0;
}
static void parse_differential_manchester_code(unsigned char *src, unsigned char *dst, unsigned char dst_size)
{
unsigned char i, j;
unsigned short tmp, tmp2;
for(i=0; i<dst_size; i++) {
tmp = (src[i*2 +1] << 8) + src[i*2];
dst[i] = 0;
for(j=0; j<8; j++) {
tmp2 = tmp & (3 << j*2);
tmp2 >>= j*2;
if((0 != tmp2) && (3 != tmp2)) {
dst[i] |= (1<<j);
}
}
}
}
static char find_sync_code(unsigned char *src, unsigned char len, unsigned char *dst, unsigned char *valid_len)
{
const unsigned short sync_code = 0xFCBF;
unsigned char i;
unsigned short tmp;
char ret=-1;
for (i=0; i<len-8; i++) {
tmp = (src[i+8] << 8) + src[i+9];
if (tmp == sync_code) {
ret =0;
*valid_len = len - i;
memcpy(dst, &src[i], 10); //10Bytes equals 80bits
break;
}
}
return ret;
}
unsigned char ltc_dst_code[40][10]= {0};
unsigned char ltc_dst_code_count = 0;
unsigned char ltc_code_valid_len=0;
unsigned char ltc_code[20] = {0};
unsigned char raw_ready[60] = {0};
unsigned short offset_bits = 0;
unsigned short ltc_remain_bits = 0;
unsigned char sec_ltc_code[20] = {0};
unsigned char ltc_dst_code_cur[10]= {0};
unsigned char new_frame =0;
void parse_linear_timecode_raw()
{
unsigned char tmp;
unsigned char raw_mv_right_bits = 0;
if(linear_timecode_status == LTC_STATUS_NEED_FIND_SYNC) {
isjam = 0;
memcpy(raw_ready, linear_timecode_raw_ready, sizeof(linear_timecode_raw_ready));
tmp = raw_ready[0] & 0x03;
while((0 != tmp) && (3 != tmp)) {
move_right_linear_timecode_raw_nbits(raw_ready, sizeof(raw_ready), 1);
raw_mv_right_bits ++;
tmp = raw_ready[0] & 0x03;
}
for (int i = 0; i < sizeof(linear_timecode_raw_ready)*8; ++i) {
memset(ltc_code, 0 , sizeof(ltc_code));
parse_differential_manchester_code(raw_ready, ltc_code, sizeof(ltc_code));
//0xFCBF;
if((ltc_code[8] == 0xFC)&&(ltc_code[9]==0xBF)) {
linear_timecode_status = !LTC_STATUS_NEED_FIND_SYNC;
offset_bits = raw_mv_right_bits;
break;
}
move_right_linear_timecode_raw_nbits(raw_ready, sizeof(raw_ready), 1);
raw_mv_right_bits ++;
}
if(linear_timecode_status == LTC_STATUS_NEED_FIND_SYNC) {
LtcInput.tim_count = 0; //reflesh tim2 period
}
} else {
isjam = 1;
memcpy(ltc_code, linear_timecode_raw_ready, sizeof(ltc_code));
parse_differential_manchester_code(ltc_code, ltc_dst_code_cur, sizeof(ltc_dst_code_cur));
if((ltc_dst_code_cur[8] != 0xFC)||(ltc_dst_code_cur[9]!=0xBF)) {
linear_timecode_status = LTC_STATUS_NEED_FIND_SYNC;
LtcInput.tim_count = 0; //reflesh tim2 period
} else {
new_frame =1;
}
}
}
总结以及调试过程中发现的问题。
1. 测量周期,比较顺利, 56MHz , 测量出脉冲周期202 us
2. 采样过程中,发现定时器中断,在修正过程中, 出现中断异常,增加调试口翻转看波形
异常1, 中断回调,里面调用HAL_TIM_xxx , 导致反复进入定时器中断,
异常2, 定时器停止-> 修改周期 -> 定时器启动,每次启动都会中断一下,启动前清中断标志也不行
解决办法,通修改 TIM中断处理程序,直接修改寄存器。 中间不停止,最终得到正确的波形
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
/* USER CODE END TIM3_IRQn 0 */
HAL_GPIO_TogglePin(TEST1_GPIO_Port, TEST1_Pin);
htim3.Instance->SR = 0;
htim3.Instance->ARR = LtcInput.period;
htim3.Instance->CNT = 0;
ltc_input_timer_callback();
/* USER CODE END TIM3_IRQn 1 */
}