Jump to content
COKPOWEHEU

stm32f103 и компас GY-271

Recommended Posts

Решил разобраться с модулем компаса GY-271 - одним из немногих модулей, которые вообще сумел найти. Пол-интернета заполнены утверждениями что собран данный модуль на микросхеме HMC5883L, которая общается по I2C и отзывается на адрес 0x1E (соответственно, 0x3C/0x3D для записи и чтения). На практике же посылка этого адреса натыкалась на глухой таймаут, мол знать не знаем такого адреса, и вообще мы не оно. Целенаправленные, но больше случайные поиски по интернету дали наводку что данное чудо техники, возможно, отзовется на адрес 0x0D (0x1A/0x1B для записи и чтения) и действительно, по такому адресу удалось добиться ответа, правда не совсем того, что ожидалось. К примеру, без "шаманской" (опять же, советы из "Всезнающего") записи единицы по адресу 9, ответ был нулевым. Потом более-менее осмысленные данные появились, но не с адреса 0x03, как положено по даташиту, а вовсе даже с 0x00. Очередная случайная находка дала направление, что заботливые китайцы запаяли на плату не HMC5883L, а вовсе даже QMC5883L - совсем другую микросхему с другими регистрами. И вот она действительно похожа по настройкам и адресу.

Стал бы я создавать тему, если бы все было так хорошо... Хотя ответ от микросхемы и есть, как-то он слабо похож на нормальные данные. Теоретически, в регистрах 1:0, 3:2, 5:4 должны быть проекции магнитного поля на оси X, Y, Z. То есть вдали от магнитов и массивных железяк, геометрическая сумма значений должны быть примерно 0.5 Гс, то есть ~32000 единиц модуля, причем пока модуль лежит на столе, сильно меняться значениям не с чего. Привожу лог снятых данных (во вложении исходный код прошивки: вся работа с периферией идет руками, без автоконфигураторов и тому подобного). За все время съема этого лога модель лежал на столе в одном положении, но данные по осям скачут достаточно произвольным образом.

I2C test
FB FF 0A 00 14 00 00 A8 F5 0D 00 01 01 00 Write
0F 00 05 00 F1 FF 04 99 F5 | 15 5 -15 2337
FB FF 00 00 05 00 00 9A F5 | -5 0 5 2338
0F 00 05 00 00 00 00 8F F5 | 15 5 0 2327
E2 FF 00 00 F6 FF 00 93 F5 | -30 0 -10 2331
28 00 F6 FF FB FF 00 90 F5 | 40 -10 -5 2328
05 00 FB FF FB FF 00 99 F5 | 5 -5 -5 2337
00 00 00 00 00 00 00 97 F5 | 0 0 0 2335
00 00 0F 00 D6 FF 00 99 F5 | 0 15 -42 2337
32 00 E2 FF 00 00 00 94 F5 | 50 -30 0 2332
E7 FF EC FF 00 00 00 97 F5 | -25 -20 0 2335
05 00 F6 FF 00 00 04 92 F5 | 5 -10 0 2330

Продублирую код чтения регистра (насколько я знаю, он довольно типичный, но мало ли...). Естественно, код писался в первую очередь под конкретную задачу - оптимизация будет потом, если оно вообще заработает.

