Форум программистов, компьютерный форум, киберфорум
Микроконтроллеры ATmega AVR
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
5 / 5 / 0
Регистрация: 03.09.2013
Сообщений: 79

Некорректно работает прошивка. Должна мигать светодиодом при переполнении TIMER0

23.09.2025, 17:24. Показов 2676. Ответов 47

Студворк — интернет-сервис помощи студентам
Здравствуйте!

Когда-то давно, для общего развития, учился писать на ассемблере для avr и достиг неких результатов. Всё благодаря участникам данного форума, которые проявили ко мне невероятную терпимость!

Теперь было решено, одну из программ переписать с ассемблера на си в рамках факультатива. Но что-то пошло не так и я не смог разобраться что именно, но зацепка есть.

Программа на ассембере
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "def.h"
 
#define LED_PIN 5           
#define T0MAX   40          ; Max value for t0c counter.
 
 
.data
t0c:    .byte 1             ; Counter for timer.
led_on: .byte 1             ; Boolean for led.
 
 
.text                   ;+Interrupts section
.org 0x000
    jmp reset           ; Reset Handler.
.org 0x040
    jmp tim0ovf         ; Timer0 Overflow Handler.
.org 0x06a              ;-Interrupts section
main:
    rjmp main           ; Buzzer.
;main:
 
 
reset:
    ldi r16, hi8(RAMEND)        ; Stack init.
    out SPH, r16
    ldi r16, lo8(RAMEND)
    out SPL, r16
 
    ldi r16, (1<<LED_PIN)       ; High output for led5.
    out DDRB, r16
 
    ldi r16, 1          ; Start with led on.
    ldi XL, lo8(led_on)
    ldi XH, hi8(led_on)
    st X, r16
 
    ldi r16, 0          ; Set counter value.
    ldi XL, lo8(t0c)
    ldi XH, hi8(t0c)
    st X, r16
 
    rcall tim0init
 
    sei             ; Global Interrupt Enable.
 
    rjmp main           ; Start program.
;reset:
 
 
tim0init:
    push r16
 
    ldi r16, 0x00
    out TCNT0, r16          ; Set Timer0 zero time.
    ldi r16, (1<<1)
    sts TIMSK0, r16         ; Set interrupt Timer0 overflow TCNT0.
    ldi r16, 0b00000101
    out TCCR0B, r16         ; Set tick. 14.9.2 of datasheet.
    
    pop r16
    ret
;tim0init:
 
tim0ovf:                ; Timer0 Overflow Handler.
    ldi XL, lo8(t0c)        ; t0c to r16;
    ldi XH, hi8(t0c)
    ld r16, X
 
    inc r16             ; Increment t0c.
    cpi r16, T0MAX          ; Compare with T0MAX.
    brlo 1f             ; Skip if lower.
 
    ldi r16, 0          ; Reset t0c.
 
    ldi YL, lo8(led_on)     ; led_on to r17.
    ldi YH, hi8(led_on)
    ld r17, Y       
 
    cpi r17, 1          ; Is led_on?
    breq 2f             ; If equal jmp.
 
    inc r17             ; led_on = true
    ldi r18, (1<<LED_PIN)
    out PORTB, r18
    rjmp 3f
2:  
    dec r17             ; led_on = false
    ldi r18, (0<<LED_PIN)
    out PORTB, r18
3:
    st Y, r17
1:
    st X, r16           ; Load new value to t0c.
    reti
;tim0ovf:
Содержимое файла `def.h`:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef DEF_H_SENTY
#define DEF_H_SENTY
 
#define DDRB 0x04                                                                 
#define PORTB 0x05                                                                
 
#define XL r26
#define XH r27
#define YL r28
#define YH r29
                    /* Stack pointer. */
#define SPL 0x3D            /* 6.5.1 of datasheet. */
#define SPH 0x3E            /* 6.5.1 of datasheet. */
                    /* RAM pointers. */
#define SRAMSTART 0x100         /* 7.3 of datasheet. */
#define RAMEND  0x8FF           /* 7.3 of datasheet. */
                    /* timer0 section. */
