вторник, 29 ноября 2011 г.

Быстрый старт с С на PIC используя SDCC и Linux

Уважаемые читатели, хотел бы поделиться с вами одной интересной статьей. Опубликована она на английском языке, но часто многие энтузиасты просто не знают столь хорошо английский язык. Я постараюсь объяснить все так, чтобы было просто и понятно.
Данное статья посвящена исключительно программированию. При этом она не является подробным описанием синтаксиса C.
Скажу честно, при программировании на данном языке надо его знать. Если вы до этого программировали исключительно для систем (т.е. для компьютера). То лично мой совет, вы всегда должны знать, в какой системе счисления вы работаете!
Сегодня я зашел в гости к другу на работу, у него там машинка игрушечная на AVR, решил позабавиться написать на нее программу. Я потратил 3 часа, на то чтобы понять где мои изначальные ошибки (но это другая история).
Начнем.
Во-первых, SDCC работает в связке с gputils (это пакет для программирования на языке assembler'a). Изначально у нас есть исходный файл, с помощью SDCC мы получаем файл на языке assembler'a. Потом происходит этап создания объектного файла с помощью gpasm (его вы найдете в пакете gputils). Дальше с помощью линковщика получаем hex файл. Это вы можете увидеть на рисунке:




После чего hex файл зашиваем в МК.
Как установить SDCC можно найти в интернете, если вы пользуетесь Debian и ее производными (Ubuntu, Mint и т.д.) то задача облегчается.
sudo apt-get install sdcc
Для начала воспользуемся простым примером, готовой программы все с того же сайта, но скажу сразу, что я его буду изменять. Вот текст программы:

// blinkled.c
//
// simple minimal program that blinks a LED

// ------------------------------------------------
// configuration
// change these if you're wiring the LED to a pin other than RB1

#define LED_TRIS  TRISBbits.TRISB1
#define LED_PIN   PORTBbits.RB1


// ------------------------------------------------
// this #include pulls in the correct processor-specific registers
// definition file

#include "pic18fregs.h"


#pragma stack 0x200 64

code char at __CONFIG1H conf1 = 0x22; // Select HS OSC
code char at __CONFIG4L conf2 = 0x81; // Disable LVP
code char at __CONFIG2H conf3 = 0x0E; // DIsable WDT

// ------------------------------------------------
// a simple delay function

void delay_ms(long ms)
{
    long i;

    while (ms--)
        for (i=0; i < 330; i++)
            ;
}

// --------------------------------------------------
// and our main entry point

void main()
{

    // set pin to output
    LED_TRIS = 0;

    // sit in an endless loop blinking the led
    for (;;)
    {
        LED_PIN = 0;
        delay_ms(250);
        LED_PIN = 1;
        delay_ms(250);
    }
}
Теперь я поясню, что я в первую очередь изменю. Во-первых, зачем самому писать функцию delay_ms(long) если уже существует такая в папке pic16 файл называется delay.h. Для того, чтобы создать задержку, есть несколько функций:

void delay10tcy(unsigned char) __wparam;
void delay100tcy(unsigned char) __wparam;
void delay1ktcy(unsigned char) __wparam;
void delay10ktcy(unsigned char) __wparam;
void delay100ktcy(unsigned char) __wparam;
void delay1mtcy(unsigned char) __wparam;
Теперь поясню tcy это цикл микроконтроллера (как говорит великий и ужасный Никулин Владислав Генадьевич: "НЕ ПУТАТЬ ЦИКЛ С ТАКТОМ!!!").
Что меня порадовало, хотя бы программисты SDCC понимают, что функция не может четко ms задержку сделать, ибо это зависит от частоты тактового генератора, что МК не известно.
10     10*n, циклов, где n параметр функции.
100   100*n циклов задержки
1k     1000*n циклов задержки
10k   10000*n циклов задержки
100k 100000*n циклов задержки
1m    1000000*n циклов задержки.
Теперь программа выглядит так:

// helloled.c
//
// Простой пример, своего рода "Hello, World!!!"
// только на МК




// ------------------------------------------------
// Конфигурация
// здесь используем сам МК как ноль и единицу
// хотя могли бы использовать землю.


#ifndef LED_TRIS
#define LED_TRIS  TRISAbits.TRISA1
#endif


#ifndef LED_PIN
#define LED_PIN   PORTAbits.RA1
#endif


// ------------------------------------------------
// Данные директивы #include включают необходимые заголовочные файлы


#include "pic18fregs.h" // Здесь хранятся адреса всех битов МК
#include "delay.h" // Здесь хранятся прототипы функций задержки


// --------------------------------------------------
// и вот наша главная функция, для С/С++ является обязательной


void main()
{
    // Устанавливаем бит на выход
    LED_TRIS = 0;
    LED_PIN = 1;
    // Цикл в котором постоянно инвертируем LED_PIN
    while(1)
    {
        LED_PIN = LED_PIN ^ 0x01;
        delay1ktcy(250);
    }
}

Скомпилировать ее можно командой: sdcc -mpic16 -p18f2550 helloled.c
Как вы заметили МК я тоже поменял. Не люблю полностью повторять урок, охота его изменить, по возможности улучшить. Тем более согласитесь, моя программа меньше, к сожалению работоспособность оной, я неизвестно когда проверю, но надеюсь скоро (если по учебе не грузанут, то я бы сказал, что очень скоро).
Теперь рассмотрим второй пример программы все с того же сайта.

/* Morse output 
   sdcc demo program
   compile with  "sdcc -mpic16 -p18f452 morse.c"
   (c) P.J.Onion 2005
*/

#include "pic18fregs.h"

include the definitions needed to use the interrupt handler/signal techniques
#include "signal.h"

define the location and size of the runtime stack
#pragma stack 0x200 0x40

set the configuration bytes (Set HS xtal oscillator, disable LVP, disable WDT) 
code char at __CONFIG1H conf1 = 0x22;
code char at __CONFIG4L conf2 = 0x81;
code char at __CONFIG2H conf3 = 0x0E;

symbolic definitions of numbers to control length of dashes and dots
#define DASH 250
#define DOT 100

symbolic definitions for the pin used to drive the LED
#define LED_TRIS  TRISBbits.TRISB7
#define LED_PIN   PORTBbits.RB7

An initialised array of bytes containing the dot/dash codes for each letter
unsigned char codes[3] = {
/* A */ 0x60,
/* B */ 0x88,
/* C */ 0xA8
};

unsigned char intCounter = 0;
unsigned char timer;

Definitions for using Timer0 interrupts
DEF_INTHIGH(high_int)
DEF_HANDLER(SIG_TMR0, _tmr0_handler)
END_DEF 

This is the interrupt handler called when timer0 overflows
SIGHANDLER(_tmr0_handler)
{

  /* action to be taken when timer 0 overflows */
  /* decrement intCounter until it reaches zero */
    if(intCounter) intCounter -= 1;
    INTCONbits.T0IF = 0;   /* Reset the Timer0 interrupt pending flag */
} 

/* Pause while intCounter is counting down to zero */
void delay(unsigned char time)
{
    intCounter = time;
    while(intCounter) ;
}

void sendLetter(unsigned char index)
{
    unsigned char shift;
    shift = codes[index];
    do
    {
        LED_PIN = 1;
        if(shift & 0x80)
        {
            delay(DASH);
        }
        else
        {
            delay(DOT);
        }
        LED_PIN = 0;
        delay(DOT);
        shift <<= 1;
    } while(shift != 0x80);
    delay(DOT);
}

void main(void)
{
    /* Set LED pin as an output */
    LED_TRIS = 0;

    /*Enable high and low priority interrupts */
    RCON = 0;
    RCONbits.IPEN = 1;

    /* Configure timer0 */
    T0CONbits.T0PS0 = 0;
    T0CONbits.T0PS1 = 0;
    T0CONbits.T0PS2 = 1;

    T0CONbits.PSA = 0;
    
    T0CONbits.T0SE = 0;
    T0CONbits.T0CS = 0;
    T0CONbits.T08BIT = 1;
    T0CONbits.TMR0ON = 1;

    /* enable timer0 interrupt as a high priority source */ 
    INTCON2bits.T0IP = 1;
    INTCONbits.T0IE = 1;
    
    /* Enable interrupts */
    INTCONbits.GIE = 1;

    while(1)
    {
      sendLetter(0);
      sendLetter(1);
      sendLetter(2);
    }
}

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

#include "pic18fregs.h"
#include "delay.h"


//Включаем заголовочный файл необходимый для обработки сигналов прерываний
#include "signal.h"


//Определяем адрес и размер стэка
#pragma stack 0x200 0x40


//Устанавливаем конфигурационные биты (Высокочастотный кварц, 
//отключено низковольтное программирование и сторожевой таймер 
code char at __CONFIG1H conf1 = 0x22;
code char at __CONFIG4L conf2 = 0x81;
code char at __CONFIG2H conf3 = 0x0E;


//Символьное определение значения длины
#define DASH 250 //Тире
#define DOT 100 //Точка


//Символьное определение порта, к которому подключен LED
#define LED_TRIS  TRISBbits.TRISB7
#define LED_PIN   PORTBbits.RB7


//Инициализируем массив байтов, содержащих коды Морзе, для каждой буквы
unsigned char codes[3] = {
/* A */ 0x60,
/* B */ 0x88,
/* C */ 0xA8
};


unsigned char intCounter = 0;
unsigned char timer;


//Определение необходимое для использования прерываний от таймера 0
DEF_INTHIGH(high_int)
DEF_HANDLER(SIG_TMR0, _tmr0_handler)
END_DEF 


//Это обработчик прерывания, который вызывается, когда таймер переполняется
SIGHANDLER(_tmr0_handler)
{
  /* Действие, которое выполняется когда Таймер 0 переполнен */
  /* Декрементируем intCounter до 0 */
    if(intCounter) intCounter--;
    INTCONbits.T0IF = 0;   /* Сброс флага прерывания, по переполнению Таймера 0 */
}


void sendLetter(unsigned char index)
{
    unsigned char shift;
    shift = codes[index];
    do
    {
        LED_PIN = 1;
        if(shift & 0x80) //0x80 = 1000 0000
        {
            delay10tcy(DASH);
        }
        else
        {
            delay10tcy(DOT);
        }
        LED_PIN = 0;
        delay10tcy(DOT);
        shift <<= 1;
    } while(shift != 0x80);
    delay10tcy(DOT);
}   


void main(void)
{
    /* Устанавливаем порт LED как выход */
    LED_TRIS = 0;


    /*Включаем высокий и низкий уровень приоритета прерываний */
    RCON = 0;
    RCONbits.IPEN = 1;


    /* Настраиваем Таймер 0 */
    T0CONbits.T0PS0 = 0;
    T0CONbits.T0PS1 = 0;
    T0CONbits.T0PS2 = 1;


    T0CONbits.PSA = 0;
    
    T0CONbits.T0SE = 0;
    T0CONbits.T0CS = 0;
    T0CONbits.T08BIT = 1;
    T0CONbits.TMR0ON = 1;


    /* Включаем Таймер 0 с высоким приоритетом */ 
    INTCON2bits.T0IP = 1;
    INTCONbits.T0IE = 1;
    
    /* Включаем прерывания */
    INTCONbits.GIE = 1;
    //В вечном цикле отсвечиваем светодиодом Морзянку A, B, C, A, B, C и т.д.
    //пока МК не отключим
    while(1)
    {
      sendLetter(0);
      sendLetter(1);
      sendLetter(2);
    }
}

Мы люди грамотные по этому должны понимать, что для того, чтобы понять полностью эту программу нужно скачать Datasheet.
Для нас интересны следующие регистры и биты:
RCON{IPEN} - бит включения приоритета прерываний, если 1 уровни приоритета включены.


INTCON{GIE} - если RCON{IPEN} = 0, то это бит глобального разрешения прерываний 
1 - все прерывания разрешены, а если RCON{IPEN} = 1, то данным битом разрешаются только прерывания  с высоким приоритетом.
INTCON{T0IF} - по букве F понятно, что это флаг, кстати в даташите данный регистр описан как TMR0IF, если 1 произошло переполнение Таймер 0
INTCON{T0IE} - здесь буква E от слова ENABLES (включен). Показывает включено прерывание по переполнению Таймера 0 или нет. Если 1 то включено.
INTCON2{T0IP} - буква P от слова PRIORITY (приоритет). Если 1 то прерывание по переполнению Таймера 0 имеет высокий приоритет.
T0CON{TOPS0} - как и следующие три бита, это бит множителя,
T0CON{TOPS1}
T0CON{TOPS2}

111 = 1:256
110 = 1:128
101 = 1:64
100 = 1:32
011 = 1:16
010 = 1:8
001 = 1:4
000 = 1:2
Надеюсь вы помните, что самый правый бит 0, т.е в данном случае последовательность такая T0PS2:T0PS0

T0CON{PSA} - Если 1, то множитель отключен, т.е. будет без разницы чему равны верхних три бита все равно соотношение тактового сигнала и тактов счетчика 1:1.
T0CON{T0CS} - с этим у меня возникла проблема перевода, если правильно понял, то это выбор по какому пину получать тактовый сигнал, если 1 то по T0CKI он же RA4 иначе же по CLKO.
T0CON{T0SE} - по какому сигналу происходит инкремент счетчика от сигнала с пина
T0CKI, если 1 - то инкремент происходит при переходе с высокого уровня сигнала в низкий, иначе наоборот с низкого в высокий.
T0CON{T08BIT} - если единица то имеем дело с 8-битным счетчиком, иначе с 16-битным
T0CON{TMR0ON} - если 1, то Таймер 0 включен.

Теперь мы в курсе всех подробностей у нас включены приоритеты, работает Таймер 0 с 8-битным счетчиком, делитель 1:4, используется тактовый сигнал с CLK0.
Ну что ж пожалуй, на сегодня хватит. :)




