Страницы

Страницы

суббота, 29 ноября 2014 г.

Настройка преобразования первых 8ми регулярных каналов АЦП с использованием канала DMA

void ADC_init(void)
{
// настройка ADC1
        RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //  такты на ADC1

        ADC1->SMPR2 |= ADC_SMPR2_SMP0 | ADC_SMPR2_SMP1 
        | ADC_SMPR2_SMP2 | ADC_SMPR2_SMP3 | ADC_SMPR2_SMP4 
        | ADC_SMPR2_SMP5 | ADC_SMPR2_SMP6 | ADC_SMPR2_SMP7;       // количество циклов преобразования 239.5

        ADC1->SQR1 |= ADC_SQR1_L_2 | ADC_SQR1_L_1 | ADC_SQR1_L_0; // длина последовательности регулярных каналов = 8;
        // добавление в последовательность каналов
        ADC1->SQR2 |= ADC_SQR2_SQ8_2 | ADC_SQR2_SQ8_1 | ADC_SQR2_SQ8_0 //канал 7
        | ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_1;                             // канал 6

        ADC1->SQR3 |=  ADC_SQR3_SQ6_2 | ADC_SQR3_SQ6_0                 //канал 5
        | ADC_SQR3_SQ5_2                                               //канал 4
        | ADC_SQR3_SQ4_1 | ADC_SQR3_SQ4_0                              //канал 3
        | ADC_SQR3_SQ3_1                                               //канал 2
        | ADC_SQR3_SQ2_0;                                              // канал 1
        //канал 0 включен по умолчанию т.к. в младших битах SQ3 0
//      ADC1->CR2 |= ADC_CR2_CONT;      // включаем если нужно непрерывное преобразование последовательности в цикле

        ADC1->CR2 |= ADC_CR2_DMA //включаем работу с DMA
        | ADC_CR2_EXTTRIG //включаем работу от внешнего события 
        | ADC_CR2_EXTSEL //выбираем триггером запуска регулярной последовательности событие SWSTART
        | ADC_CR2_JEXTSEL; // выбираем триггером запуска выделенной последовательности событие JSWSTART дабы эти каналы не отнимали у мк времени
        ADC1->CR1 |= ADC_CR1_SCAN; // включаем автоматический перебор всех каналов в последовательности
//макросы для включения/выключения АЦП с DMA
#define ADC_ON ADC1->CR2 |= ADC_CR2_ADON;  \
                                ADC1->CR2 |= ADC_CR2_SWSTART; \
                                DMA1_Channel1->CCR |= DMA_CCR1_TCIE | DMA_CCR1_EN;      //включаем преобразование прерывание DMA
#define ADC_OFF ADC->CR2 &= (~(ADC_CR2_ADON)); \
                                DMA1_Channel1->CCR &= (~(DMA_CCR1_TCIE | DMA_CCR1_EN)); //выключаем преобразование и прерывание DMA
        
        return;
}


Настройка DMA для АЦП


        DMA1_Channel1->CPAR = ADC1_BASE+0x4C; // Загружаем адрес регистра DR
        DMA1_Channel1->CMAR = pADC_readbuf_addr->Buf_Addr; //грузим адрес буфера обмена
        DMA1_Channel1->CNDTR = 8; //длина буфера 
        DMA1_Channel1->CCR |= DMA_CCR1_MINC     //инкремент адреса памяти
        | DMA_CCR1_PSIZE_0 //размерность данных периферии 16 бит
        | DMA_CCR1_MSIZE_0 //размерность данных памяти 16 bit
        | DMA_CCR1_CIRC;   // закольцевать буфер

SPI2 в режиме мастера для работы с DMA

void SPI_init(void)
{
// SPI2 init
        RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; // такты на SPI2

        GPIOB->CRH &= (~(GPIO_CRH_CNF13_0 | GPIO_CRH_CNF15_0)); //настройка выводов для SPI
        GPIOB->CRH |= (GPIO_CRH_MODE13 | GPIO_CRH_MODE15                
         | GPIO_CRH_CNF13_1 | GPIO_CRH_CNF15_1);                // AF Push-Pull out

        SPI2->CR1 |= SPI_CR1_BR_1    //настройка делителя 
//       | SPI_CR1_BR_0    //комментируя или раскомментируя эти строки можно изменять значение делителя 
//       | SPI_CR1_BR_2
         | SPI_CR1_SSM   // программное управление слэйвом
         | SPI_CR1_SSI   // при программном управлении слэйвом данный бит должен быть 1
         | SPI_CR1_MSTR  // Режим мастера
         | SPI_CR1_SPE;   // разрешить SPI2 

        SPI2->CR2 |= SPI_CR2_RXDMAEN // разрешить передачу принятых данных через DMA
         | SPI_CR2_TXDMAEN; // Разрешить принимать данные для передачи через DMA

        return;
}

DMA для работы с SPI2 на прием и передачу



        RCC->AHBENR |= RCC_AHBENR_DMA1EN; //такты

        // 4 канал DMA1  RX SPI2 -> память
        DMA1_Channel4->CPAR = SPI2_BASE+0x0C; // адрес DR
        DMA1_Channel4->CMAR = pSPI2_readbuf_addr->Buf_Addr; //адрес буфера приема
        DMA1_Channel4->CNDTR = 12; // размер транзакции
        DMA1_Channel4->CCR |= DMA_CCR4_MINC     //инкремент адреса буфера
        | DMA_CCR4_PSIZE_0;     //размерность данных SPI 16 бит (происходит автоматическое преобразование 16->8 бит)

        // 5 канал DMA1  память -> TX SPI2
        DMA1_Channel5->CPAR = SPI2_BASE+0x0C; //адрес DR
        DMA1_Channel5->CMAR = pSPI2_writebuf_addr->Buf_Addr; //адрес буфера передачи
        DMA1_Channel5->CNDTR = 12; // размер транзакции
        DMA1_Channel5->CCR |= DMA_CCR5_MINC     //инкремент адреса буфера
        | DMA_CCR5_DIR; // направление передачи из памяти в периферию


Транзакция запускается следующим куском кода:


void SPI2_start (void) // По сути это переинициализация каналов DMA
{
        DMA1_Channel4->CPAR = SPI2_BASE+0x0C; //DR Base 
        DMA1_Channel4->CMAR = pSPI2_readbuf_addr->Buf_Addr;
        DMA1_Channel4->CNDTR = 12;
        DMA1_Channel5->CPAR = SPI2_BASE+0x0C; //DR Base
        DMA1_Channel5->CMAR = pSPI2_writebuf_addr->Buf_Addr;
        DMA1_Channel5->CNDTR = 12;
        DMA1_Channel4->CCR |= DMA_CCR4_TCIE | DMA_CCR4_EN;
        DMA1_Channel5->CCR |= DMA_CCR5_EN;
        return;
}


Результаты обрабатываются по прерыванию завершения работы канала DMA работающего на прием данных от SPI:


void DMAChannel4_IRQHandler     (void)
{
        DMA1_Channel4->CCR &= (~(DMA_CCR4_TCIE | DMA_CCR4_EN)); // выключаем канал приемника и его прерывание
        DMA1->IFCR |= DMA_IFCR_CTCIF4 | DMA_IFCR_CGIF4; //очищаем флаги
        DMA1_Channel5->CCR &= (~(DMA_CCR5_EN)); // выключаем канал передатчика
        pCurrent_mode->stat.led_data_send = 0; // выставляем нужные флаги для дальнейшей работы с данными 
        HC595_OUT;
        pCurrent_mode->stat.read_key_data_ready=1;
        return;
}

ШИМ по двум каналам на TIM1

RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;  //такты
TIM1->CCER = 0;                     //обнуляем CCER (выключаем каналы)
TIM1->ARR = 100;                 // максимальное значение, до которого таймер ведет счет 
TIM1->PSC = 7200-1;                // предделитель         
TIM1->BDTR |= TIM_BDTR_MOE;     // Разрешаем вывод сигнала на выводы
//для первого ШИМ-сигнала используем канал 1 
//задаем скважность в регистр сравнения канала (значения от 0 до TIM1->ARR) 
TIM1->CCR1 = 50;      
// Включаем режим канал в режим ШИМ           
TIM1->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;     
// Разрешаем вывод не инвертированного сигнала на ногу МК     
TIM1->CCER |= TIM_CCER_CC1E;     
// Разрешаем вывод не инвертированного сигнала на ногу МК 
//для второго ШИМ-сигнала используем канал 4
//задаем скважность в регистр сравнения канала (значения от 0 до TIM1->ARR)   
TIM1
->CCR4 = 50;                   
// Включаем канал в режим ШИМ       
TIM1->CCMR2 |= TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2;     
// Разрешаем вывод не инвертированного сигнала на ногу МК
TIM1
->CCER |= TIM_CCER_CC4E;     
// Запускаем счет
TIM1
->CR1 |= TIM_CR1_CEN;



Теперь для управления скважностью нужного канала меняем значения в регистрах TIM1->CCR1 и TIM1->CCR4. Т.к. у нас глубина счета установлена 100, то задавая значение от 0 до 100 получим скважность в процентах :)