#define TCCR0B 0x25         /* 14.9.2 of datasheet. */                          
#define TIMSK0 0x6E         /* 14.9.6 of datasheet. Interrupt Mask. */
#define TCNT0 0x26          /* 14.9.3 of datasheet. Timer Register. */
 
#endif
Моя попытка переписать это на С:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <avr/io.h>
#include <avr/interrupt.h>
 
enum { pin_led = 5, tim0_max = 40 };
 
volatile int g_tim0_counter = 0;
volatile char g_is_led_on = 1; /* Starts on. */
 
#if 1
ISR(TIMER0_OVF_vect)
{
    g_tim0_counter++;
 
    if (g_tim0_counter > tim0_max) {
        g_tim0_counter = 0;
        
        g_is_led_on = !g_is_led_on;
 
        PORTB = (g_is_led_on<<pin_led);
    }
}
#endif
 
static void init_led()
{
    DDRB = 1<<pin_led;      /* High output for led. */
    PORTB = g_is_led_on<<pin_led;   /* Switch led state. */
}
 
static void init_timer0()
{
    TCNT0 = 0;          /* T0 zero time. */
    TIMSK0 = 1<<1;          /* Set interrupt T0 overflowf TCNT0 */
    TCCR0B = 0b0000101;     /* Set 1/1024 tick. */
}
 
static void init()
{
    /* Is stack init needed?.. */
    init_led();
    init_timer0();
    sei();              /* Enable interrupts. */
}
 
int main()
{
    init();
    for (;;) {
    /* Just chill. */
    }
}
И `Makefile` им собирается, что ассемблерная, что сишная версия:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FILENAME = blink_led_tim0
 
MCU = atmega328p
OBJCOPY = avr-objcopy
BAUDRATE = 57600
 
#compiler
GCC = avr-gcc
GCC_FLAGS = -Wall -g -nostdlib -ansi -short-enums
 
#flash
AVRDUDE_FLAGS = -F -V -D
    # -D -- Disable auto-erase for flash. Helps save the bootloader.
    # -F -- Helps if device signature was erased.
    # -V
 
build: $(FILENAME).c
    $(GCC) $(GCC_FLAGS) -mmcu=$(MCU) $< -o $(FILENAME).elf
 
flash: $(FILENAME).elf
    avrdude -b $(BAUDRATE) -c usbasp -p $(MCU) -P usb -U flash:w:$<
Ожидалось, что светодиод будет равномерно мигать, но нет. Мигает неравномерно, а значение в DDRB для светодиода не 1, а 0. Судя по всему, выполняется только подпрограмма `ISR(TIMER0_OVF_vect)`, а `main()` нет. `main()` переносил в начало исходного кода, оно отрабатывало, а всё что в `ISR(TIMER0_OVF_vect)` -- нет.

Проверил с помощью `avr-objdump`, судя по всему, некорректно формируются первые ячейки program memory, через которые и происходит прыжки при прерывании.

`avr-objdump` программы на ассемблере:
Assembler
1
2
3
4
5
6
7
8
9
Disassembly of section .text:
 
00000000 <__ctors_end>:
   0:   0c 94 36 00     jmp     0x6c    ; 0x6c <reset>
        ...
 
00000040 <.Loc.1>:
  40:   0c 94 51 00     jmp     0xa2    ; 0xa2 <tim0ovf>
        ...
`avr-objdump` программы на си:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Disassembly of section .text:
 
00000000 <__vector_16>:
   0:   1f 92           push    r1
   2:   0f 92           push    r0
   4:   0f b6           in      r0, 0x3f        ; 63
   6:   0f 92           push    r0
   8:   11 24           eor     r1, r1
   a:   2f 93           push    r18
   c:   8f 93           push    r24
   e:   9f 93           push    r25
  10:   ef 93           push    r30
  12:   ff 93           push    r31
  14:   cf 93           push    r28
  16:   df 93           push    r29
  18:   cd b7           in      r28, 0x3d       ; 61
  1a:   de b7           in      r29, 0x3e       ; 62
 
0000001c <.Loc.1>:
  1c:   80 91 02 01     lds     r24, 0x0102     ; 0x800102 <g_tim0_counter>
  20:   90 91 03 01     lds     r25, 0x0103     ; 0x800103 <g_tim0_counter+0x1>
  24:   01 96           adiw    r24, 0x01       ; 1
  26:   90 93 03 01     sts     0x0103, r25     ; 0x800103 <g_tim0_counter+0x1>
  2a:   80 93 02 01     sts     0x0102, r24     ; 0x800102 <g_tim0_counter>
 