среда, 9 ноября 2011 г.

PIC и Linux

Моя мечта приступить к началу, но все времени нету, добраться до магазина радиодеталей, чтобы купить запчасти для программатора.
Увы, но если на ноутбуке нету COM то это плохо :(.
А с USB есть проблемы. Потому, решил начать с программирования. Сам я программирую на C++, точнее я бы сказал, что учусь программировать. Но увы микроконтроллеры (далее МК) программируют на ассемблере, C и BASIC. Потому пошарился в инете и нашел интересную ссылку о программировании на C МК Microchip. Я решил попробовать с простого, коли у меня завалялось два МК, то для начала я хочу сделать так, чтобы эта программа пахала для моего PIC16F873.
При легком изменении программы, а точнее я поменял в исходном тексте 16f627 на 16f873 в строках:
/* Define processor and include header file. */
#define __16f627
#include"pic/pic16f627.h"
Также для компиляции программы я использовал команду:

sdcc --debug -mpic14 -p16f873 main.c
В итоге компилятор заматюгался:
main.c:25: error 20: Undefined identifier '_ER_OSC_CLKOUT'
main.c:26: error 20: Undefined identifier '_MCLRE_ON'
main.c:25: error 2: Initializer element is not constant
main.c:25: error 2: Initializer element is not constant
Открываем заголовочный файл, для данного МК в моей системе (Kubuntu 11.10) оный находится по адресу: /usr/share/sdcc/include/pic/
Первое, что привлекло мой взгляд:
This header file defines configurations, registers, and other useful bits of
// information for the PIC16F873 microcontroller.
Этот заголовочный файл определяет конфигурацию, регистры, и другие полезные функции.
Нас интересует конфигурация.
Первое строка, которую мы получили при компиляции гласит о нестыковке тактового генератора, видать тип ER мой МК не поддерживает ищем замену, среди 4 возможных вариантов тактирования МК (это мы увидели в заголовочном файле):
#define _LP_OSC              0x3FFC
#define _XT_OSC              0x3FFD
#define _HS_OSC              0x3FFE
#define _RC_OSC              0x3FFF
Поясню, LP – низкочастотный кварцевый резонатор (малое энергопотребление)
 XT – кварцевый/керамический резонатор
 HS – высокочастотный кварцевый/керамический резонатор
 RC – внешний резистор/конденсатор
Я в своей схеме планирую использовать кварцевый резонатор. Потому _ER_OSC_CLKOUT поменяю на _HS_OSC.
Теперь перейдем к cбросу по включению питания POR: 
_MCLRE_ON
Во-первых в заголовочном файле такого просто не оказалось, во вторых, если я правильно понял, этот тип сброса для данного МК всегда включен. В документации не нашел способ включить или выключить тип сброса, но везде написано, что он есть. По этому просто удаляем эту строку.
Пытаемся скомпилировать:
sdcc --debug -mpic14 -p16f873 main.c
message: using default linker script "/usr/share/gputils/lkr/16f873.lkr"
warning: relocation of section "UDL_idata_0" failed, relocating to a shared memory location
warning: relocation of section "ID_idata_0" failed, relocating to a shared memory location
получили предупреждение, что перемещение секции таких то оказалось неудачным. Получен hex файл, я подумал неинтересно просто изменить МК, поменяем программу.
Во первых создадим функцию delay. Ну разумеется прежде я поискал есть ли оная в заголовочных файлах, зато если посмотреть массу примеров эту функцию часто можно встретить, это задержка. Слава богу не месячных у девушки :).
Потом определим, что будет делать наша программа, пусть по нажатию кнопки подцепленной на RB0 будет инвертироваться состояние RB1.
Притом надо учесть дребезг контакта, максимальное время дребезга которое я встречал это 100 мс или 0,1 с. :(
В итоге вот, не судите строго в первый раз такое пишу ибо студент и я только учусь :):
/*
 toggle_led.c
 Micah Carrick - email@micahcarrick.com
 04.25.2005
 Toggles an LED on Pin 1 of PORTB on a PIC16F627. Written
 as a sample for the article on using SDCC and GPSIM in
 Linux. http://www.micahcarrick.com/v2/content/view/14/4/
 Compile: sdcc --debug -mpic14 -p16f627 toggle_led.c
 Simulate: gpsim -pp16f627 -s toggle_led.cod toggle_led.asm
 This program was modified Russian student.
 Compile: sdcc --debug -mpic14 -p18f873 main.c
*/
/* Define processor and include header file. */
#define __16f873
#include"pic/pic16f873.h"
/* Setup chip configuration */
typedef unsigned int config;
config at 0x2007 __CONFIG = _CP_OFF & 
 _WDT_OFF & 
 _BODEN_OFF & 
 _PWRTE_OFF & 
 _HS_OSC &
 _LVP_OFF;
#define b1 0x02 /* pin 1 on PORTB */
#define B_OUTPUTS 0xFD /* value used to setup TRISB */
void delay(int count);
unsigned int temp;
unsigned int i;
void main(void) {
 /* PORTB.1 is an output pin */ 
 TRISB = B_OUTPUTS; 
 while(1) { /* Loop forever */
 delay(1000);
 RB3 = ~RB3;
 temp=RB0; //Изначально это 0
 delay(1000);
           /*после первого нажатия на кнопку, temp как и RB0 станет равным единице условие не сработает, в итоге условие срабатывает лишь по изменению сигнала с 0 на 1 */
 if((RB0) && (!temp))
   RB1=~RB1;
 }
}
void delay(int count)
{
  i=0;
  while (i<count)
    i++;
}
Из особенностей синтаксиса, которые я заметил, этот С на дух не переваривает статические переменные объявленные внутри функций :(. А это очень удобная штука, но увы в этом С все глобально.
На выходе я получил hex файл, но и предупреждения от компилятора и линковщика следующего вида:
main.c:44: warning 180: using ~ on bit/bool/unsigned char variables can give unexpected results due to promotion to int
main.c:49: warning 180: using ~ on bit/bool/unsigned char variables can give unexpected results due to promotion to int
message: using default linker script "/usr/share/gputils/lkr/16f873.lkr"
warning: relocation of section "UDL_idata_0" failed, relocating to a shared memory location
warning: relocation of section "UDL_main_0" failed, relocating to a shared memory location
warning: relocation of section "UD_main_0" failed, relocating to a shared memory location
warning: relocation of section "UD_main_1" failed, relocating to a shared memory location
warning: relocation of section "ID_idata_0" failed, relocating to a shared memory location







воскресенье, 6 ноября 2011 г.

Выходной воскресение, время экономики

Выходной, моя девушка, делала экономику, решил составить ей компанию. Задачка простая.
Функция инвестиций (I) задана формулой I=10-2*i (i-процентная ставка);
функция сбережений (S) выражена формулой: S=0,1*Y-2 (Y-доход);
функция трансакционного спроса (делового спроса на деньги): M_t=0,2*Y;
функция спекулятивного спроса на деньги: M_a=12-4*i.
При условии, что предложение денег равно M_s=6 трлн. долл. найти равновесные процентную ставку и доход. Как изменится равновесие если функция инвестиций примет вид: 24-2i, а спекулятивный спрос: 12-i?

Скажу, честно долго вкурить не мог, как это решать, пока не наткнулся на следующий ресурс. В итоге все оказалось просто как 2x2. Берем и приравниваем I=S, а M_s=M_t+M_a=6 Распишем эти два равенства: 10-2i=0.1Y-2
и второе равенство: 0.2Y+12-4i=6
Что общего между этими уравнениями? В них по две переменные при том одни и те же!!! И если мой блог читают экономисты, то понятно, что рисуется график. i(Y), на котором мы рисуем две прямые IS и L_m.

Уравнение кривой IS это уравнение: 10-2*i=0.1*Y-2
Из которого выражаем i=(0.1*Y-2 - 10)/(-2)=5-0.05*Y+1=6-0.05*Y
Или можем выразить Y=(10-2*i+2)/(0.1)=(12-2*i)*10=120-20*i.
Из уравнения мы видим, что графиком является прямая, а для построения оной, нужно всего две точки. Теперь найдем эти 2 точки по которым построим график функции IS:

10-2*i=0.1*Y-2;
i=(0.1*Y-2-10)/(-2);
Пусть Y=0, тогда i=(0.1*0 - 12)/(-2)=6
Пусть i=0, тогда
0=(0.1*Y-12)/(-2)=6-0.05*Y
0.05*Y=6
Y=6/0.05=120
Имеем две точки
(Y1;i1)=(0;6)
(Y2;i2)=(120;0)

Запишем уравнение L_m: 0.2*Y+12-4*i=6.
Как видно из уравнения график этой функции, тоже представляет из себя прямую. Теперь найдем две точки, для построения функции L_m:

Выразим i=(6-0.2*Y-12)/(-4).
Пусть Y=0, тогда i=(6-0.2*0-12)/(-4)=(-6)/(-4)=1.5
Получаем, что первая точка (0;1.5). Найдем вторую точку.
Пусть i=0, тогда
0=(6-0.2*Y-12)/(-4)=(-6-0.2*Y)/(-4)=1.5+0.05*Y
0.05*Y=-1.5
Y=-1.5/0.05
Y=-30
Получили вторую точку (-30;0).

Построим график:
На графике, как не сложно догадаться зеленая линия это L_m, а синяя линия это IS.

Найдем точку равновесия. Так как я не люблю графический метод решения найдем аналитически:

Есть уравнение для L_m: i=(6-0.2*Y-12)/(-4);
и для IS: i=(0.1*Y-2-10)/(-2).
Приравняем их.
(6-0.2*Y-12)/(-4)=(0.1*Y-2-10)/(-2);
(-6-0,2*Y)/(-4)=(0.1*Y-12)/(-2)
(-6-0,2*Y)/2=(0.1*Y-12)
-3-0,1*Y=0.1*Y-12
0.2*Y=-3+12=9
Y=9/0.2=45
Теперь подставим значение Y равновесия в любое уравнение для i:
i(45)=(0.1*45-12)/(-2)=6-0.05*45=3.75
Координаты точки равновесия IS и L_m (Y=45; i=3.75).

Осталось ответить на еще один последний вопрос!
Как изменится равновесие если функция инвестиций примет вид: 24-2i, а спекулятивный спрос: 12-i?
Повторим все действия проделанные выше:
Равенство для IS: 24 - 2*i = 0,1*Y - 2
Выразим i:
- 2*i = 0,1*Y - 2-24=0.1*Y-26;
i = (0,1*Y - 26) / (- 2)=13-0.05*Y;
Как мы видим графиком остается прямая, по этому ищем две точки для построения графика IS.
Пусть Y=0, тогда i(0)=13-0.05*Y=13-0.05*0=13.
Имеем первую точку (0; 13).
Найдем вторую точку.
Пусть i=0 тогда
0=13-0.05*Y;
0.05*Y=13;
Y=260;
Вторая точка имеет координаты (260; 0)
Найдем уравнение для L_m из равенства M_s = M_t + M_a.
0.2*Y+12-i=6
Выразим i:
i=0.2*Y+12-6=0.2*Y+6;
Из уравнения видим, что графиком является прямая. Найдем две точки для построения графика.
Пусть Y=0, тогда i(0) = 0.2 * Y + 6 = 0.2 * 0 + 6 = 6
Первая точка есть (0; 6), найдем вторую.
Пусть i=0, тогда
0 = 0.2 * Y + 6;
0.2 * Y = -6;
Y = -6 / 0.2 = -30;
Получили вторую точку (-30; 0).
Построим график.
Точку равновесия найдем аналитически. Приравняв уравнение IS=L_m.
13-0.05*Y=0.2*Y+6;
0.2 * Y + 0.05 * Y = 13 - 6;
0.25 * Y = 7;
Y = 7 / 0.25 = 28;
i(28) = 0.2*Y+6 = 0.2*28+6 = 5.6 + 6 = 11.6;
Сравним с равновесием полученным до этого (Y=45; i=3.75) и только
что полученные (Y=28; i=11.6), как мы видим, увеличился процент в 3 раза, а доход уменьшился, 1.6 раз.
Все задачка решена! :)

среда, 2 ноября 2011 г.

JPG to PDF

Как то раз, делая детали машин, появилась нужда, чтения ГОСТ 1284.3-96 "Ремни приводные клиновые нормальных сечений. Передаваемые мощности." Но вот незадача, нашел, только вариант в JPG, который читать не очень то удобно, пошарился в инете нашел сайт http://linsovet.com/jpeg-book-scans-to-pdf. Но согласитесь мало кому охота вручную забивать правильную последовательность файлов. По тому написал скрипт, аргументами которого являются, номер последней страницы книги (нумерация с нуля!!!), имя выходного файла, без расширения, и путь к картинкам, которые конвертируешь. Скрипт выглядит следующим образом.

      #!/bin/bash
      
cd $3
      
str="convert "
      
for ((i=0; i<=$1; i++))
      
do
      
str+="$i.jpg "
      
done
      
str+="-adjoin $2.pdf"
      
$str
      
exit 0

 Но увы разрешение изображений оказалось таким плохим, что полученный PDF файл просто невозможно было прочитать :( По этому прежде чем делать PDF воспользуйтесь, проверьте разрешение файлов и читаемость оных :)