Cadiz

Plata BT139

Cadiz

 

Вкратце — устройство должно при наличии удобрений, воды и электричества делать климат-контроль и полив автономным, программируемым и удалённо-доступным.

Опишем что нужно от устройства:

0123456789ABCDEF

XX 0..255 0..FF

  • оно должно отслеживать основные параметры контролируемого процесса (выращивание растений в данном случае) и записывать их на носитель данных (SD card)

  • оно должно иметь возможность управлять внешними нагрузками (такими как нагреватели, кондиционеры, свет, вентиляторы, увлажнители, насосы, компрессоры и прочие)

  • управление возможно в ручном режиме — на месте или удалённо

  • управление возможно в автоматическом режиме согласно заданной пользователем программе

  • настройка и программирование устройства возможны удалённо (Ethernet, Internet)

При имеющихся внешних устройствах и сенсорах устройство позволяет поддерживать заданными или менять по заданной пользователем программе:

    • уровень кислотности раствора

    • концентрацию солей в растворе

    • температуру в помещении

    • влажность

Читая http://easyelectronics.ru/arm-uchebnyj-kurs-taktovyj-generator-stm32.html я обнаруживаю интересную конструкцию по присваиванию, которой я ранее не встречал.

RCC->CR |= ((uint32_t)RCC_CR_HSEON);

Во-первых, указан тип данных переменной перед самой переменной uint32_t. Уже интересно. Далее идёт RCC_CR_HSEON, явно константа. Товарищ DiHALT рекомендовал посмотреть тот файлик с дефайнами. У меня он оказался для STM32VL-Discovery под именем stm32f10x.h. Там я нашел определение этой константы:

#define RCC_CR_HSEON ((uint32_t)0x00010000)

Далее я заметил удивительный способ записи чисел при присваивании последних константам: ((uint32_t)0x0000FF00). Опять указан тип и 8 знаков после 0x. Видимо, 0x — это, типа основание (шестнадцетиричное, походу) и 8 знаков отвечают за 32 бита регистра (один знак — 4 бита). Получается, что в данном примере (0x0000FF00) при переводе в двоичный вид будет что-то типа 0000 0000 0000 0000 1111 1111 0000 0000. Это (как и другие, приведённые выше, эквивалентные записи) называют битовой маской.

Теперь, читая http://eugenemcu.ru/publ/2-1-0-53/ я догадываюсь, что биты выставляются каким-то волшебным образом, с использованием логических операций:

SREG = SREG | 128; // Установить бит №7 регистра SREG.

Можно представить как:

SREG = SREG | 10000000;

Оказывается, «Такую работу с регистром принято называть “чтение – модификация – запись”, в отличие от простого присваивания она сохраняет состояние остальных битов без именения.»

Поскольку «(1<<N) – это выражение на языке Си означает, число один, сдвинутое на N разрядов влево, это и есть маска с установленным N-ным битом», то можно то же самое записать как:

SREG = SREG | (1<<7); // Установить бит №7 регистра SREG.

А это можно упростить, записав так:

SREG = SREG | (1<<7);

Отсюда следует, что наша первоначальная запись

RCC->CR |=((uint32_t)RCC_CR_HSEON);

выставляет бит 16 (RCC_CR_HSEON=0x00010000) ) в регистре RCC->CR

Теперь надо бы разобраться и со сбросом, думаю я. И он тоже описан. Вот пример из мигалки ледом:

GPIOC->CRH &= ~GPIO_CRH_CNF8; // Настроили ногу 8 (синий LED) на выход Push-Pull.