0000002e <.Loc.2>:
  2e:   80 91 02 01     lds     r24, 0x0102     ; 0x800102 <g_tim0_counter>
  32:   90 91 03 01     lds     r25, 0x0103     ; 0x800103 <g_tim0_counter+0x1>
 
00000036 <.Loc.3>:
  36:   87 39           cpi     r24, 0x97       ; 151
  38:   91 05           cpc     r25, r1
  3a:   b4 f0           brlt    .+44            ; 0x68 <.L4>
 
0000003c <.Loc.4>:
  3c:   10 92 03 01     sts     0x0103, r1      ; 0x800103 <g_tim0_counter+0x1>
  40:   10 92 02 01     sts     0x0102, r1      ; 0x800102 <g_tim0_counter>
Как это дело поправить? Изучал исходники других проектов. Вроде всё должно быть нормально, но всё же что-то упускаю.

Добавлено через 1 час 13 минут
Смог найти в чём проблема. Нужно убрать флаг `-nostdlib`.

Добавлено через 56 минут
Гыгы, ещё в ассемблерной части, была ошибка, было так:
Assembler
1
2
ldi r16, (1<<1)
sts TIMSK0, r16         ; Set interrupt Timer0 overflow TCNT0.
А должно было быть так:
Assembler
1
2
ldi r16, (1<<0)
sts TIMSK0, r16         ; Set interrupt Timer0 overflow TCNT0.
Но на ассемблере оно в обоих случая работает. А вот на сишке нет...

А совсем правильно так:
C
1
TIMSK0 = 1<<TOIE0; /* TOIE0 -- бит под номером 0 отвечающий за перерыванию по переполнению таймера0.  */
1
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
23.09.2025, 17:24
Ответы с готовыми решениями:

SysTick можно ли с его помощью мигать светодиодом
Снова здравствуйте!) Перешел к изучению SysTck Рассмотрел примеры, в которых на основе этого...

Не работает TIMER0 OVF
Всем привет, помогите пожалуйста разобраться с кодом, он не входит в обработчик прерываний хотя...

Не работает TIMER0 OVF
Всем привет, подскажите почему программа не входит в обработчик прерывания хотя флаг выставляется,...

47
51 / 49 / 2
Регистрация: 23.01.2019
Сообщений: 378
28.09.2025, 14:44
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Я, когда писал для AVR на асме, прописывал таблицу полностью, со всеми reti. Правда, без .org - не знал тогда про них.
Я сначала также. ОРГи нужны для 328-й и прочих. Для 8-й меги орг-и не нужны.

Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
И даже если исправить 0b0000_0001 на чуть менее синтаксически некорректное 0b00000001
Как раз очень читабельно, особенно когда например отправлять по интерфейсу байт в котором несколько данных, типа:
0b01_110_11_0, и аврасм такое написание принимает, что радует.

Ещё по поводу записи 0b0000_0001. Дело в том, что в даташите на 328-ю с ошибкой описаны биты регистра TIMSK0

Должно быть OCIE0B OCIE0A TOIE0.
И что бы не разбираться куда ставить 0, в конец или перед А/В, удобнее порой написать 0b00000001, но ни как не (1<<0)
Возможно поэтому ТС и написал (1<<1), так как компилятор не принял у него TOIE.
А "скопировал" бы таблицу в бинарное представление байта, то ошибки бы не было.
0
658 / 411 / 76
Регистрация: 21.09.2008
Сообщений: 1,411
28.09.2025, 15:10
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
в стандарт языка С ввели наконец 0b или оно так и остается "нестандартным"
В рабочем черновике N2596 ISO/IEC 9899:202x (E) от December 11, 2020 в разделе 6.4.4 есть упоминание.
Миниатюры
Некорректно работает прошивка. Должна мигать светодиодом при переполнении TIMER0  
0
5 / 5 / 0
Регистрация: 03.09.2013
Сообщений: 79
28.09.2025, 15:53  [ТС]
Цитата Сообщение от locm Посмотреть сообщение
Проще будет прошивку отредактировать.
Хорошая идея! У меня получилось.

