В настоящий момент мы успешно умеем писать цифровой сигнал в порт, читать его оттуда, писать в порт аналоговый сигнал (при помощи ШИМа). Остается научиться читать аналоговый сигнал, после чего можно смело приступать к реализации практически чего угодно. Практически все общение с внешним миром сводится к одной из вышеперечисленных операций. Строго говоря, есть еще всякие протоколы общения типа SPI, I2C, CAN и прочее. Но, во-первых, их всегда можно эмулировать софтверно, выкурив доки по нужному протоколу (а курить их все равно придется, ибо без них все равно не пообщаешься), а во-вторых, суть работы с ними остается такой же и к их освоению можно будет приступить по мере необходимости.
Сегодня будем читать аналоговые данные. Специально для этого существуют аналог-цифровые преобразователи, которых в нашем МК два. В каждом из АЦП - по четыре незваисимых секвенсера. Можно аппаратно усреднять данные по выборке из нескольких значений (до 64), можно за одну выборку читать значения из нескольких (до четырех) каналов. На все про все есть 24 канала. Есть встроенный в чип датчик температуры, который так же может быть входом АЦП. В общем функцинальности достаточно, хотя конечно отдельно выделеные специально обученые АЦП порвут наш встроенный как тузик грелку.
Итак, задача будет такая: Читаем данные с аналогового входа и от датчика температуры. Раз в пять секунд выплевываем из в УАРТ, чтобы любоваться на нах в консоли. Кроме того, яркостью красного диода показываем уровень напряжения на аналоговом входе.
Получается примерно так (инклюды я опущу, а то их что-то много расплодилдось, хотя больше половины можно смело выкинуть):
unsigned long adcData[1];
unsigned long tempData[2];
int
main(void)
{
unsigned long rgbColors[3] = {0, 0, 0};
unsigned last_millis = 0; //А что, коньяк так никому и не нужен???
float volt;
int voltInt, voltDec;
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //в порту F у нас живут светодиоды
//включаем ЮАРТ. Последовательно - включаем сам юарт, порт на котором он живет (А), пины Rx и Tx.
//библиотека uartstdio позволяет чуть проще общаться с юартом - не отдельными символами, а строчками
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTStdioInit(0);
sysSoftTimerInit(TIMER0_BASE, TIMER_A); //наш добрый знакомый - самописный системный таймер
RGBInit(1); //еще одна библиотека из примеров, она будет рулить светодиодом. Причем самопальный програмный
//себя не оправдал, а эта библиотека вешает ШИМ на таймеры, что более правильно идеологически
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); //включаем ADC
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); //и порт, где будет аналоговый вход
ROM_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3); //а будет он на пине 3
ROM_ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); //говорим что третий секвенсер будет читать данные по нашей команде.
ROM_ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE |
ADC_CTL_END); // а измерения в нес состоят из одного шага - берем данные со входа, вызываем прерывание "готово"
ROM_ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, 0); //и тоже самое для второго секвенсера.
ROM_ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_TS | ADC_CTL_IE |
ADC_CTL_END);
ROM_ADCSequenceEnable(ADC0_BASE, 3); //включаем оба секвенсера
ROM_ADCSequenceEnable(ADC0_BASE, 2);
ROM_ADCIntClear(ADC0_BASE, 3); //на всякий случай чистим флаги - прерываний пока нету
ROM_ADCIntClear(ADC0_BASE, 2);
while(1){ //loop forever
ROM_ADCProcessorTrigger(ADC0_BASE, 3); //просим третий секвенсер померять напряжение
while(!ADCIntStatus(ADC0_BASE, 3, false)){}; //ждем, пока померяет
ROM_ADCIntClear(ADC0_BASE, 3); //флаг прерывания чистим - это индикатор того, что данные готовы
ROM_ADCSequenceDataGet(ADC0_BASE, 3, adcData); //забираем собственно данные
ROM_ADCProcessorTrigger(ADC0_BASE, 2); //все тоже самое для второго секвенсера, там датчик температуры
while(!ADCIntStatus(ADC0_BASE, 2, false)){};
ROM_ADCIntClear(ADC0_BASE, 2);
ROM_ADCSequenceDataGet(ADC0_BASE, 2, tempData);
rgbColors[RED] = adcData[0]<<4; //АЦП у нас 12-бытный, а градации яркости светодиода - 8битные. Приводим данные к нужному размеру
RGBColorSet(rgbColors); //и включаем светодиод на соответствующую яркость
if(millis() > last_millis + 5000) { //если пора орать в ЮАРТ
last_millis = millis();
volt = (3.3*adcData[0])/4096; //цифру из АЦП преобразуем в напряжение
voltInt = (int)volt; //эти пляски с бубнами нужны потому, что наш псевдо stdio не умеет
voltDec = (int)((volt-voltInt)*10); //печатать действительные числа, только целые.
UARTprintf("Voltage = %d.%d\n", voltInt, voltDec); //вот и печатаем по частям, "с фиксированной точкой"
UARTprintf("Temperature = %d\n", (int)(147.5 - ((75 * 3.3 * tempData[0]) / 4096))); //а температру вообще напечатаем в целом виде. Формулу преобразования взял из даташита
};
}
}
Осталось придумать, какое напряжение будем мерять. Я не стал особенно выпендриваться, взял потенциометр, подключил между землей и питанием, а средний вывод подал на аналоговый вход.
Компилим, запускаем. Крутим ручку потенциометра, диод меняет яркость, в ЮАРТ данные пишутся. Внутри МК 27 градусов. Подносим палец - становится 28, 29, 30... Вроде все работает.