void I2C_Read(uint8_t device, uint8_t addr, uint8_t *buf, uint8_t count){
  //start
  I2C2->CR1 |= I2C_CR1_START;
  while(!(I2C2->SR1 & I2C_SR1_SB)){}
  (void) I2C2->SR1;
 
  //device
  I2C2->DR = (device<<1) &~(1<<0);
  while(!(I2C2->SR1 & I2C_SR1_ADDR)){}
  (void) I2C2->SR1;
  (void) I2C2->SR2;
 
  //addr
  I2C2->DR = addr;
  while(!(I2C2->SR1 & I2C_SR1_TXE)){}
  I2C2->CR1 |= I2C_CR1_STOP;
 
  //restart
  I2C2->CR1 |= I2C_CR1_START;
  while(!(I2C2->SR1 & I2C_SR1_SB)){}
 
  //device
  I2C2->DR = (device<<1) | (1<<0);
  while(!(I2C2->SR1 & I2C_SR1_ADDR)){}
  (void) I2C2->SR1;
  (void) I2C2->SR2;
 
  //data
  for(uint8_t i=0; i<count-1; i++){
    //I2C2->CR1 &=~ I2C_CR1_ACK;
    I2C2->CR1 |= I2C_CR1_ACK;
    while(!(I2C2->SR1 & I2C_SR1_RXNE)){}
    buf[i] = I2C2->DR;
  }
  I2C2->CR1 &=~I2C_CR1_ACK;
  while(!(I2C2->SR1 & I2C_SR1_RXNE)){}
  buf[count-1] = I2C2->DR;
 
  //stop
  I2C2->CR1 |= I2C_CR1_STOP;
}
#define QMC5883L 0x0D

  I2C_Read(QMC5883L, 0, data, 14);
  for(uint8_t i=0; i<14; i++){
    UART_HEX(data[i]); UART_putc(USART, ' ');
  }

  data[0] = 0x80;
  I2C_Write(QMC5883L, 10, data, 1);
  data[0] = 0x01;
  I2C_Write(QMC5883L, 0x0B, data, 1);

 
  data[0] = (0b00<<6) | (0b00<<4) | (0b11<<2) | (0b01<<0);
  //oversampling = 512
  //range = 2 G
  //ODR = 200 Hz
  //mode = continuous
  I2C_Write(QMC5883L, 9, data, 1);

  UART_puts(USART, "Write\x0d\n");

  while(1){
    I2C_Read(QMC5883L, 0, data, 9);
    for(uint8_t i=0; i<9; i++){
      UART_HEX(data[i]); UART_putc(USART, ' ');
    }
    i16a x,y,z,t;
    x.u8val[0] = data[0]; x.u8val[1] = data[1];
    y.u8val[0] = data[2]; y.u8val[1] = data[3];
    z.u8val[0] = data[4]; z.u8val[1] = data[5];
    t.u8val[0] = data[7]; t.u8val[1] = data[8];
    t.ival += 3000+2000;
    sprintf(str,"| %i %i %i %i", x.ival, y.ival, z.ival, t.ival);
    UART_puts(USART, str);

Собственно, вопрос: что я делаю не так и как сделать так, чтобы модуль отдавал правильные данные магнитного поля?

compass.rar


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Там проблема с сохранением результатов измерений, если не успеть забрать - портятся. Есть два варианта: Читать статус в режиме нонстоп , и быть привязанным к тактовому генератору компаса (там +- лапоть).  Или запускать однократное измерение по таймеру мк (событие - чтение значений - новый запуск).

Share this post


Link to post
Share on other sites

Добавил ожидание флага DRDY (сначала он возникает вместе с флагом DOR), считываю регистры, потом снова дожидаюсь бита DRDY:

  while(1){
...
    do{
      I2C_Read(QMC5883L, 6, &data[6], 1);
    }while(!(data[6] & 1));
    UART_HEX(data[6]); UART_putc(USART, ' ');
    I2C_Read(QMC5883L, 0, data, 6);
    UART_HEX(data[0]); UART_putc(USART, ' ');
    
    do{
      I2C_Read(QMC5883L, 6, &data[6], 1);
    }while(!(data[6] & 1));
    UART_HEX(data[6]); UART_putc(USART, ' ');

    I2C_Read(QMC5883L, 0, &data[1], 6);
...
  }

Особой разницы нет:

05 00 01 00 05 00 14 00 F1 FF 00 F5 | 1280 5120 -3840 2184
05 BD 01 BD DD FF 00 00 FB FF 00 F5 | -8771 255 -1280 2184
05 14 01 14 23 00 0F 00 1E 00 00 F5 | 8980 3840 7680 2184
05 00 01 00 F6 FF 0F 00 F6 FF 00 F5 | -2560 4095 -2560 2184
05 00 01 00 00 00 00 00 D1 FF 00 F5 | 0 0 -12032 2184
05 14 01 14 DD FF 2D 00 1E 00 00 F5 | -8940 11775 7680 2184

здесь первый байт - считанный регистр 0x06 с битами DRDY+DOR, потом 0-й регистр (считываются-то все, но отображается только он), потом еще раз 6-й регистр (теперь видно, что выставлен только бит DRDY, то есть данные вроде как готовы). Потом считываю данные, но модуль опять выдает непонятно что.

Если запускать одиночные преобразования - как это делать? В даташите алгоритма или подходящих регистров не нашел.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Взаимодействие устройств IoT от Microchip с облаком Google

Благодаря облачным технологиям появилась возможность реализовать сложные проекты на базе микроконтроллера путем перераспределения вычислительной нагрузки между микроконтроллером и облаком. Простые в использовании отладочные платы, такие как AVR- и PIC-IoT WG, позволяют выполнять ресурсоемкие вычисления, передавая их в облако.

Подробнее

Я кажется спутал QMC5883L с другим датчиком, у которого был одиночный режим. Сейчас покопался в своём бардаке, оказывается я его нагло сбрасывал на перемагничивание (калибровку) , и читал по внешнему таймеру. Кстати, у этого датчика есть неприятный глюк - он помнит магнитное поле если оно превышало определённый порог. То-есть если поднести магнитик - то значения магнитного поля не спадают.  Мягкий сброс очищает эту память. 

У датчика на макетке из китая есть стаб 3,3в, он полезен когда внешнее питание 5в. Но при питании от 3,3в - на стабе будут пульсации. Резисторы которые стоят на шине - мне китайцы поставили по 100к, перепаял на 1к.

Ну и самое простое, когда датчик используется на собственной ПП: под ним должен быть массивный полигон, чем больше -тем лучше. Не, датчик не греется, но зато идеально ловит 50герц наводку от сети. 

Share this post


Link to post
Share on other sites

Пружинные клеммники Degson - высокое качество соединения по оптимальной цене!

Пружинные клеммные блоки Degson для монтажа на печатную плату – это простое и надежное соединение, которое позволяет легко решать задачи для различных приложений за счет обширного ассортимента. Клеммники Degson доступны в двух конструктивных исполнениях (торцевой контакт и зажимная клеть), имеют различные направления ввода проводника (45°, 90°, 180°) и обладают широким диапазоном поперечных сечений (0,2…2,5 мм2).

Подробнее

6 часов назад, AVI-crak Home сказал:

Мягкий сброс очищает эту память. 

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

6 часов назад, AVI-crak Home сказал:

У датчика на макетке из китая есть стаб 3,3в, он полезен когда внешнее питание 5в. Но при питании от 3,3в - на стабе будут пульсации. Резисторы которые стоят на шине - мне китайцы поставили по 100к, перепаял на 1к.

То есть советуете запитать сам модуль от 5 В и проверить резисторы подтяжки? Попробую, хотя раз проблем с обменом нет, вряд ли дело в резисторах.

6 часов назад, AVI-crak Home сказал:

Ну и самое простое, когда датчик используется на собственной ПП:

Нет, используется готовый модуль GY-271. Перепаивать буду если заработает, но пока до этого далеко :) В любом случае спасибо за предупреждение.

