В этот раз разговор пойдёт про аппаратные интерфейсы STM8S: UART, АЦП и I2C. Каждый их этих интерфейсов поддерживает несколько режимов работы, но сейчас мне хотелось бы сфокусироваться на наиболее типовых, на мой взгляд, примерах их использования: а)организация передатчика на UART, б) режим однократного замера АЦП, в) использование I2C в режиме мастера. Напомню, что вариант использования SPI в режиме мастера я приводил на примере драйвера для 4-x разрядного семисегментного индикатора .
Документация которая понадобится для прочтения статьи: Reference Manual STM8S - RM0016, главы: 22 (UART), 24 (АЦП), 21 (I2C). В качестве целевого микроконтроллера я буду использовать 20-пиновый STM8S103F3P6.
Так же как и в прошлый раз, упор будет делаться на "чистом" программировании на Си и Ассемблере без использования сторонних библиотек. В качестве компилятора используется open-source SDCC версии 3.7. Справедливости ради замечу, что я ввёл макросы для прямого доступа к битовым инструкциям, что бы хоть как-то оптимизировать код.
Скачать полные исходники со сборочными файлам и скомпилированными прошивками, можно по ссылке в конце статьи.
В статье используются формулы в формате MathML который поддерживается браузером Firefox, для браузеров Chrome и Opera потребуется установить одноимённое расширение MathML.
Примечание от 01.09.2022г. В SDCC версии 4.2 поменялся формат передачи аргументов функций. Если раньше все аргументы передавались через стек, то теперь они передаются через регистры. Поэтому для совместимости со старым кодом следует добавлять опцию компиляции "--sdcccall 0".
Документация на чип STM8S103f3 обещает нам скорость UART протокола ~1 Mbit/sec при частоте fMASTER=16МГц. От себя замечу, что заявленная скорость доступна и без внешнего кварца или генератора.
Блок-схема модуля UART1 представлена на картинке ниже:
В первую очередь здесь бросается в глаза регистр данных - UART1_DR. Как видно, он состоит из двух независимых регистров на чтение и на запись и так же из двух независимых внутренних сдвиговых регистров. Т.е. записывая в UART1_DR и читая с него, фактически обращаешься в разным регистрам.
UART1 включает в себя один флаговый регистр UART1_SR1 и пять конфигурационных: UART1_CR1, UART1_CR2, UART1_CR3, UART1_CR4, UART1_CR5. Кроме регистра данных UART1_DR имеются также пара регистров для указания битрейта: UART1_BRR1 и UART1_BRR2, регистр предделителя: UART1_PSCR, и защитный регистр UART1_GTR для Smartcard режима.
Общая карта регистров с предустановленными значениями показана в следующей табличке:
Значения регистров я рассматривал в статье STM8S+SDCC+SPL: использование модуля UART1 на примере функции printf(). Для того чтобы сконфигурировать и запустить UART1 передатчик нужно иметь в виду всего три флага.
В UART1_CR1 это флаг UARTD который включает и выключает модуль UART1. Обращаю внимание, что в сброшенном состоянии флага, модуль UART1 включён:
В регистре UART1_CR2 нас будет интересовать флаг TEN который включает передатчик:
И во флаговом регистре UART1_SR1 нас будет интересовать бит TXE, который устанавливается в момент передачи содержимого UART1_DR в сдвиговый регистр. Т.е. в момент, когда UART1_DR освободится. Очищается этот флаг путём записи в UART1_DR.
В качестве шаблона проекта я взял пример из предыдущей статьи: Передача параметров из Си в ассемблерную функцию
В результате у меня получился такой исходник:#include <stdint.h> #include "stm8s103f.h" #include "uart1.h" #define LED 5 extern void delay(uint16_t value); int main( void ) { uint16_t i=0; //----------- Setup Clock ---------------------- // fCPU =16MHz CLK_CKDIVR=0; // enable UART and turn off other peripherals CLK_PCKENR1=0; CLK_PCKENR2=0; clk_pckenr1_bset(#3); // enable UART1 //----------- Setup GPIO ----------------------- // GPIO setup pb_ddr_bset(#LED); pb_cr1_bset(#LED); //----------- Setup UART1 ---------------------- // Clear UART1_CR1=0; UART1_CR2=0; UART1_CR3=0; UART1_CR4=0; UART1_CR5=0; UART1_GTR=0; UART1_PSCR=0; // setup UART1 uart1_cr1_bset(#5); // set UARTD, UART1 disable // 9600 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03 //UART1_BRR1=0x68 // 115200 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x0b //UART1_BRR1=0x08 // 230400 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x05; //UART1_BRR1=0x04; // 460800 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03; //UART1_BRR1=0x02; // 921600 Baud Rate, when fMASTER=16MHz UART1_BRR2=0x01; UART1_BRR1=0x01; // Trasmission Enable uart1_cr2_bset(#3); // set TEN, Transmission Enable // enable UART1 uart1_cr1_bres(#5); // clear UARTD, UART1 enable // main loop for(;;) { static uint16_t i=0; pb_odr_bcpl(#LED); uart1_print_string("count: "); uart1_print_number(i++); uart1_send_char('\n'); delay(1000); } }
Макросы: регистр_bset(), регистр_bres(), регистр_bcpl() определены в заголовочном файле stm8s103f.h. Они заменяются ассеблерными инструкциями:
; main.c: 51: uart1_cr2_bset(#3); // set TEN, Transmission Enable bset 0x5235,#3 ; main.c: 53: uart1_cr1_bres(#5); // clear UARTD, UART1 enable bres 0x5234,#5 00102$: ; main.c: 58: pb_odr_bcpl(#LED); bcpl 0x5005,#5
Функцией printf() я в этот раз не пользовался, они слишком тяжёлая. Поэтому ее пришлось заменить несколькими самописными функциями для передачи данных через UART:
#include "uart1.h" #define uart1_sr1_btjf(X,Y) __asm Y: btjf 0x5230,X,Y __endasm #define len 6 void uart1_send_char(uint8_t ch) { UART1_DR=ch; uart1_sr1_btjf(#7,l0); // while (TXE bit == 0); } void uart1_print_string(char *str) { while (*str) { uart1_send_char(*str++); } } void uart1_print_number(uint16_t num){ uint8_t n[len]; char *s=n+(len-1); *s=0; // EOL do { *(--s)=(num%10 + 0x30); num=num/10; } while (num>0); uart1_print_string(s); } void uart1_print_bcd(uint8_t num) { UART1_DR=(num>>4) + 0x30; uart1_sr1_btjf(#7,l1); // while (TXE bit == 0); UART1_DR=(num & 0x0f) + 0x30; uart1_sr1_btjf(#7,l2); // while (TXE bit == 0); }
Прошивка весит 284 байта. Посмотреть вывод в можно в любой терминальной программе, например в Linux c помощью minicom:
$ minicom -D /dev/ttyUSB0 -b 921600
или через Arduino IDE, вызвав монитор последовательного порта.
В STM8S имеется 10-битный АЦП, и так как сам микроконтроллер является 8-битным, имеются варианты выравнивания результата, для получения однобайтного 8-битного числа или двухбайтного 10-битного. Варианты выравнивания представлены на картинке ниже:
Нетрудно догадаться, что выравнивание по левому краю удобно для получения 8-битного значения АЦП, а выравнивание по правому удобно для получения 10-битного значения.
Если говорить о режиме однократного замера, то кроме вышеупомянутой регистровой пары ADC_DR, нас будут интересовать ещё несколько РВВ:
1. Флагово - конфигурационный регистр ADC_CSR:
Интересующие нас здесь поля: EOC - завершение преобразования, EOCIE - разрешение прерывания по завершению преобразования и CH[3:0] - выбор аналогового канала.
2. Первый конфигурационный регистр АЦП - ADC_CR1:
Здесь через SPSEL[2:0] устанавливается частота дискретизации АПЦ. Через бит CONT устанавливается или сбрасывается непрерывный режим работы АЦП. ADON включает или выключает АЦП.
3. Второй конфигурационный регистр АЦП - ADC_CR2:
Здесь нас будет интересовать только ALIGN - флаг выравнивания.
Также не стоит забывать про регистры отключающие триггеры Шмитта:
Здесь каждый бит соответствует своему каналу. Отключение триггера делает невозможным использование соответствующего пина в качестве пина цифрового ввода-вывода, а оставление его включённым значительно снижает точность измерения АЦП. Триггер отключается записью единицы.
Вариант программы для получения 8-значения с АЦП и вывода его через UART у меня получился таким:
#include <stdint.h> #include "stm8s103f.h" #include "uart1.h" #define LED 5 extern void delay(uint16_t value); volatile uint8_t adc_value; void adc_irq(void) __interrupt(22) { adc_value=ADC_DRH; adc_csr_bres(#7); // Clear EOC bit } void awu_irq(void) __interrupt(1) { awu_csr_bres(#5); //Clear AWUF bit for AWU_CSR } int main( void ) { //----------- Setup Clock ---------------------- // fCPU =16MHz CLK_CKDIVR=0; // enable UART and turn off other peripherals CLK_PCKENR1=0; CLK_PCKENR2=0; clk_pckenr1_bset(#3); // enable UART1 clk_pckenr2_bset(#2); // enable AWU clk_pckenr2_bset(#3); // enable ADC //----------- Setup GPIO ----------------------- // GPIO setup pb_ddr_bset(#LED); pb_cr1_bset(#LED); // ------- Setup AWU & Buzzer ------------------------ AWU_TBR = 12; AWU_APR = 62; // =1 sec AWU_CSR = 0x10; // set AWUEN bit for AWUCSR BEEP_CSR=0x1e; // buuuuuuuuuzzzzz (low tone) beep_csr_bset(#5); // Enable BEEP delay(100); beep_csr_bres(#5); // Disable BEEP //----------- Setup UART1 ---------------------- // Clear UART1_CR1=0; UART1_CR2=0; UART1_CR3=0; UART1_CR4=0; UART1_CR5=0; UART1_GTR=0; UART1_PSCR=0; // setup UART1 uart1_cr1_bset(#5); // set UARTD, UART1 disable // 9600 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03 //UART1_BRR1=0x68 // 115200 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x0b //UART1_BRR1=0x08 // 230400 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x05; //UART1_BRR1=0x04; // 460800 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03; //UART1_BRR1=0x02; // 921600 Baud Rate, when fMASTER=16MHz UART1_BRR2=0x01; UART1_BRR1=0x01; // Trasmission Enable uart1_cr2_bset(#3); // set TEN, Transmission Enable // enable UART1 uart1_cr1_bres(#5); // clear UARTD, UART1 enable // ------------- ADC Setup -------------------- adc_cr2_bres(#3); // Clear ALIGN, Left Align adc_cr1_bres(#1); // Clear CONT, Single Conersion Mode ADC_CSR &= 0xf0; // Clear CH[3:0] bits adc_csr_bset(#1); // Select AIN2 Channel ADC_CR1 |= 0x70; // Prescaler = fMASTER/18 adc_cr2_bres(#6); // Clear EXTTRIG, Disable External Trigger ADC_TDRL=0x04; // Diable Schmitt Trigger adc_csr_bset(#5); // Set EOCIE, enable ADC Interrupt // ------------ END SETUP ---------------------- // main loop rim(); // Enable Interrupts for(;;) { adc_cr1_bset(#0); // set ADON, Start Of Conversion wfi(); // sleep pb_odr_bcpl(#LED); uart1_print_string("adc: "); uart1_print_number((uint16_t)adc_value); uart1_send_char('\n'); delay(1000); } }
Я подключил на пин РС3 потенциометр и пару раз покрутив ручкой получил такой результат:
Как видно, работает вполне нормально, не шумит, однако в программе есть "подводный камень". Если закомментировать строку: AWU_CSR = 0x10, пересобрать и перезалить прошивку, то она работать не будет. Микроконтроллер не будет выходить из режима сна после инструкции wfi. Попытка заменить инструкцию wfi на цикл ожидания установки флага EOC не поможет. При этом если закомментировать на этот раз инструкцию wfi, то программа вроде как работает. Какая взаимосвязь между AWU и АЦП, я честное слово не знаю. Возможно где-то каким-то образом задевается аналоговый watchdog. Могу только уверенно сказать что дело не в микроконтроллере. Аналогичная программа на ассемблере на том же микроконтроллере работает без всякого AWU, но вот при переносе алгоритма на Си, возникла такая проблема. Так что я предполагаю что дело в компиляторе SDCC, что весьма печально.
Вариант программы для 10-битного АЦП, представлен ниже:
#include <stdint.h> #include "stm8s103f.h" #include "uart1.h" #define LED 5 extern void delay(uint16_t value); volatile uint16_t adc_value; void adc_irq(void) __interrupt(22) { adc_value=ADC_DRL; adc_value|=(ADC_DRH<<8); adc_value &= 0x03ff; adc_csr_bres(#7); // Clear EOC bit } void awu_irq(void) __interrupt(1) { awu_csr_bres(#5); //Clear AWUF bit for AWU_CSR } int main( void ) { //----------- Setup Clock ---------------------- // fCPU =16MHz CLK_CKDIVR=0; // enable UART and turn off other peripherals CLK_PCKENR1=0; CLK_PCKENR2=0; clk_pckenr1_bset(#3); // enable UART1 clk_pckenr2_bset(#2); // enable AWU clk_pckenr2_bset(#3); // enable ADC //----------- Setup GPIO ----------------------- // GPIO setup pb_ddr_bset(#LED); pb_cr1_bset(#LED); // ------- Setup AWU & Buzzer ------------------------ AWU_TBR = 12; AWU_APR = 62; // =1 sec AWU_CSR = 0x10; // set AWUEN bit for AWUCSR BEEP_CSR=0x1e; // buuuuuuuuuzzzzz (low tone) beep_csr_bset(#5); // Enable BEEP delay(100); beep_csr_bres(#5); // Disable BEEP //----------- Setup UART1 ---------------------- // Clear UART1_CR1=0; UART1_CR2=0; UART1_CR3=0; UART1_CR4=0; UART1_CR5=0; UART1_GTR=0; UART1_PSCR=0; // setup UART1 uart1_cr1_bset(#5); // set UARTD, UART1 disable // 9600 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03 //UART1_BRR1=0x68 // 115200 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x0b //UART1_BRR1=0x08 // 230400 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x05; //UART1_BRR1=0x04; // 460800 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03; //UART1_BRR1=0x02; // 921600 Baud Rate, when fMASTER=16MHz UART1_BRR2=0x01; UART1_BRR1=0x01; // Trasmission Enable uart1_cr2_bset(#3); // set TEN, Transmission Enable // enable UART1 uart1_cr1_bres(#5); // clear UARTD, UART1 enable // ------------- ADC Setup -------------------- adc_cr2_bset(#3); // Set ALIGN, Right Align adc_cr1_bres(#1); // Clear CONT, Single Conersion Mode ADC_CSR &= 0xf0; // Clear CH[3:0] bits adc_csr_bset(#1); // Select AIN2 Channel ADC_CR1 |= 0x70; // Prescaler = fMASTER/18 adc_cr2_bres(#6); // Clear EXTTRIG, Disable External Trigger ADC_TDRL=0x04; // Diable Schmitt Trigger adc_csr_bset(#5); // Set EOCIE, enable ADC Interrupt // ------------ END SETUP ---------------------- // main loop rim(); // Enable Interrupts for(;;) { adc_cr1_bset(#0); // set ADON, Start Of Conversion wfi(); // sleep pb_odr_bcpl(#LED); uart1_print_string("adc: "); uart1_print_number((uint16_t)adc_value); uart1_send_char('\n'); delay(1000); } }
Результат работы с тем же потенциометром:
На мой взгляд, для 10-битного АЦП это вполне достойный результат. Шумит только последний бит, да и то, слегка.
Чтобы, составить полное мнение о возможностях I2C модуля, нужно будет ознакомиться с его конфигурационными регистрами. Мне лично модуль понравился, это мощная и гибкая штука. Сейчас мне хотелось бы рассмотреть самый простой режим его работы: режим мастера на стандартной скорости 100кГц и 7-битным I2C адресом, без использования прерываний (работа в режиме опроса). В качестве слейва я выбрал RTC DS3231, с которым наверное уже все знакомы.
Порядок работы с I2C я бы разделил на инициализацию и рабочий цикл. Думаю, что сначала имеет смысл рассмотреть регистры которые отвечают за инициализацию.
В данном случае, инициализация занимает 8 строчек кода и затрагивает 5 регистров:
//----------- Setup I2C ------------------------ i2c_cr1_bres(#0); // PE=0, disable I2C before setup I2C_FREQR= 16; // peripheral frequence =16MHz I2C_CCRH = 0; // =0 I2C_CCRL = 80; // 100kHz for I2C i2c_ccrh_bres(#7); // set standart mode(100кHz) i2c_oarh_bres(#7); // 7-bit address mode i2c_oarh_bset(#6); // see reference manual i2c_cr1_bset(#0); // PE=1, enable I2C //------------- End Setup ---------------------
Инициализация I2C модуля состоит из задания скорости работы и переключения в нужный режим: стандартная скорость шины, 7-битный адрес. Переключение в режим мастера и обратно в слейв происходит автоматически, после команд START и STOP.
где значением CCR является:
и следовательно:
К примеру для частоты I2C шины =100 кГц, и частоты fMASTER = 16 МГц, имеем значение CCR=80:
Проще конечно вычислять CCR как отношение частоты fMASTER к частоте I2C шины:
Кроме значения CCR, в регистре I2C_CCRH содержится флаг F/S который должен быть сброшен в ноль для стандартного режима 100 кГц.
Функция передачи I2C адреса и записи одного числа у меня получилась такая (параметры функции читаются из стека):
_init_i2c: ;----------- Begin I2C routine ------------------ ; Send Address bset I2C_CR2,#2 ; set ACK bit bset I2C_CR2,#0 ; START wait_start_tx: ; wait SB in I2C_SR1 btjf I2C_SR1, #0, wait_start_tx ld a,I2C_SR1 ; Clear SB bit ld a, (03,sp) ld I2C_DR,a ; I2C address wait_adr_tx: btjf I2C_SR1,#1, wait_adr_tx ld a,I2C_SR1 ; clear ADDR bit ld a,I2C_SR3 ; clear ADDR bit ld a,(04,sp) ld I2C_DR,a ; DS1307 set address =0 wait_zero_tx: ; wait set TXE bit btjf I2C_SR1,#7, wait_zero_tx bset I2C_CR2,#1 ; STOP ;--------------------------------------------------- bres I2C_CR2,#7 ; set SWRST ;-------------------------------------------------- ret
Это наверно самый простейший случай, здесь не анализируется ответ слейва, всегда предполагается что он на линии и отвечает ACK'ом.
Сначала посылается сигнал START, затем адрес устройства, затем один байт данных. После чего посылается СТОП и сброс линии. Функция годится для проверки I2C модуля. Три цикла обеспечивают гарантированное зависание микроконтроллера, если у вас что-то работает не так.
Важным моментом является то, что для сброса флагов состояния нужно целиком читать регистры, а не пытаться их сбросить битовой операцией.
здесь флаг START при установке посылает на линию сигнал соответствующий сигнал, после чего I2C модуль переключается к режим мастера. Установка этого флага в режиме мастера, посылает сигнал RESTART, это позволяет заменить последовательности STOP - START, когда переключаются режимы для слейва: запись на чтение и наоборот. DS1307 сигнал RESTART не поддерживает, но другие устройства, например LM75, поддерживают его. Флаг сбрасывается автоматически при установке состояния СТАРТ или при отключении модуля (PE=0).
Флаг STOP. Установка его посылает на линию соответствующий сигнал, после чего I2C модуль переводится в режим слейва.
Установка флага SWRST сбрасывает, отпускает линию.
Флаги POS и ACK используются в режиме чтения. ACK - определяет: посылать ли в ответ на принятый байт ACK или NACK (если флаг ACK установлен, значит посылать, а если флаг ACK сброшен, значит посылать NACK). POS определяет: посылать ACK/NACK текущим или следующим байтом.
Здесь, флаг SB устанавливается при сигнале START на линии. Флаг очищается программно чтением регистра I2C_SR1. Флаг очищается аппаратно при отключении модуля (PE=0).
Флаг ADDR устанавливается при передаче I2C адреса, включая получение отклика ACK. Флаг ADDR НЕ устанавливается при неполучении отклика ACK от слейва! Флаг сбрасывается чтением регистра I2C_SR1 с последующим чтением I2C_SR3. Флаг очищается аппаратно при отключении модуля (PE=0).
Флаг BTF - завершение операции обмена. Устанавливается в режиме чтения, кода новый байт был принят, но ещё не считан с регистра данных DR. Также устанавливается при передаче, когда был отправлен байт, а регистр данных DR ещё не записан новым значением. Флаг очищается: программно при чтении SR1 регистра, аппаратно при записи или чтении регистра данных DR, или при отключении модуля (PE=0).
Флаг STOPF - устанавливается аппаратно при обнаружении сигнала стоп на линии. Флаг очищается программно чтением регистра I2C_SR1 с последующим чтением I2C_SR2. Флаг очищается аппаратно при отключении модуля (PE=0).
Флаг RxNE (регистр данных не пуст) - устанавливается аппаратно при получении (приёме) данных в регистре DR. Флаг очищается программно чтением или записью в регистр DR. Флаг очищается аппаратно при отключении модуля (PE=0).
Флаг TxE (регистр данных пуст) - устанавливается аппаратно при перемещении байта из регистра данных DR в сдвиговый регистр для передачи на линию. Флаг очищается программно записью в регистр DR. Флаг очищается аппаратно после сигналов START или STOP на линии или при отключении модуля (PE=0).
Мощной возможностью STM8 является то, что данные со слейва можно читать порциями по одному, двум, трём байтам или каким-то массивом. Часто при работе по I2C требуется прочитать лишь один или два байта устройства и здесь мы имеем возможность использовать упрощённый алгоритм чтения без циклов.
Отличительной чертой, модуля I2C является то, что сигналы ACK, NACK, STOP передаются "пакетом" вместе с читаемым байтом, и они должны быть сконфигурированы ДО(!) чтения этого байта из регистра DR.
Т.к. сессию чтения мастер завершает передачей сигналов NACK и STOP, порядком выставления флагов для формирования этих сигналов и отличаются алгоритмы с завершением сессии чтении в один, два или три байта.
Завершение сессии в один байт, мне показался наиболее универсальным. С его помощью можно читать как один байт, так и массив байтов. Функция чтения массива из семи байт представлена ниже:
_read_i2c: ;-------------------------------------------------- bset I2C_CR2,#2 ; set ACK bit bset I2C_CR2,#0 ; START wait_start_rx: ; wait SB in I2C_SR1 btjf I2C_SR1, #0, wait_start_rx ld a,I2C_SR1 ; Clear SB bit ld a,(03,sp) inc a ld I2C_DR, a ; DS1307 address RAED Mode wait_adr_rx: btjf I2C_SR1,#1, wait_adr_rx ; --------- READ 7 BYTES -------------------- bset I2C_CR2,#2 ; send ACK ld a,I2C_SR1 ; clear ADDR bit ld a,I2C_SR3 ; clear ADDR bit clrw y ldw x,(04,sp) ; --------- READ LOOP ---------------------- wait_read: btjf I2C_SR1,#6,wait_read ld a,I2C_DR ld (x),a incw x incw y cpw y,#06 jrne wait_read ; get last byte bres I2C_CR2,#2 ; NACK bset I2C_CR2,#1 ; STOP wait_last: ; wait RXNE bit btjf I2C_SR1,#6, wait_last ld a,I2C_DR ; get data from DS1307 and clear RXNE bit ld (x), a ; save seconds in RAM bres I2C_CR2,#7 ; set SWRST ret
Здесь адрес устройства и указатель на массив передаются через стек.
Если из функции убрать цикл чтения шести байт, останется функция чтения одного байта. Как видно, установка флага STOP и сброс флага ACK происходит до чтения последнего байта из регистра DR.
Порядок чтения одного байта в документации представлен следующей диаграммой:
Один, два или три байта можно читать без использования циклов. Например чтобы прочитать текущее время из DS3231 нужно прочитать три байта. Тогда функция чтения будет выглядеть так:
.globl _read_i2c _read_i2c: ;-------------------------------------------------- bset I2C_CR2,#2 ; set ACK bit bset I2C_CR2,#0 ; START wait_start_rx: ; wait SB in I2C_SR1 btjf I2C_SR1, #0, wait_start_rx ld a,I2C_SR1 ; Clear SB bit ld a,(03,sp) inc a ld I2C_DR, a ; DS1307 address RAED Mode ldw x, (04,sp) w0: btjf I2C_SR1,#1, w0 ; --------- READ THREE BYTES -------------------- bset I2C_CR2,#2 ; send ACK ld a,I2C_SR1 ; clear ADDR bit ld a,I2C_SR3 ; clear ADDR bit w2: btjf I2C_SR1,#2,w2 bres I2C_CR2,#2 ; NACK ld a,I2C_DR ld (x),a bset I2C_CR2,#1 ; STOP ld a,I2C_DR incw x ld (x),a w4: btjf I2C_SR1,#6,w4 ld a,I2C_DR incw x ld (x),a bres I2C_CR2,#7 ; set SWRST ret
Полная Си-часть программы чтения данных с DS3231:
#include <stdint.h> #include "stm8s103f.h" #include "uart1.h" #define LED 3 #define DS1307_ADR 0x68 uint8_t data[7]; extern void delay(uint16_t value); extern void init_i2c(uint8_t adr, uint8_t value); extern void read_i2c(uint8_t adr, uint8_t *values); int main( void ) { //----------- Setup Clock ---------------------- // fCPU =16MHz CLK_CKDIVR=0; // enable UART and turn off other peripherals CLK_PCKENR1=0; CLK_PCKENR2=0; clk_pckenr1_bset(#3); // enable UART1 clk_pckenr1_bset(#0); // enable I2C clk_pckenr2_bset(#2); // enable AWU //----------- Setup GPIO ----------------------- // GPIO setup pc_ddr_bset(#LED); // PC_DDR|=(1<<LED) pc_cr1_bset(#LED); // PC_CR1|=(1<<LED) // ------- Setup AWU & Buzzer ------------------------ BEEP_CSR=0x1e; // buuuuuuuuuzzzzz (low tone) beep_csr_bset(#5); // Enable BEEP delay(100); beep_csr_bres(#5); // Disable BEEP //----------- Setup UART1 ---------------------- // Clear UART1_CR1=0; UART1_CR2=0; UART1_CR3=0; UART1_CR4=0; UART1_CR5=0; UART1_GTR=0; UART1_PSCR=0; // setup UART1 uart1_cr1_bset(#5); // set UARTD, UART1 disable // 9600 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03 //UART1_BRR1=0x68 // 115200 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x0b //UART1_BRR1=0x08 // 230400 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x05; //UART1_BRR1=0x04; // 460800 Baud Rate, when fMASTER=16MHz //UART1_BRR2=0x03; //UART1_BRR1=0x02; // 921600 Baud Rate, when fMASTER=16MHz UART1_BRR2=0x01; UART1_BRR1=0x01; // Trasmission Enable uart1_cr2_bset(#3); // set TEN, Transmission Enable // enable UART1 uart1_cr1_bres(#5); // clear UARTD, UART1 enable //----------- Setup I2C ------------------------ i2c_cr1_bres(#0); // PE=0, disable I2C before setup I2C_FREQR= 16; // peripheral frequence =16MHz I2C_CCRH = 0; // =0 I2C_CCRL = 80; // 100kHz for I2C i2c_ccrh_bres(#7); // set standart mode(100кHz) i2c_oarh_bres(#7); // 7-bit address mode i2c_oarh_bset(#6); // see reference manual i2c_cr1_bset(#0); // PE=1, enable I2C //------------- End Setup --------------------- // main loop for(;;) { init_i2c((DS1307_ADR<<1), 0x0); read_i2c((DS1307_ADR<<1), data); pc_odr_bcpl(#LED); uart1_print_string("time: "); uart1_print_bcd(data[2]); uart1_send_char(':'); uart1_print_bcd(data[1]); uart1_send_char(':'); uart1_print_bcd(data[0]); uart1_send_char('\n'); delay(800); } }
Хочу обратить внимание, что светодиод для индикации рабочего цикла был перенесён с PB5 на PC3, т.к. пин PB5 занят I2C линией.
Текст функций init_i2c() и read_i2c() был представлен выше.
Результат работы в окне программы minicom:
Скачать полные исходники со сборочными файлам и скомпилированными прошивками, можно по этой ссылке .