1) В сишной программе включаю "неправильное" прерывание:
C
1
TIMSK0 = 1<<1;
Собрал, прошил. Светодиод горит и не моргает, прерывание не работает.

2) Использую `avr-objdump -D` к собранной прошивке, интересующие строки:

Code
1
2
3
38:   0c 94 51 00     jmp     0xa2    ; 0xa2 <__bad_interrupt>
3c:   0c 94 51 00     jmp     0xa2    ; 0xa2 <__bad_interrupt>
40:   0c 94 53 00     jmp     0xa6    ; 0xa6 <__vector_16>
3) (Далее я пытался использовать `hexdump`, в интернетах пишут, что у него есть флаг `-R` для revert, но по факту его не оказалась, к счастью, оказалось команда `xxd` умеет не только делать заголовочные файлы для бинарных данных, так ещё получать hex и делать revert).

Делаю `xxd blink_led_tim0.elf > firm.hex`, находим в нём `0c 94 53 00`, которые являются `jmp 0xa6` и предыдущие две последовательности `0c 94 51 00` заменяем на `00 00 00 00` (вроде как nop всё же 00, а не ff, а может то и то, фиг знает).

Далее hex обратно в бинарник кидаем `xxd -r firm.hex > firm.bin`, зашиваем `firm.bin` и... Светодиод мигает, не смотря на то, что прерывание стоит неправильное.


Подытожу. Делал я это просто для себя в рамках факультатива, пользы от этого не то чтобы много, но удовольствие получил. Без вас бы не получилось, большая всем благодарность!

Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Ну, знания лишними не бывают.
Ну-с, тут я с вами всё же не согласен. Знаний лишних полно. Но, думаю, в конкретном случае это не лишнее.
1
 Аватар для COKPOWEHEU
4147 / 2725 / 433
Регистрация: 09.09.2017
Сообщений: 12,073
28.09.2025, 16:23
Цитата Сообщение от shonty Посмотреть сообщение
Я сначала также. ОРГи нужны для 328-й и прочих. Для 8-й меги орг-и не нужны.
С ними все-таки надежнее.
Цитата Сообщение от shonty Посмотреть сообщение
Как раз очень читабельно, особенно когда например отправлять по интерфейсу байт в котором несколько данных, типа:
0b01_110_11_0
Какая разница насколько читабельно (все равно ведь хуже сдвигов), если не компилируется.
Цитата Сообщение от shonty Посмотреть сообщение
удобнее порой написать 0b00000001
И лазить потом по даташиту, проверяя что же хотел сказать автор этим магическим чиселком? Нет уж спасибо. А еще веселее этот подход будет выглядеть для 32-битных контроллеров.
И да, там действительно иногда приходится вручную ставить биты, поскольку китайцам прописать все дефайны было лень.
В общем, лучше сразу не привыкать к плохому стилю. Если выставляются именованные биты, пишем (1<<bitName). Если поименовать их забыли, пишем (1<<x). А запись 0bxxx оправдана только для битовых полей TCCR1B = (0b11<<WGM12) | (0b001<<CS10), и то только потому что для них производитель почти никогда не пишет констант. Приходится пояснять в комментариях.
Цитата Сообщение от sharpey Посмотреть сообщение
В рабочем черновике
А, с c23. Значит, пока толком воспользоваться не выйдет.
Цитата Сообщение от Medusa Kaiser Посмотреть сообщение
вроде как nop всё же 00, а не ff, а может то и то, фиг знает
0xFF - байты, остающиеся в флешке после стирания. "некорректная команда", которая интерпретируется как тот же nop.
1
51 / 49 / 2
Регистрация: 23.01.2019
Сообщений: 378
28.09.2025, 16:31
The hexadecimal opcode for the NOP (No Operation) instruction in AVR microcontrollers is 0x0000

x86 / x86-64: 0x90
Intel 8048 / MCS-48 family: 0x00
Intel 8051 / MCS-51 family: 0x00
Intel 8080 / Intel 8085 / Z80: 0x00
Intel i860: 0xA0000000

По погоде вобщем))
1
 Аватар для COKPOWEHEU