6 часов назад, AVI-crak Home сказал:

Сейчас покопался в своём бардаке, оказывается я его нагло сбрасывал на перемагничивание (калибровку) , и читал по внешнему таймеру.

Может, поделитесь кодом?


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Новое семейство ИП от MEAN WELL мощностью 150, 300, 600 Вт с повышенной перегрузочной способностью

Компания MEAN WELL разработала семейство источников питания номинальной мощностью 150, 300 и 600 Вт с возможностью кратковременной безопасной работы при мощности в 2,5 раза выше номинальной (до 5 секунд). Данное семейство источников питания востребовано в промышленных установках с использованием электродвигателей или с емкостной нагрузкой. Применяя новые источники питания HRP/N можно получить экономический выигрыш.

Подробнее

Проверил резисторы. Оказалось 2.2к. Вроде нормальные. Попробовал подать на модуль пятивольтовое питание - без изменений.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites
Скрытый текст

    i2xdata.data[0] = 0x00; /// adres_reg - 1
    i2xdata.read_write = 1;
    i2xdata.nBytes = 1;
    i2c_RW_master();



    i2xdata.read_write = 0;
    i2xdata.nBytes = 9;
    i2xdata.read_write = 0;
    i2c_RW_master();


    i2xdata.Sdata[0] = i2xdata.data[0];
    i2xdata.Sdata[0] |= i2xdata.data[1] <<8;
 //   i2xdata.Sdata[0] += 998;

    i2xdata.Sdata[1] = i2xdata.data[2];
    i2xdata.Sdata[1] |= i2xdata.data[3] <<8;
 //   i2xdata.Sdata[1] += 63;

    i2xdata.Sdata[2] = i2xdata.data[4];
    i2xdata.Sdata[2] |= i2xdata.data[5] <<8;
  //  i2xdata.Sdata[2] -= 40;

    i2xdata.Sdata[3] = i2xdata.data[7];
    i2xdata.Sdata[3] |= i2xdata.data[8] << 8;

 

Проблема в том что HMC5883L был первым датчиком что я хотел подружить с stm. Непонимание того как работает протокол шины и аппаратная часть мк - дало очень забавного уродца. Массив i2xdata.data - это то что я записывал или читал из чипа.

Запуск чипа делался до победного конца, пока его не отпустит. Для HMC5883L есть режим залипания, когда он позволяет только читать свои регистры, и больше ни на что не реагирует. То-есть в случае сброса всей системы - вполне возможно получить ситуацию когда данные прочитались частично, а датчик не имеет аппаратного сброса.

Скрытый текст

volatile struct _i2xdata
{
    uint8_t adres_mx;
    uint8_t nBytes;
    uint8_t read_write; ///0-1
    uint8_t status;
    uint8_t data[10];
    int16_t Sdata[4];
    float Sdata2[4];
}i2xdata;
----------------
    i2xdata.adres_mx = 0x0d;

  do
  {
    i2xdata.status = 0;

    i2xdata.adres_mx = 0x0d;
    i2xdata.data[0] = 0x0a;
    i2xdata.data[1] = 0x80;
    i2xdata.read_write = 1;
    i2xdata.nBytes = 2;
    i2c_RW_master();
    sDelay_mc(50);

    i2xdata.data[0] = 0x0B; /// adres_reg - 1
    i2xdata.data[1] = 1; ///  demagnetization
    i2xdata.read_write = 1;
    i2xdata.nBytes = 2;
    i2c_RW_master();
    sDelay_mc(10);

    i2xdata.data[0] = 0x09; /// adres_reg - 1
    i2xdata.data[1] = 0x1;
    i2xdata.read_write = 1;
    i2xdata.nBytes = 2;
    i2c_RW_master();
  } while (i2xdata.status);

 

 

Share this post


Link to post
Share on other sites

@AVI-crak Home , спасибо, код посмотрел, но вряд ли поможет.

HappyEnd:

Раздобыл Ардуину и нашел несколько разных скетчей для нее (с I2C под stm32 работаю впервые, все же мог накосячить). Даже прогрел строительным феном (вдруг какой контакт не пропаялся). Ничего не меняется: коннект прекрасный, I2C работает замечательно, даже температура считывается, а вот вместо магнитного поля рандомное дерганье.

Значит, скорее всего, просто датчик бракованный попался. Тут ничего не поделаешь, буду искать другой.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Может, на него окружающие магнитные поля действуют?


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites

Кусок лога данных я выкладывал, все время измерения модуль просто лежал на столе, без движения. Даже если мусорное магнитное поле есть, оно должно складываться с полем Земли, то есть должно быть колебание в районе 8000, а вовсе не +/- 50. Поднесение магнита и повороты датчика тоже не дают видимого эффекта


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

В запись цикле:

адрес 0x0d, регистр назначения 0x0a, данные 0x80, ждём 10-50мс.

адрес 0x0d, регистр назначения 0x0B, данные 0x01, ждём 10-50мс.

адрес 0x0d, регистр назначения 0x09, данные 0x01.

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

Share this post


Link to post
Share on other sites

Мой код в первом посте, все это я делал. Запись в 0x0B даже уменьшает разброс "показаний". Хуже всего что чужой код дает ровно те же результаты, хотя там народ отписывался что "работает прекрасно". Да и по моему коду видно, что проблемы с I2C нет, микросхема отвечает, на запись в регистры реагирует, даже выдает что-то, что считает данными.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

В том и есть весь прикол, что заставить адекватно реагировать QMC5883L - достаточно трудно. Это не память, которую можно читать вдоль и поперёк, это отдельный процессор с программным I2C. На обращения к регистрам есть небольшие окна - в которые нужно попасть. А потом он начнёт оцифровывать датчики, и в этот момент его трогать нельзя, иначе получится каша.

Share this post


Link to post
Share on other sites

@AVI-crak Home еще раз: я дожидаюсь, когда взведется флаг, никакой разницы нет.  Я проверял на готовом коде и готовом железе, где, по уверениям народа, это работает. Более того, народ хвалит датчик, говорит что он менее шумящий, чем HMC5883.

И я прекрасно знаю, как выглядят подобные обвинения "компилятор виноват, контроллер неисправен" когда на самом деле у автора руки кривые. И именно ради этого я искал Ардуину и готовые чужие скетчи, да и по косвенным признакам не вижу других объяснений. Лучшей проверкой было бы найти другой модуль или микросхему (уж развести под него плату не проблема) и проверить на нем, но у меня его под рукой нет. Хотя если в Москве, а лучше в Щелково или рядом, найдется человек, готовый одолжить аналогичный модуль на пару минут, можно проверить и так.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Happy end.

Сегодня зашел туда же, где покупал модуль и попросил другой для тестирования. С собой захватил и предыдущего пациента и платку для тестирования. К моему удивлению продавец без вопросов распаковал другой модуль и позволил ткнуть в адскую конструкцию, заляпанную местами суперклеем. Как и предполагалось, свежий модуль заработал нормально (что интересно, он был сделан на том же чипе QMC). А тот пациент, который мой, работать как не хотел, так и не стал. Но что еще удивительнее - продавец был готов бесплатно поменять мой модуль на рабочий. Если бы я над своим не успел поиздеваться (пайка, прогрев феном). Естественно, меня это не расстроило.

Так что все в порядке, с живыми QMC5883 я работать умею, а вот с мертвыми пока нет. Есть к чему стремиться!


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Сообщения

    • а где ты ее фото приложил, окромя пружины?
    • Индикаторы F13K для часов Новые, с хранения. В наличии 10шт. Цена по 750 руб/шт. Возможна отправка почтой или ТК.
    • гражданство есть, ни кто не отменял, другое Дело корпоративное Право, с кодом ООО на "1". )) Сколько по времени Вы уже в этом сортире? Хоть один даташит (ссылки) на идентификацию "Кто Я", "Где Я" прочитал? Нет? Тогда, "морская разведка" СССР право, 95% "без понятийного" аппарата, "зомби - био - роботы".
    • смотрел. ток мизерный. но всё-же интересно посмотреть что это за микра. ну не гуглится, собака. а на счёт говноакб, вполне может быть, но не могу исключать и другие факторы. сейчас заменил акб, на пару китайских нонейм банок, вот стоит заряжается, буду следить вовремя ли прекращается заряд. а то мало ли что. у меня в одном чудесном фонарике контроллер заряда пробило и обычная 18650 зарядилась до пяти вольт как раз у другой модели колонок от этой фирмы есть болезнь с умирающими микрами, правда чаще они дохнут мирно и просто не заряжают. @KRAB  дядька, дай даташит, а. ты ж старожил, ремонтер, должен лучше меня понимать как такие вещи искать, так что если нехрен будет делать, то поиск даташита скрасит досуг.
    • совершенно разные по спектру и энергетике задачи .... но я тебе подскажу - посади китаезу за пиалу риса или алкаша за полташ .... самый простой и дешевый датчик ....
    • на чупа-чупс - и то не хватит .....
    • А вы наверное и фазу сетевую голыми руками берете? Вы же не раб!
  • Similar Content

    • By Tuxford
      С помощью STM32CubeMX сгенерил самый примитывний проект чтобы что-то принять-передать по USB. Все бы ничего, но обмена не происходит, хотя в системе устройство видно.
      Шаг за шагом обнаружил что приходит прерывание SOF, и просто скидывается. На этом финиш. И так постоянно. Находил проекты по эти борды (Blue pill, STM32 Smart), где собственно все то же. Но нифига не рабтает.

      Что же такого особенного в этом MCU особенного чтобы USB работал?
      PS. Пробывал делать тоже на STM32L4-Discovery. Работает старта. В чем разница?
      IOC в атаче.
      TestBP.ioc
    • By john2103
      Доброго времени суток, товарищи, пытаюсь создать функции передачи по spi с использованием cmsis и прерываний. Раньше пользовался HAL на "полу- интуитивном" уровне. Все работало. Сейчас пытаюсь все писать только на cmsis и вот возникла проблема... Я попытался сделать две функции ( Отправка по SPI - 8-битного  массива и передача 8 битная DFF = 0 и отправка 16 битного массива и передача 16 бит DFF =1 и все это через прерывания)Результат смотрю анализатором и вот в чем проблема... если сначала идет функция передачи 8 битная а потом 16 битная то мелькает лишняя передача, 8 бит 0x00 и потом идет правильная передача, 16 битная. Не могу понять откуда берется эта передача??? (0xAA 0xBB 0xCC 0x00 0xDD 0xDD 0xEE 0xEE 0xFF 0xFF) ,  если поставить передачи 8 бит 16 бит 8 бит 16 бит то получается вот это (0xAA 0xBB 0xCC 0x00 0xDD 0xDD 0xEE 0xEE 0xFF 0xFF 0xAA 0xBB 0xCC 0xFE 0xDD 0xDD 0xEE 0xEE 0xFF 0xFF), если между функциями вставить задержку 1 мс то все ок иначе тот результат о котором писал ..."головной убор Михаила Боярского"
      void SPI1_STM32F1_init(void) { // --- Инициализация порта для работы spi --- // ------ Настраиваем порт А -------------------------------------------------------------------------- RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //--- Включаем тактирование порта А RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //--- Включаем тактирование альтернативных функции RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //--- Включаем тактированние SPI1 // ---------------------------------------------------------------------------------------------------- GPIOA->CRL &= ~(GPIO_CRL_CNF5|GPIO_CRL_CNF7|GPIO_CRL_MODE5|GPIO_CRL_MODE7); //--- Обнуление первоначальных параметров порта GPIOA->CRL |= ( GPIO_CRL_MODE5 // --- |Настройка вывода SCL, на работу в режиме альтернативной функции |GPIO_CRL_CNF5_1 // --- |режим output mode 11b - max 50 Мгц, CNF = 10b Альтернативная функция output push-pull ) |GPIO_CRL_MODE7 // --- |Настройка вывода MOSI |GPIO_CRL_CNF7_1 // --- | ); // ---------------------------------------------------------------------------------------------------- GPIOA->CRL &= ~(GPIO_CRL_CNF6|GPIO_CRL_CNF6); GPIOA->CRL |= GPIO_CRL_MODE6; SPI1->CR1 |= SPI_CR1_BIDIMODE; // --- BIDIMODE режим работы (1 - одна линия, 0 - две линии связи) SPI1->CR1 |= SPI_CR1_BIDIOE; // --- BIDIOE Этот бит в сочетании с битом BIDImode выбирает направление передачи в двунаправленном режиме // --- 0: Output disabled (receive-only mode) // --- 1: Output enabled (transmit-only mode) SPI1->CR1 &=~SPI_CR1_CRCEN; // --- Аппаратный расчет CRC включить // --- 0: расчет CRC отключен // --- 1: Расчет CRC включен SPI1->CR1 &= ~SPI_CR1_CRCNEXT; // --- Следующая передача данных будет завершаться CRC-кодом. // --- 0: Этап передачи данных // --- 1: Следующая передача завершится передачей RCR SPI1->CR1 &= ~SPI_CR1_DFF; // --- Формат кадра данных // --- 0: Размер кадра передачи 8 бит // --- 1: Размер кадра передачи 16 бит SPI1->CR1 &= ~SPI_CR1_RXONLY; // --- Этот бит совместно с BIDIMODE выбирает направление передачи в 2-х проводном (MISO и MISO) режиме. // --- 0: Full duplex — передача и прием // --- 1: Output disabled — только прием SPI1->CR1 |= SPI_CR1_SSM; // --- Программное управление ведомым устройством. Когда бит SSM установлен, сигнал NSS заменяется значением бита SSI. // --- 0: Программное управление ведомым отключено // --- 1: Программное управление ведомым включено SPI1->CR1 |= SPI_CR1_SSI; // --- Внутренний выбор ведомого. Этот бит работает только когда бит SSM установлен. Значение этого бита принудительно подается на NSS, а значение IO вывода NSS игнорируется. // --- 1: (Master) Заменяет значение на выводе NSS // --- 0; (Slave) SPI1->CR1 &= ~SPI_CR1_LSBFIRST;// --- Формат кадра // --- 0: MSB передается первым // --- 1: LSB передается первым SPI1->CR1 |= SPI_CR1_BR; // --- BR[2:0]: Выбор скорости передачи // 000: fPCLK/2 // 001: fPCLK/4 // 010: fPCLK/8 // 011: fPCLK/16 // 100: fPCLK/32 // 101: fPCLK/64 // 110: fPCLK/128 // 111: fPCLK/256 //#define SPI_CR1_BR_Pos (3U) //#define SPI_CR1_BR_Msk (0x7U << SPI_CR1_BR_Pos) /*!< 0x00000038 */ //#define SPI_CR1_BR SPI_CR1_BR_Msk /*!< BR[2:0] bits (Baud Rate Control) */ //#define SPI_CR1_BR_0 (0x1U << SPI_CR1_BR_Pos) /*!< 0x00000008 */ //#define SPI_CR1_BR_1 (0x2U << SPI_CR1_BR_Pos) /*!< 0x00000010 */ //#define SPI_CR1_BR_2 (0x4U << SPI_CR1_BR_Pos) /*!< 0x00000020 */ SPI1->CR1 |= SPI_CR1_MSTR; // --- Выбор режима работы SPI: Master/Slave // --- 0: Режим Slave (ведомый) // --- 1: Режим Master (ведущий) SPI1->CR1 &= ~SPI_CR1_CPOL; // --- Полярность тактового сигнала // --- 0: CK в 0 при простое // --- 1: CK в 1 при простое SPI1->CR1 &= ~SPI_CR1_CPHA; // --- Фаза тактового сигнала // --- 0: Первый переход тактового сигнала является краем захвата данных // --- 1: Второй переход тактового сигнала является краем захвата данных SPI1->CR2 &= ~SPI_CR2_TXEIE; // --- Прерывание опустошения буфера передачи данных Tx // --- 0: Прерывание TXE запрещено // --- 1: Прерывание TXE разрешено. Используется для генерации прерывания когда устанавливается флаг TXE SPI1->CR2 &= ~SPI_CR2_RXNEIE; // --- Прерывание не пустого буфера приема Rx // --- 0: Прерывание RXNE запрещено // --- 1: Прерывание RXNE разрешено. Используется для генерации прерывания когда устанавливается флаг RXNE. SPI1->CR2 &= ~SPI_CR2_ERRIE; // --- Прерывание при возникновении ошибок передачи. Этот бит контролирует генерацию прерывания при возникновении одной из ошибок интерфейса SPI (CRCERR, OVR, MODF). // --- 0: Прерывание при возникновении ошибок запрещено // --- 1: Прерывание при возникновении ошибок разрешено SPI1->CR2 |= SPI_CR2_SSOE; // --- Разрешить выход SS // --- 0: Выход SS отключен в режиме master (ведущий) и есть возможность работать в multimaster режиме // --- 1: Выход SS включен в режиме master (ведущий), при этом нет возможности работать в multimaster режиме SPI1->CR2 &= ~SPI_CR2_TXDMAEN; // --- Когда этот бит установлен, запрос DMA возникает при установке флага TXE // --- 0: Tx buffer DMA disabled // --- 1: Tx buffer DMA enabled SPI1->CR2 &= ~SPI_CR2_RXDMAEN; // --- Когда этот бит установлен, запрос DMA возникает при установке флага RXNE // --- 0: Rx buffer DMA disabled // --- 1: Rx buffer DMA enabled SPI1_Enable; NVIC_EnableIRQ(SPI1_IRQn); } void SPI1_STM32F1_write_8bit_irq(uint8_t *data, int32_t len_8bit) { if(len_8bit<=0) return; //Ждем, пока SPI освободится от предыдущей передачи while(SPI1->SR & SPI_SR_BSY)) ; SPI1->CR1 &= ~SPI_CR1_SPE; SPI1->CR1 &= ~SPI_CR1_DFF; SPI1->CR1 |= SPI_CR1_SPE; //Настройка переменных, которые будут //использоваться в обработчике прерывания SPI tx_index_8_bit = 0; tx_len_8_bit = len_8bit; tx_data_8_bit = data; //Разрешаем прерывание TXEIE И запускаем обмен SPI1->CR2 |= SPI_CR2_TXEIE; } void SPI1_STM32F1_write_16bit_irq(uint16_t *data, int32_t len_16bit) { if(len_16bit<=0) return; //Ждем, пока SPI освободится от предыдущей передачи while(SPI1->SR & SPI_SR_BSY) ; SPI1->CR1 &= ~SPI_CR1_SPE; SPI1->CR1 |= SPI_CR1_DFF; SPI1->CR1 |= SPI_CR1_SPE; //Настройка переменных, которые будут //использоваться в обработчике прерывания SPI tx_index_16_bit = 0; tx_len_16_bit = len_16bit; tx_data_16_bit = data; //Разрешаем прерывание TXEIE И запускаем обмен SPI1->CR2 |= SPI_CR2_TXEIE; } void SPI1_IRQHandler(void) { if ((SPI1->CR1&SPI_CR1_DFF) == 0) { SPI1->DR = tx_data_8_bit[tx_index_8_bit]; //Записываем новое значение в DR tx_index_8_bit++; //увеличиваем счетчик переданных байт на единицу //если все передали, то отключаем прерывание, //тем самым завершаем передачу данных if(tx_index_8_bit >= tx_len_8_bit) SPI1->CR2 &= ~(SPI_CR2_TXEIE); } else { SPI1->DR = tx_data_16_bit[tx_index_16_bit]; //Записываем новое значение в DR tx_index_16_bit++; //увеличиваем счетчик переданных байт на единицу //если все передали, то отключаем прерывание, //тем самым завершаем передачу данных if(tx_index_16_bit >= tx_len_16_bit) SPI1->CR2 &= ~(SPI_CR2_TXEIE); } } ............. uint8_t data_8bit[3] = { 0xAA, 0xBB, 0xCC}; uint16_t data_16bit[3] = { 0xDDDD, 0xEEEE, 0xFFFF}; int main(void) { SPI1_STM32F1_write_8bit_irq( data_8bit, 3); //LL_mDelay(1); SPI1_STM32F1_write_16bit_irq( data_16bit, 3); //LL_mDelay(1); SPI1_STM32F1_write_8bit_irq( data_8bit, 3); //LL_mDelay(1); SPI1_STM32F1_write_16bit_irq( data_16bit, 3); }  
    • By Prominence
      Здравствуйте, в силу поставленных передо мной ограничений (на плате нет внешнего резонатора), я вынужден пытаться запустить STM32F103 от внутреннего тактирования, да ещё и под 72МГц.
      Правильный ли код?
          RCC_DeInit();  //Сброс
          RCC_HSEConfig(RCC_HSE_OFF); //Выключение внешнего тактирования (на всякий случай)
          RCC_PLLConfig( RCC_PLLSource_HSI_Div1, RCC_PLLMul_9); //использование множителя 9 для получения 72МГц, но компилятор ругается на делитель Div1
      Дескать, минимум можно использовать Div2, а совсем без делителя нельзя, как ещё можно выставить множитель?

          RCC_PLLCmd( ENABLE); //Включение PLL как такового
          RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK); //Использование PLL в качестве основного источника тактирования
      Вышеперечисленная шняга будет вызываться в мэйне, но это совсем другая история, пока что с этим туплю.
    • By artos5
      День добрый! Кто работал с такой памятью?? Проблема у меня следующая:
      Считывать массив получается , но иногда он считывается как ноли.
      Писать весь массив (больше одной страницы) тоже не получается, записывается в лучшем случае 2 страницы (64байта) остальной массив остается со старыми данными.
      Но вот если писать страницы по отдельности. Например записать во вторую страницу что то , или в третью и т.д. - то все нормально .
      Как я пишу:
      1) отправляю команду на откл.защиты от записи
      2) отправляю команду что я хочу записать по адресу 0
      3) потом пишу 32 байта 
      далее идет задержка (пробовал разные) 50мс.
      повторяю снова 1,2,3 пункты только адрес уже 32. 
      и снова заливаю 32 байта , CS при зтом поднимаю перед первым пунктом после первого и после третьего пункта.
      Код покажу чуть позже , а то он дома остался....
      Кто работал с этой памятью , что я упустил??? 
       
    • Guest Вячеслав
      By Guest Вячеслав
      Всем привет!
      Есть необходимость выполнять некоторую функцию по таймеру, для этого вызов функции осуществляется по условию и из бесконечного цикла main, выполнение условия - по флагу, собственно, флаг передергивается в обработчике прерывания по таймеру. Проблема заключается в том, что 
      while (1) { if(flag == 1) // сюда программа попадает один раз, последующие передергивания флага дают результат условия 1, однако код в условии не выполняется // дальше цикл бесконечно крутит, таймер дергает флаг, условие сравнивает, но не пускает { func(); flag = 0; } } Контроллер stm32f103c8.
      Буду благодарен и постараюсь оперативно уточнять, если вдруг проблема не типовая и нужно более подробное описание!
×
×
  • Create New...