Тильда (~) означает инверсию. То есть битовая маска (#define GPIO_CRH_CNF8 ((uint32_t)0x0000000C) из того самого stm32f10x.h) инвертируется на обратные значения. Нули стали единицами и наоборот. И потом эта маска накладывается на регистр GPIOC->CRH логической операцией «И».

0x0000000C — это 1100. Инвертируем и получим 0011. Приложим эти биты с операцией «И» и биты 3 и 2 сбросятся, так как 0 И «что угодно» даст 0.

HD44780 aka GDM1602A

Интересная история случилась при попытке подсоединить дисплей типа HD44780. В моём случае дисплеем оказался GDM1602A. Дело в том, что уважаемый товарищ sasha85ru выложил в CooCox репозиторий свой драйвер (написаный при помощи других уважаемых граждан уважаемого сообщества easyelectronics.ru). Его я и взял для проб. Он замечательно работает в конфигурации по-умолчанию, однако в этой конфигурации он также влияет на светодиоды, которыми хотелось поморгать отдельно (да и работа дисплея нарушается при мигании своими программными силами). Так я предпочёл не тратить время на железные модификации и воткнуть разъём в другой ряд пинов, идущих друг за другом, плюс которые будут 5В толерантны (можно подсмотреть в даташите в таблице Low & medium-density STM32F100xx pin definitions со страницы 24 и вниз). Такими я выбрал PB11-PB15 для даты и PB3-PB5 для управления. Благодаря зоркости HHIMERA c easylectronics.ru я узнал, что:

PB3 — JTDO
PB4 — NJTRST

Тогда я взял разъём на 4 пина, оставив одну дырку пустой (для пина Boot, который разделяет PB7 и PB8), я воткнул 3 имеющихся пина. Всё заработало.

ADC

Кнопки сразу же приходят в голову, когда обнаруживаю, что экранчик работает. 4 кнопки, дают 4 сопротивления по 20килоом каждая. Итого 20, 40, 60 и 80килоом. Собираем делитель напряжения, добавив резистор на 120килоом (первый попавшийся того же порядка, что и максимальное сопротивление кнопок).

Тут – http://mycontroller.ru/stm32-adc-primeryi-ispolzovaniya-shag-1/ я обнаружил вполне рабочий код:

//********************************************************************************************
//function инициализация АЦП //
//argument none //
//return none //
//********************************************************************************************
void AdcInit(void)
{
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;// Разрешить тактирование порта PORTA
  //Конфигурирование PORTA.6 - аналоговый вход
  GPIOA->CRL &= ~GPIO_CRL_MODE6;//Очистить биты MODE
  GPIOA->CRL &= ~GPIO_CRL_CNF6;//Очистить биты CNF

  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;//подаем тактирование АЦП 
  RCC->CFGR &= ~RCC_CFGR_ADCPRE;//входной делитель
  ADC1->CR1 =0;//
  ADC1->SQR1 =0;// 
  ADC1->CR2 |= ADC_CR2_CAL;//запуск калибровки 
  while(!(ADC1->CR2 & ADC_CR2_CAL)){};//ждем окончания калибровки
  ADC1->CR2 = ADC_CR2_EXTSEL;//выбрать источником запуска разряд SWSTART
  ADC1->CR2 |= ADC_CR2_EXTTRIG;//разр. внешний запуск регулярного канала
  ADC1->CR2 |= ADC_CR2_CONT;//режим непрерывного преобразования 
  ADC1->SQR3 =6;//загрузить номер канала
  ADC1->CR2 |= ADC_CR2_ADON;//включить АЦП
  ADC1->CR2 |= ADC_CR2_SWSTART;//запустить процес преобразования
}

Только он выдаёт число, а нам нужна строка. Поэтому, поискав на просторах интернетов я нашёл подобное обсуждение, где приводились примеры функций преобразования – http://electronix.ru/forum/lofiversion/index.php/t69301.html

Тут я взял функцию char* adc2str(uint_fast16_t d, char* out). Когда я её добавил и попытался скомпилировать проект, компилятор сказал:

[cc] C:\CooCox\CoIDE\workspace\adc_test1\main.c:78:7: error: conflicting types for ‘adc2str’

что ж, с ним не поспоришь. Пришлось искать причину и оказалось, что проблема в том, что данная функция (adc2str) не имеет прототипа (это такая особенность Си, когда перед использованием функции её надо ещё дообъявить).

Вот сама функция:


char* adc2str(uint_fast16_t d, char* out)

{

out[5] = '\0';

out[4] = '0' + ( d ) % 10;

out[3] = '0' + ( d /= 10 ) % 10;

out[2] = '0' + ( d /= 10 ) % 10;

out[1] = '0' + ( d /= 10 ) % 10;

out[0] = '0' + ( d / 10 ) % 10;

return out;

}

Добавляем прототип простым копированием первой строки функции (до самой функции и её вызовов, видимо, или только вызовов, что вероятней):

char* adc2str(uint_fast16_t d, char* out)

И теперь программа компилируется.

Вот полный код для main.c где использованы кнопки и LCD (бОльшая часть кода появилась тут автоматом при добавлении драйвера LCD):

#include“stm32f10x.h”

#include“stm32f10x_gpio.h”

#include“stm32f10x_rcc.h”

#include“FreeRTOS.h”

#include“task.h”

#include“queue.h”

/*Подключения LCD WH1602B (Winstar) по 4-х битной шине

*alexander85ru@gmail.ru

*/

//#include “stm32f10x.h”

//#include “stm32f10x_gpio.h”

//#include “stm32f10x_rcc.h”

#define port GPIOB

#define init_port RCC_APB2Periph_GPIOB

#define pin_e GPIO_Pin_7

#define pin_rw GPIO_Pin_8

#define pin_rs GPIO_Pin_9

#define lcd_shift 11 ///номерпоследниегобита в 4-х битнойшине

#define use_gpio GPIO_Pin_14 //старшийбит

#define pin_d7 use_gpio //старшийбит

#define pin_d6 use_gpio>>1 //следующийбитпоубыванию

#define pin_d5 use_gpio>>2 //следующийбитпоубыванию

#define pin_d4 use_gpio>>3 //последнийбит в 4-х битнойшин

#define Function_set 0b00100000//4-bit,2 – line mode, 5*8 dots

#define Display_on_off_control 0b00001100/// display on,cursor off,blink off

#define Display_clear 0b00000001

#define Entry_mode_set 0b00000100//

#define rs_1 port->ODR |= pin_rs

#define rs_0 port->ODR &=~ pin_rs

#define e_1 port->ODR |= pin_e

#define e_0 port->ODR &=~ pin_e

#define rw_1 port->ODR |= pin_rw

#define rw_0 port->ODR &=~ pin_rw

u32 del;

constunsignedchar russian[]={ 0x41, 0xA0, 0x42, 0xA1, 0xE0, 0x45,

0xA3, 0xA4, 0xA5,0xA6, 0x4B, 0xA7, 0x4D, 0x48, 0x4F, 0xA8, 0x50,0x43,

0x54, 0xA9, 0xAA, 0x58, 0xE1, 0xAB, 0xAC, 0xE2, 0xAD,0xAE, 0x62,

0xAF, 0xB0, 0xB1, 0x61, 0xB2, 0xB3, 0xB4, 0xE3, 0x65, 0xB6, 0xB7,

0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0x6F, 0xBE, 0x70, 0x63,0xBF,

0x79, 0xE4, 0x78, 0xE5, 0xC0, 0xC1, 0xE6, 0xC2,0xC3, 0xC4, 0xC5,

0xC6, 0xC7 };

voidInit_pin_out(void);

voidInit_pin_in(void);

voidInit_lcd(void);

voidLcd_write_data(uint8_t byte);

voidLcd_write_cmd(uint8_t byte);

voidLcd_clear(void);

voidReturn_home(void);

voidLcd_goto(uc8 x, uc8 y);

voidLcd_write_str(uc8 *STRING);

char* adc2str(uint_fast16_t d, char* out);

voidAdcInit(void);

staticvoidprvSetupHardware( void );

staticvoidprvLedBlink( void *pvParameters );

staticvoidprvLedBlink2( void *pvParameters );

staticvoiddisplayClock( void *pvParameters );

typedefstruct

{

unsignedcharhour; //часы

unsignedcharmin; //минуты

unsignedcharsec; //секунды

} RTC_Time;

//********************************************************************************************

//function преобразуетВремя” в секунды (если 0->00:00:00) //

//argument указатель на струтуру, хранящую время для преобразования //

//result время в форматесекунды” //

//********************************************************************************************

uint32_tTimeToRtc(RTC_Time *time )

{

uint32_t result;

result = (uint32_t)time->hour * 3600; //учестьчасы

result += (uint32_t)time->min * 60; //минуты

result += time->sec; //секунды

return result;

}

//********************************************************************************************

//function преобразует секунды в форматВремя” (если 0->00:00:00) //

//argument значение счетчика, указатель на струтуруВремя” //

//result none //

//********************************************************************************************

voidRtcToTime( uint32_t cnt, RTC_Time *time )

{

time->sec = cnt % 60; //получитьсекунды

cnt /= 60; //количествоминут

time->min = cnt % 60; //получитьминуты

cnt /= 60; //количестводней

time->hour = cnt % 24; //получитьчасы

}

voiddisplayClock(void *pvParameters)

{

RTC_Time Time; //структурадляработы с типомвремя

uint_fast16_t tmp, tmp2; //вспомогательнаяпеременная

uint32_t i, k;

char str[6];

k=0;

SystemInit();

AdcInit(); //выполнитьинициализацию АЦП

Init_lcd();

Lcd_clear();

Lcd_goto(0,0);

Lcd_write_str(priveeeeet);

Lcd_goto(1,0);

Lcd_write_str(mir);

for (i = 0; i < 350000; i++) ; // Подождалиполсекунды

Lcd_clear();

Time.sec = 0;

Time.min = 0;

Time.hour= 0;

while (1)

{

tmp = TimeToRtc(&Time);

tmp++;

RtcToTime(tmp,&Time); //преобразовать в “человеческийформат

Lcd_goto(0,0);

Lcd_write_str(“now”);

Lcd_goto(1,0);

Lcd_write_digit(Time.hour);

Lcd_write_str(“:”);

Lcd_write_digit(Time.min);

Lcd_write_str(“:”);

Lcd_write_digit(Time.sec);

Lcd_goto(0,6);

tmp2 = ADC1->DR; //прочитатьрезультатизмерения

adc2str(tmp2, str);

Lcd_write_str(str);

}

while (1) {

}

}

voidAdcInit(void)

{

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // Разрешитьтактированиепорта PORTA

//Конфигурирование PORTA.6 – аналоговыйвход

GPIOA->CRL &= ~GPIO_CRL_MODE6; //Очиститьбиты MODE

GPIOA->CRL &= ~GPIO_CRL_CNF6; //Очиститьбиты CNF

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //подаемтактирование АЦП

RCC->CFGR &= ~RCC_CFGR_ADCPRE; //входнойделитель

ADC1->CR1 = 0; //

ADC1->SQR1 = 0; //

ADC1->CR2 |= ADC_CR2_CAL; //запусккалибровки

while (!(ADC1->CR2 & ADC_CR2_CAL)){}; //ждемокончаниякалибровки

ADC1->CR2 = ADC_CR2_EXTSEL; //выбратьисточникомзапускаразряд SWSTART

ADC1->CR2 |= ADC_CR2_EXTTRIG; //разр. внешнийзапускрегулярногоканала

ADC1->CR2 |= ADC_CR2_CONT; //режимнепрерывногопреобразования

ADC1->SQR3 = 6; //загрузитьномерканала

ADC1->CR2 |= ADC_CR2_ADON; //включить АЦП

ADC1->CR2 |= ADC_CR2_SWSTART; //запуститьпроцеспреобразования

}

char* adc2str(uint_fast16_t d, char* out)

{

out[5] = ‘\0’;

out[4] = ‘0’ + ( d ) % 10;

out[3] = ‘0’ + ( d /= 10 ) % 10;

out[2] = ‘0’ + ( d /= 10 ) % 10;

out[1] = ‘0’ + ( d /= 10 ) % 10;

out[0] = ‘0’ + ( d / 10 ) % 10;

return out;

}

voidLcd_write_digit(uint8_t numb){

if (numb<10) {

Lcd_write_data(48);

Lcd_write_data(48+numb);

}

else {

Lcd_write_data((numb/10)+48);

Lcd_write_data((numb-(numb/10)*10)+48);

}

}

voidprvSetupHardware()

{

// RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Включили тактирование порта GPIOB.

RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; //Включилитактированиепорта GPIOC.

GPIOC->CRH &= ~GPIO_CRH_CNF8; // Настроилиногу 8 (синий LED) навыход Push-Pull.

GPIOC->CRH &= ~GPIO_CRH_CNF9; // Настроилиногу 9 (зелёный LED) навыход Push-Pull.

GPIOC->CRH |= GPIO_CRH_MODE8_0; // Настроилиногу 8 (синий LED) на 10MHz.

GPIOC->CRH |= GPIO_CRH_MODE9_0; // Настроилиногу 9 (зелёный

}

voidprvLedBlink( void *pvParameters )

{

uint32_t i;

while(1) {

GPIOC->BRR = GPIO_BRR_BR8; // Погасилисиний

vTaskDelay(1000);

GPIOC->BSRR = GPIO_BSRR_BS8; // Зажглисиний

vTaskDelay(1000);

}

}

voidprvLedBlink2( void *pvParameters )

{

uint32_t i;

while(1) {

GPIOC->BSRR = GPIO_BSRR_BS9; // Зажглизелёный

vTaskDelay(400);

GPIOC->BRR = GPIO_BRR_BR9; // Погасилизелёный

vTaskDelay(400);

}

}

intmain(void)

{

prvSetupHardware();

xTaskCreate(prvLedBlink,(signedchar*)“LED”,configMINIMAL_STACK_SIZE,

NULL, tskIDLE_PRIORITY + 1, NULL);

xTaskCreate(prvLedBlink2,(signedchar*)“LED2”,configMINIMAL_STACK_SIZE,

NULL, tskIDLE_PRIORITY + 1, NULL);

xTaskCreate(displayClock,(signedchar*)“CLK”,configMINIMAL_STACK_SIZE,

NULL, tskIDLE_PRIORITY + 1, NULL);

/* Start the scheduler. */

vTaskStartScheduler();

while(1);

}

voidLcd_write_str(uc8 *STRING)

{

char c; //символизстроки

while (c=*STRING++){

if(c>=192) Lcd_write_data(russian[c-192]);

else Lcd_write_data(c);

}

}

voidLcd_goto(uc8 x,uc8 y)

{

int str;

str = y + 0x80;

if(x == 1)

{

str+= 0x40;

}

Lcd_write_cmd(str);

}

voidInit_pin_out()

{

RCC_APB2PeriphClockCmd(init_port, ENABLE);

GPIO_InitTypeDef init_pin;

init_pin.GPIO_Pin = pin_e | pin_rs | pin_rw | pin_d7 | pin_d6 | pin_d5 | pin_d4;

init_pin.GPIO_Mode = GPIO_Mode_Out_PP;

init_pin.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init (port, &init_pin);

}

voidInit_pin_in()

{

RCC_APB2PeriphClockCmd(init_port, ENABLE);

GPIO_InitTypeDef init_pin;

init_pin.GPIO_Pin = pin_d7 | pin_d6 | pin_d5 | pin_d4 ;

init_pin.GPIO_Mode = GPIO_Mode_IPD;

init_pin.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init (port, &init_pin);

}

voidLcd_write_cmd(uc8 cmd )

{

Init_pin_out();

del=24000; while (del–){}

rs_0;

GPIO_Write(port,((cmd>>4)<<lcd_shift)); // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ

e_1;

del=10; while (del–){}

e_0;

GPIO_Write(port,(0x00)<<lcd_shift);

GPIO_Write(port,((cmd&0x0F)<<lcd_shift));// пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ

del=10; while (del–){}

e_1;

del=10; while (del–){}

e_0;rs_0;rw_0;

}

voidLcd_write_data(uint8_t data)

{

Init_pin_out();

GPIO_Write(port,((data>>4)<<lcd_shift)); // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ

e_1;rs_1;

del=10; while (del–){}

e_0;

GPIO_Write(port,(0x00)<<lcd_shift);

GPIO_Write(port,((data&0x0F)<<lcd_shift));// пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ

del=10; while (del–){}

e_1;rs_1;

del=10; while (del–){}

e_0;rs_0;rw_0;

GPIO_Write(port,(0x00)<<lcd_shift);

}

voidInit_lcd()

{

Init_pin_out();

del=72000; while (del–){}

Lcd_write_cmd(Function_set);

del=72000; while (del–){}

Lcd_write_cmd(Display_on_off_control);

del=72000; while (del–){}

Lcd_write_cmd(Display_clear);

del=72000; while (del–){}

Lcd_write_cmd(Entry_mode_set);

}

voidLcd_clear()

{

Lcd_write_cmd(Display_clear);

}

voidReturn_home()

{

Lcd_write_cmd(0b0000001);

}7

Leave a Reply

Your email address will not be published. Required fields are marked *


*