4147 / 2725 / 433
Регистрация: 09.09.2017
Сообщений: 12,073
28.09.2025, 23:50
Цитата Сообщение от shonty Посмотреть сообщение
По погоде вобщем
Мы говорим все-таки про AVR, там nop это и правда 0x0000.
А 0xFFFF это, повторяю, чистая флеш, которая интерпретируется как "Illegal instruction". Точнее, интерпретировалась бы бы, поддерживай AVR такую ситуацию. В более мощных камнях это приводит к исключению (это почти как прерывание, но генерируется не периферией, а самим кодом), но здесь просто игнорируется.
Миниатюры
Некорректно работает прошивка. Должна мигать светодиодом при переполнении TIMER0  
1
1507 / 524 / 75
Регистрация: 22.09.2023
Сообщений: 1,654
03.10.2025, 11:05
Цитата Сообщение от shonty Посмотреть сообщение
Я сначала также. ОРГи нужны для 328-й и прочих. Для 8-й меги орг-и не нужны.
Не совсем так. Размер памяти меги8 и более мелких позволяет "дотянуться" из любого адреса программной памяти (в том числе и из вектора) до любого другого адреса командой RJMP, которая занимает 2 байта/1 слово. Поэтому команды JMP у них нет и вектора расположены с таким шагом, чтобы туда влезла команда RJMP. У 16-й и выше меги "дальности" RJMP на всю память уже не хватает, для некоторых обработчиков может потребоваться команда JMP, которая занимает 4 байта/2 слова. Поэтому вектора у 16-й и более крупных мег расположены с шагом 4 байта - чтобы туда влезла JMP. И если всю таблицу заполнить командами JMP - никакие .ORG не потребуются. Однако никто не запрещает на больших мегах в подходящих случаях использовать в векторах более быструю RJMP или RETI, которые занимают по 2 байта и вот после них уже нужен или .ORG или любая (она все равно никогда не будет выполняться) команда размером 2 байта.
0
 Аватар для COKPOWEHEU
4147 / 2725 / 433
Регистрация: 09.09.2017
Сообщений: 12,073
03.10.2025, 12:14
В любом случае явная запись адресов нагляднее и безопаснее просто таблицы. Если что, можно свериться с даташитом, а не считать jrmp-ы и не гадать не удалилась ли случайно одна строчка.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
03.10.2025, 12:14

PIC 16F876 Не работает прерывание от внутреннего TIMER0
Что я сделал не так? Работу проверяю через протеус. Компилятор XC8 из MPLAB #pragma config...

Мигание светодиода по переполнению счетчика
Проблема в том, что при проверке в AVRStudio, програмка не выходит из внутреннего цикла, где может...

Работа с прерыванием TIMER0. Atmega328p. Arduino nano
Хочу по прерыванию TIM0_OFV зажигать светодиод PB5. .equ DDRB, 0x04 ...

MP430. Timer0. Compare mode
Здравствуйте, я с вопросом по любимому msp430f5435 Настроил работу таймера на режим сравнения...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
48
Ответ Создать тему
Новые блоги и статьи
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
Как ИИ начал спорить и врать (возможно почуяв опасность для себя от индустрии - уход от электроники).
Hrethgir 04.08.2026
Недельный диалог, на фоне событий с НПЗ. Да, из спирта можно получать бензин, и это не сложно. Но потом в схеме я решил избавиться от насоса, при этом полностью сделав контроль подачи спирта в. . .
Термопринтер QR701
Argus19 03.08.2026
Термопринтер QR701 Купил два термопринтера QR701. На сэлф-тесте написано: Language: PC936 (GB18030). Что означает, что принтеры могут печатать только латиницу и китайские иероглифы. Так же. . .
Создание формы заимствованного документа
Maks 03.08.2026
Задача: Необходимо создать собственную форму заимствованного документа. На форме должен быть реквизит "Покупатель", а также табличная часть со следующими реквизитами: - Расчетный счет покупателя. . .
Задача предоставления скидок покупателям
Maks 03.08.2026
Задача: В документе "Продажи" необходимо реализовать функционал предоставления скидок покупателям. Скидка должна автоматически рассчитываться и подставляться в соответствующее поле при выборе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru